Created
September 27, 2017 14:04
-
-
Save juliedavila/fc21488e44d73cdd919f81ee1b43e204 to your computer and use it in GitHub Desktop.
AWX SAML with Mapping
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?xml version="1.0" encoding="UTF-8"?> | |
| <saml2:Assertion | |
| xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="id74539476266456101610211577" IssueInstant="2016-11-11T02:15:18.004Z" Version="2.0"> | |
| <saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.EXAMPLE-SAML-PROVIDER.com/Issuer</saml2:Issuer> | |
| <saml2:Subject> | |
| <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">userName</saml2:NameID> | |
| <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> | |
| <saml2:SubjectConfirmationData NotOnOrAfter="2016-11-11T02:20:18.004Z" Recipient="https://10.220.73.142/sso/complete/saml/"/> | |
| </saml2:SubjectConfirmation> | |
| </saml2:Subject> | |
| <saml2:Conditions NotBefore="2016-11-11T02:10:18.004Z" NotOnOrAfter="2016-11-11T02:20:18.004Z"> | |
| <saml2:AudienceRestriction> | |
| <saml2:Audience>https://tower.example.com</saml2:Audience> | |
| </saml2:AudienceRestriction> | |
| </saml2:Conditions> | |
| <saml2:AuthnStatement AuthnInstant="2016-11-11T02:15:18.004Z"> | |
| <saml2:AuthnContext> | |
| <saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef> | |
| </saml2:AuthnContext> | |
| </saml2:AuthnStatement> | |
| <saml2:AttributeStatement> | |
| <saml2:Attribute Name="User.FirstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"> | |
| <saml2:AttributeValue | |
| xmlns:xs="http://www.w3.org/2001/XMLSchema" | |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">user.firstName | |
| </saml2:AttributeValue> | |
| </saml2:Attribute> | |
| <saml2:Attribute Name="User.LastName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"> | |
| <saml2:AttributeValue | |
| xmlns:xs="http://www.w3.org/2001/XMLSchema" | |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">user.lastName | |
| </saml2:AttributeValue> | |
| </saml2:Attribute> | |
| <saml2:Attribute Name="User.email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"> | |
| <saml2:AttributeValue | |
| xmlns:xs="http://www.w3.org/2001/XMLSchema" | |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">user.email | |
| </saml2:AttributeValue> | |
| </saml2:Attribute> | |
| <saml2:Attribute Name="User.username" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"> | |
| <saml2:AttributeValue | |
| xmlns:xs="http://www.w3.org/2001/XMLSchema" | |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">user.login | |
| </saml2:AttributeValue> | |
| </saml2:Attribute> | |
| <saml2:Attribute Name="Groups" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"> | |
| <saml2:AttributeValue | |
| xmlns:xs="http://www.w3.org/2001/XMLSchema" | |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">GroupName Match Starts with "Ansible" (ignores case) | |
| </saml2:AttributeValue> | |
| </saml2:Attribute> | |
| </saml2:AttributeStatement> | |
| </saml2:Assertion> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| SOCIAL_AUTH_SAML_SP_ENTITY_ID = 'https://tower.example.com' | |
| # Create a keypair for Tower to use as a service provider (SP) and include the | |
| # certificate and private key contents here. | |
| SOCIAL_AUTH_SAML_SP_PUBLIC_CERT = open('/etc/tower/conf.d/saml_sp_certs/server.crt', 'r').read() | |
| SOCIAL_AUTH_SAML_SP_PRIVATE_KEY = open('/etc/tower/conf.d/saml_sp_certs/server.key', 'r').read() | |
| # Configure the following settings with information about your app and contact | |
| # information. | |
| SOCIAL_AUTH_SAML_ORG_INFO = { | |
| 'en-US': { | |
| 'name': 'Ansible Tower', | |
| 'displayname': 'Ansible Tower', | |
| 'url': 'http://www.example-org.com', | |
| }, | |
| } | |
| SOCIAL_AUTH_SAML_TECHNICAL_CONTACT = { | |
| 'givenName': 'Some User', | |
| 'emailAddress': 'suser@example.com', | |
| } | |
| SOCIAL_AUTH_SAML_SUPPORT_CONTACT = { | |
| 'givenName': 'Some User', | |
| 'emailAddress': 'suser@example.com', | |
| } | |
| # Configure the entity ID, SSO URL and certificate for each identity provider | |
| # (IdP) in use. Multiple SAML IdPs are supported. | |
| # Some IdPs may provide user data using attribute names that differ from the | |
| # default OIDs (https://github.com/omab/python-social-auth/blob/master/social/backends/saml.py#L16). | |
| # Attribute names may be overridden for each IdP as shown below. | |
| SOCIAL_AUTH_SAML_ENABLED_IDPS = { | |
| 'okta': { | |
| 'entity_id': 'http://www.okta.com/dsklfdjksdjkl', | |
| 'url': 'https://example.saml-provider.com/app/skljdjlksdfkljd/sdjklfsdfklj/sso/saml', | |
| 'x509cert': open('/etc/tower/conf.d/saml_sp_certs/saml-provider.cert').read(), | |
| 'attr_user_permanent_id': 'name_id', | |
| 'attr_first_name': 'User.FirstName', | |
| 'attr_last_name': 'User.LastName', | |
| 'attr_username': 'User.username', | |
| 'attr_email': 'User.email', | |
| }, | |
| } | |
| # Once configuration is complete, you will need to register your SP with each | |
| # IdP. Provide the entity ID and the following callback URL for your | |
| # application, replacing tower.example.com with the FQDN to your Tower server: | |
| # | |
| # https://tower.example.com/sso/complete/saml/ | |
| # | |
| # If your IdP allows uploading an XML metadata file, you can download one from | |
| # your Tower installation customized with the settings above: | |
| # | |
| # https://tower.example.com/sso/metadata/saml/ | |
| # | |
| # Superuser mapping | |
| # ----------------- | |
| # SUPERUSERS: A list of SAML group names whose members should become Tower super users | |
| # REMOVE_SUPERUSERS: True/False, if a is user is currently a super user but is not part | |
| # of the group, remove that user's super admin permissions | |
| SOCIAL_AUTH_SUPERUSERS = ['SOME_SAML_GROUP'] | |
| SOCIAL_AUTH_REMOVE_SUPERUSERS = True | |
| # Organiztion and Team Mapping | |
| # ---------------------------- | |
| # Mapping to organization admins/users from social auth accounts. This setting | |
| # controls which users are placed into which Tower organizations based on | |
| # their username and email address. Dictionary keys are organization names. | |
| # organizations will be created if not present if the license allows for | |
| # multiple organizations, otherwise the single default organization is used | |
| # regardless of the key. Values are dictionaries defining the options for | |
| # each organization's membership. For each organization it is possible to | |
| # specify which users are automatically users of the organization and also | |
| # which users can administer the organization. | |
| # | |
| # - admins: None, True/False, string or list/tuple of strings. | |
| # If None, organization admins will not be updated. | |
| # If True, all users using social auth will automatically be added as admins | |
| # of the organization. | |
| # If False, no social auth users will be automatically added as admins of | |
| # the organiation. | |
| # If a string or list of strings, specifies the usernames and emails for | |
| # users who will be added to the organization. Compiled regular expressions | |
| # may also be used instead of string literals. | |
| # - remove_admins: True/False. Defaults to False. | |
| # If True, a user who does not match will be removed from the organization's | |
| # administrative list. | |
| # - users: None, True/False, string or list/tuple of strings. Same rules apply | |
| # as for admins. | |
| # - remove_users: True/False. Defaults to False. Same rules as apply for remove_admins | |
| SOCIAL_AUTH_ORGANIZATION_MAP = { | |
| # Add all users to the default organization. | |
| 'Default': { | |
| 'users': True, | |
| }, | |
| 'General': { | |
| 'admins': False, | |
| 'users': ['SOME_OTHER_SAML_GROUP'], | |
| }, | |
| #'Test Org 2': { | |
| # 'admins': ['admin@example.com', re.compile(r'^tower-[^@]+*?@.*$], | |
| # 'users': re.compile(r'^[^@].*?@example\.com$'), | |
| #}, | |
| } | |
| # Organization mappings may be specified separately for each social auth | |
| # backend. If defined, these configurations will take precedence over the | |
| # global configuration above. | |
| #SOCIAL_AUTH_GOOGLE_OAUTH2_ORGANIZATION_MAP = {} | |
| #SOCIAL_AUTH_GITHUB_ORGANIZATION_MAP = {} | |
| #SOCIAL_AUTH_GITHUB_ORG_ORGANIZATION_MAP = {} | |
| #SOCIAL_AUTH_GITHUB_TEAM_ORGANIZATION_MAP = {} | |
| #SOCIAL_AUTH_SAML_ORGANIZATION_MAP = {} | |
| # Mapping of team members (users) from social auth accounts. Keys are team | |
| # names (will be created if not present). Values are dictionaries of options | |
| # for each team's membership, where each can contain the following parameters: | |
| # - organization: string. The name of the organization to which the team | |
| # belongs. The team will be created if the combination of organization and | |
| # team name does not exist. The organization will first be created if it | |
| # does not exist. If the license does not allow for multiple organizations, | |
| # the team will always be assigned to the single default organization. | |
| # - users: None, True/False, string or list/tuple of strings. | |
| # If None, team members will not be updated. | |
| # If True/False, all social auth users will be added/removed as team | |
| # members. | |
| # If a string or list of strings, specifies expressions used to match users. | |
| # User will be added as a team member if the username or email matches. | |
| # Compiled regular expressions may also be used instead of string literals. | |
| # - remove: True/False. Defaults to False. If True, a user who does not match | |
| # the rules above will be removed from the team. | |
| SOCIAL_AUTH_TEAM_MAP = { | |
| 'SOME_GROUP_NAME_FOR_TOWER': { | |
| 'organization': 'General', | |
| 'users': ['YET_ANOTHER_SAML_GROUP'], | |
| 'remove': True, | |
| }, | |
| #'Other Team': { | |
| # 'organization': 'Test Org 2', | |
| # 'users': re.compile(r'^[^@]+?@test2\.example\.com$'), | |
| # 'remove': False, | |
| #}, | |
| } | |
| # Team mappings may be specified separately for each social auth backend. If | |
| # defined, these configurations will take precedence over the the global | |
| # configuration above. | |
| #SOCIAL_AUTH_GOOGLE_OAUTH2_TEAM_MAP = {} | |
| #SOCIAL_AUTH_GITHUB_TEAM_MAP = {} | |
| #SOCIAL_AUTH_GITHUB_ORG_TEAM_MAP = {} | |
| #SOCIAL_AUTH_GITHUB_TEAM_TEAM_MAP = {} | |
| #SOCIAL_AUTH_SAML_TEAM_MAP = {} | |
| # Uncomment the line below (i.e. set SOCIAL_AUTH_USER_FIELDS to an empty list) | |
| # to prevent new user accounts from being created. Only users who have | |
| # previously logged in using social auth or have a user account with a matching | |
| # email address will be able to login. | |
| #SOCIAL_AUTH_USER_FIELDS = [] | |
| # It is also possible to add custom functions to the social auth pipeline for | |
| # more advanced organization and team mapping. Use at your own risk. | |
| def custom_social_auth_pipeline_function(backend, user, response, *args, **kwargs): | |
| from awx.api.license import feature_enabled | |
| groups = response['attributes']['Groups'] | |
| ## Super user logic | |
| explicit_su = False | |
| superusers = backend.setting('SUPERUSERS') or {} | |
| remove_su = backend.setting('REMOVE_SUPERUSERS') or None | |
| if superusers: | |
| for su_group in superusers: | |
| if su_group in groups: | |
| explicit_su = True | |
| user.is_superuser = True | |
| break | |
| if not explicit_su and remove_su: | |
| user.is_superuser = False | |
| def _update_m2m_from_expression(user, rel, expr, remove=True): | |
| ''' | |
| Helper function to update m2m relationship based on user matching one or | |
| more expressions. | |
| ''' | |
| should_add = False | |
| if expr is None: | |
| return | |
| elif not expr: | |
| pass | |
| elif expr is True: | |
| should_add = True | |
| else: | |
| if isinstance(expr, basestring): | |
| expr = [expr] | |
| for ex in expr: | |
| if isinstance(ex, basestring): | |
| if ex in groups: | |
| should_add = True | |
| if should_add: | |
| rel.add(user) | |
| elif remove: | |
| rel.remove(user) | |
| from awx.main.models import Organization, Team | |
| multiple_orgs = feature_enabled('multiple_organizations') | |
| org_map = backend.setting('ORGANIZATION_MAP') or {} | |
| for org_name, org_opts in org_map.items(): | |
| # Get or create the org to update. If the license only allows for one | |
| # org, always use the first active org, unless no org exists. | |
| if multiple_orgs: | |
| org = Organization.objects.get_or_create(name=org_name)[0] | |
| else: | |
| try: | |
| org = Organization.objects.order_by('pk')[0] | |
| except IndexError: | |
| continue | |
| # Update org admins from expression(s). | |
| remove = bool(org_opts.get('remove', True)) | |
| admins_expr = org_opts.get('admins', None) | |
| remove_admins = bool(org_opts.get('remove_admins', remove)) | |
| _update_m2m_from_expression(user, org.admin_role.members, admins_expr, remove_admins) | |
| # Update org users from expression(s). | |
| users_expr = org_opts.get('users', None) | |
| remove_users = bool(org_opts.get('remove_users', remove)) | |
| _update_m2m_from_expression(user, org.member_role.members, users_expr, remove_users) | |
| ## Add users to teams logic | |
| team_map = backend.setting('TEAM_MAP') or {} | |
| for team_name, team_opts in team_map.items(): | |
| # Get or create the org to update. If the license only allows for one | |
| # org, always use the first active org, unless no org exists. | |
| if multiple_orgs: | |
| if 'organization' not in team_opts: | |
| continue | |
| org = Organization.objects.get_or_create(name=team_opts['organization'])[0] | |
| else: | |
| try: | |
| org = Organization.objects.order_by('pk')[0] | |
| except IndexError: | |
| continue | |
| # Update team members from expression(s). | |
| team = Team.objects.get_or_create(name=team_name, organization=org)[0] | |
| users_expr = team_opts.get('users', None) | |
| remove = bool(team_opts.get('remove', True)) | |
| _update_m2m_from_expression(user, team.member_role.members, users_expr, remove) | |
| SOCIAL_AUTH_PIPELINE += ( | |
| 'awx.settings.production.custom_social_auth_pipeline_function', | |
| ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment