def refresh_identity(self, identity, *args, **kwargs): refresh_token = identity.data.get('refresh_token') refresh_token_url = kwargs.get('refresh_token_url') if not refresh_token: raise IdentityNotValid('Missing refresh token') if not refresh_token_url: raise IdentityNotValid('Missing refresh token url') data = self.get_refresh_token_params(refresh_token, *args, **kwargs) req = safe_urlopen( url=refresh_token_url, headers={}, data=data, ) try: body = safe_urlread(req) payload = json.loads(body) except Exception: payload = {} self.handle_refresh_error(req, payload) identity.data.update(get_oauth_data(payload)) return identity.update(data=identity.data)
def refresh_identity(self, identity, *args, **kwargs): refresh_token = identity.data.get("refresh_token") refresh_token_url = kwargs.get("refresh_token_url") if not refresh_token: raise IdentityNotValid("Missing refresh token") if not refresh_token_url: raise IdentityNotValid("Missing refresh token url") data = self.get_refresh_token_params(refresh_token, *args, **kwargs) req = safe_urlopen(url=refresh_token_url, headers={}, data=data) try: body = safe_urlread(req) payload = json.loads(body) except Exception as e: self.logger( "gitlab.refresh-identity-failure", extra={ "identity_id": identity.id, "error_status": e.code, "error_message": str(e), }, ) payload = {} self.handle_refresh_error(req, payload) identity.data.update(get_oauth_data(payload)) return identity.update(data=identity.data)
def refresh_identity(self, identity, *args, **kwargs): refresh_token = identity.data.get('refresh_token') refresh_token_url = kwargs.get('refresh_token_url') if not refresh_token: raise IdentityNotValid('Missing refresh token') if not refresh_token_url: raise IdentityNotValid('Missing refresh token url') data = self.get_refresh_token_params(refresh_token, *args, **kwargs) req = safe_urlopen( url=refresh_token_url, headers={}, data=data, ) try: body = safe_urlread(req) payload = json.loads(body) except Exception as e: self.logger('gitlab.refresh-identity-failure', extra={ 'identity_id': identity.id, 'error_status': e.code, 'error_message': e.message, }) payload = {} self.handle_refresh_error(req, payload) identity.data.update(get_oauth_data(payload)) return identity.update(data=identity.data)
def refresh_identity(self, auth_identity): refresh_token = auth_identity.data.get("refresh_token") if not refresh_token: raise IdentityNotValid("Missing refresh token") data = self.get_refresh_token_params(refresh_token=refresh_token) req = safe_urlopen(self.get_refresh_token_url(), data=data) try: body = safe_urlread(req) payload = json.loads(body) except Exception: payload = {} error = payload.get("error", "unknown_error") error_description = payload.get("error_description", "no description available") formatted_error = f"HTTP {req.status_code} ({error}): {error_description}" if req.status_code == 401: raise IdentityNotValid(formatted_error) if req.status_code == 400: # this may not be common, but at the very least Google will return # an invalid grant when a user is suspended if error == "invalid_grant": raise IdentityNotValid(formatted_error) if req.status_code != 200: raise Exception(formatted_error) auth_identity.data.update(self.get_oauth_data(payload)) auth_identity.update(data=auth_identity.data)
def build_identity(self, state): data = state["data"] try: id_token = data["id_token"] except KeyError: raise IdentityNotValid(u"Missing id_token in OAuth response: %s" % data) try: _, payload, _ = map(urlsafe_b64decode, id_token.split(".", 2)) except Exception as exc: raise IdentityNotValid(u"Unable to decode id_token: %s" % exc) try: user_data = json.loads(payload) except ValueError as exc: raise IdentityNotValid(u"Unable to decode id_token payload: %s" % exc) # XXX(epurkhiser): This is carryover from the AuthProvider version of # google identity. Because we will have code that handles interop # between newstyle generic Identity, and oldstyle AuthProviders, we # have to keep the MigratingIdentityId here. user_id = MigratingIdentityId(id=user_data["sub"], legacy_id=user_data["email"]) return { "type": "google", "id": user_id, "email": user_data["email"], "email_verified": user_data["email_verified"], "name": user_data["email"], "domain": user_data.get("hd", DEFAULT_GOOGLE_DOMAIN), "scopes": sorted(self.oauth_scopes), "data": self.get_oauth_data(data), }
def handle_refresh_error(self, req, payload): error_name = 'unknown_error' error_description = 'no description available' for name_key in ['error', 'Error']: if name_key in payload: error_name = payload.get(name_key) break for desc_key in ['error_description', 'ErrorDescription']: if desc_key in payload: error_description = payload.get(desc_key) break formatted_error = u'HTTP {} ({}): {}'.format(req.status_code, error_name, error_description) if req.status_code == 401: raise IdentityNotValid(formatted_error) if req.status_code == 400: # this may not be common, but at the very least Google will return # an invalid grant when a user is suspended if error_name == 'invalid_grant': raise IdentityNotValid(formatted_error) if req.status_code != 200: raise ApiError(formatted_error)
def handle_refresh_error(self, req, payload): error_name = "unknown_error" error_description = "no description available" for name_key in ["error", "Error"]: if name_key in payload: error_name = payload.get(name_key) break for desc_key in ["error_description", "ErrorDescription"]: if desc_key in payload: error_description = payload.get(desc_key) break formatted_error = u"HTTP {} ({}): {}".format(req.status_code, error_name, error_description) if req.status_code == 401: self.logger.info( "identity.oauth.refresh.identity-not-valid-error", extra={ "error_name": error_name, "error_status_code": req.status_code, "error_description": error_description, "provider_key": self.key, }, ) raise IdentityNotValid(formatted_error) if req.status_code == 400: # this may not be common, but at the very least Google will return # an invalid grant when a user is suspended if error_name == "invalid_grant": self.logger.info( "identity.oauth.refresh.identity-not-valid-error", extra={ "error_name": error_name, "error_status_code": req.status_code, "error_description": error_description, "provider_key": self.key, }, ) raise IdentityNotValid(formatted_error) if req.status_code != 200: self.logger.info( "identity.oauth.refresh.api-error", extra={ "error_name": error_name, "error_status_code": req.status_code, "error_description": error_description, "provider_key": self.key, }, ) raise ApiError(formatted_error)
def handle_refresh_error(self, req, payload): error_name = 'unknown_error' error_description = 'no description available' for name_key in ['error', 'Error']: if name_key in payload: error_name = payload.get(name_key) break for desc_key in ['error_description', 'ErrorDescription']: if desc_key in payload: error_description = payload.get(desc_key) break formatted_error = u'HTTP {} ({}): {}'.format(req.status_code, error_name, error_description) if req.status_code == 401: self.logger.info( 'identity.oauth.refresh.identity-not-valid-error', extra={ 'error_name': error_name, 'error_status_code': req.status_code, 'error_description': error_description, 'provider_key': self.key, } ) raise IdentityNotValid(formatted_error) if req.status_code == 400: # this may not be common, but at the very least Google will return # an invalid grant when a user is suspended if error_name == 'invalid_grant': self.logger.info( 'identity.oauth.refresh.identity-not-valid-error', extra={ 'error_name': error_name, 'error_status_code': req.status_code, 'error_description': error_description, 'provider_key': self.key, } ) raise IdentityNotValid(formatted_error) if req.status_code != 200: self.logger.info( 'identity.oauth.refresh.api-error', extra={ 'error_name': error_name, 'error_status_code': req.status_code, 'error_description': error_description, 'provider_key': self.key, } ) raise ApiError(formatted_error)
def refresh_identity(self, auth_identity): with GitHubClient(auth_identity.data["access_token"]) as client: try: if not client.is_org_member(self.org["id"]): raise IdentityNotValid except GitHubApiError as e: raise IdentityNotValid(e)
def build_identity(self, state): raw_attributes = state["auth_attributes"] attributes = {} # map configured provider attributes for key, provider_key in iteritems(self.config["attribute_mapping"]): attributes[key] = raw_attributes.get(provider_key, [""])[0] # Email and identifier MUST be correctly mapped if not attributes[Attributes.IDENTIFIER] or not attributes[Attributes.USER_EMAIL]: raise IdentityNotValid( _( "Failed to map SAML attributes. Assertion returned the following attribute keys: %(keys)s" ) % {"keys": raw_attributes.keys()} ) name = (attributes[k] for k in (Attributes.FIRST_NAME, Attributes.LAST_NAME)) name = " ".join(filter(None, name)) return { "id": attributes[Attributes.IDENTIFIER], "email": attributes[Attributes.USER_EMAIL], "name": name, }
def build_identity(self, state): raw_attributes = state["auth_attributes"] attributes = {} # map configured provider attributes for key, provider_key in self.config["attribute_mapping"].items(): attribute_list = raw_attributes.get(provider_key, [""]) attributes[key] = attribute_list[0] if len(attribute_list) > 0 else "" # Email and identifier MUST be correctly mapped if not attributes[Attributes.IDENTIFIER] or not attributes[Attributes.USER_EMAIL]: error_msg_keys = ", ".join(repr(key) for key in sorted(raw_attributes.keys())) raise IdentityNotValid( _( f"Failed to map SAML attributes. Assertion returned the following attribute keys: {error_msg_keys}" ) ) name = (attributes[k] for k in (Attributes.FIRST_NAME, Attributes.LAST_NAME)) name = " ".join(_f for _f in name if _f) return { "id": attributes[Attributes.IDENTIFIER], "email": attributes[Attributes.USER_EMAIL], "name": name, }
def test_simple(self, mock_check_auth_identity): organization = self.create_organization(name='Test') user = self.create_user(email='*****@*****.**') auth_provider = AuthProvider.objects.create( organization=organization, provider='dummy', ) om = OrganizationMember.objects.create( user=user, organization=organization, flags=OrganizationMember.flags['sso:linked'], ) ai = AuthIdentity.objects.create( auth_provider=auth_provider, user=user, last_verified=timezone.now() - timedelta(days=1), ) with patch.object(DummyProvider, 'refresh_identity') as mock_refresh_identity: mock_refresh_identity.side_effect = IdentityNotValid() with self.auth_provider('dummy', DummyProvider): check_auth_identity(auth_identity_id=ai.id) mock_refresh_identity.assert_called_once_with(ai) # because of an error, it should become inactive om = OrganizationMember.objects.get(id=om.id) assert not om.flags['sso:linked'] assert om.flags['sso:invalid'] updated_ai = AuthIdentity.objects.get(id=ai.id) assert updated_ai.last_synced != ai.last_synced assert updated_ai.last_verified != ai.last_verified
def test_basic_flow_error(self, build_identity, logger): build_identity.side_effect = IdentityNotValid() user = self.create_user("*****@*****.**") organization = self.create_organization(name="foo", owner=user) self.login_as(user) self.assert_basic_flow(user, organization, expect_error=True)
def refresh_identity(self, auth_identity): client = GenericClient(self.client_id, self.client_secret) access_token = auth_identity.data['access_token'] try: if not client.get_user(access_token): raise IdentityNotValid except GenericApiError as e: raise IdentityNotValid(e)
def refresh_identity(self, auth_identity): client = GitLabClient(self.client_id, self.client_secret) access_token = auth_identity.data['access_token'] try: if not client.is_org_member(access_token, self.org['id']): raise IdentityNotValid except GitLabApiError as e: raise IdentityNotValid(e)
def refresh_identity(self, auth_identity): client = ThaliaClient() try: user_data = client.get_user(auth_identity.data['auth_token']) auth_identity.user.update(name=u'{} {}'.format( user_data['first_name'], user_data['last_name']), email=user_data['email']) except ThaliaApiError as e: raise IdentityNotValid(e.message)
def dispatch(self, request, organization_slug): provider = get_provider(organization_slug) organization = Organization.objects.get(slug=organization_slug) saml_config = provider.build_saml_config(organization_slug) auth = provider.build_auth(request, saml_config) auth.process_response() errors = auth.get_errors() if errors: error_reason = auth.get_last_error_reason() raise IdentityNotValid(error_reason) attributes = auth.get_attributes() nameid = auth.get_nameid() email = self.retrieve_email(attributes, nameid, provider.config) # Filter users based on the emails provided in the commits user_emails = list( UserEmail.objects.filter(email__iexact=email, is_verified=True).order_by('id')) if user_emails: users = list( User.objects.filter( id__in=set((ue.user_id for ue in user_emails)), is_active=True, sentry_orgmember_set__organization_id=organization.id) [0:2]) if users: if len(users) == 1: user = users[0] user.backend = settings.AUTHENTICATION_BACKENDS[0] if login(request, user, after_2fa=request.build_absolute_uri(), organization_id=organization.id): request.session['saml'] = { 'nameid': nameid, 'nameid_format': auth.get_nameid_format(), 'session_index': auth.get_session_index() } return HttpResponseRedirect(get_login_redirect(request)) else: return HttpResponseServerError( "Found several accounts related with %s on this organization" % email) else: return HttpResponseServerError( "The user %s is not related with this organization" % email) else: return HttpResponseServerError( "An user with a verified mail: %s does not exist" % email)
def refresh_identity(self, identity, *args, **kwargs): refresh_token = identity.data.get('refresh_token') if not refresh_token: raise IdentityNotValid('Missing refresh token') data = self.get_refresh_token_params(refresh_token, *args, **kwargs) req = safe_urlopen( url=self.get_refresh_token_url(), headers=self.get_refresh_token_headers(), data=data, ) try: body = safe_urlread(req) payload = json.loads(body) except Exception: payload = {} error = payload.get('error', 'unknown_error') error_description = payload.get('error_description', 'no description available') formatted_error = 'HTTP {} ({}): {}'.format(req.status_code, error, error_description) if req.status_code == 401: raise IdentityNotValid(formatted_error) if req.status_code == 400: # this may not be common, but at the very least Google will return # an invalid grant when a user is suspended if error == 'invalid_grant': raise IdentityNotValid(formatted_error) if req.status_code != 200: raise Exception(formatted_error) identity.data.update(self.get_oauth_data(payload)) return identity.update(data=identity.data)
def refresh_identity(self, identity, *args, **kwargs): refresh_token = identity.data.get("refresh_token") refresh_token_url = kwargs.get("refresh_token_url") if not refresh_token: raise IdentityNotValid("Missing refresh token") if not refresh_token_url: raise IdentityNotValid("Missing refresh token url") kwargs["identity"] = identity data = self.get_refresh_token_params(refresh_token, *args, **kwargs) req = safe_urlopen(url=refresh_token_url, headers={}, data=data) try: body = safe_urlread(req) payload = json.loads(body) except Exception as e: # JSONDecodeError's will happen when we get a 301 # from GitLab, and won't have the `code` attribute # we use the req.status_code instead in that case error_status = getattr(e, "code", req.status_code) self.logger.info( "gitlab.refresh-identity-failure", extra={ "identity_id": identity.id, "error_status": error_status, "error_message": str(e), }, ) payload = {} self.handle_refresh_error(req, payload) identity.data.update(get_oauth_data(payload)) identity.update(data=identity.data) return identity
def refresh_identity(self, auth_identity): url = '%s?corpid=%s&corpsecret=%s' % ( self.access_token_url, self.client_id, self.client_secret) response = safe_urlopen(url) self.logger.debug('Response code: %s, content: %s' % (response.status_code, response.content)) data = json.loads(response.content) if data['errcode'] != 0: raise IdentityNotValid('errcode: %d, errmsg: %s' & (data['errcode'], data['errmsg'])) auth_identity.data.update(self.get_identity_data(data)) auth_identity.update(data=auth_identity.data)
def refresh_identity(self, identity, *args, **kwargs): refresh_token = identity.data.get("refresh_token") if not refresh_token: raise IdentityNotValid("Missing refresh token") # XXX(meredith): This is used in VSTS's `get_refresh_token_params` kwargs["identity"] = identity data = self.get_refresh_token_params(refresh_token, *args, **kwargs) req = safe_urlopen( url=self.get_refresh_token_url(), headers=self.get_refresh_token_headers(), data=data ) try: body = safe_urlread(req) payload = json.loads(body) except Exception: payload = {} self.handle_refresh_error(req, payload) identity.data.update(self.get_oauth_data(payload)) return identity.update(data=identity.data)
def dispatch(self, request, organization_slug): if request.method != 'POST': return HttpResponseNotAllowed(['POST']) provider = get_provider(organization_slug) if provider is None: messages.add_message(request, messages.ERROR, ERR_NO_SAML_SSO) return HttpResponseRedirect('/') organization = Organization.objects.get(slug=organization_slug) saml_config = provider.build_saml_config(organization_slug) auth = provider.build_auth(request, saml_config) auth.process_response() errors = auth.get_errors() if errors: error_reason = auth.get_last_error_reason() raise IdentityNotValid(error_reason) attributes = auth.get_attributes() nameid = auth.get_nameid() email = self.retrieve_email(attributes, nameid, provider.config) user_emails = list( UserEmail.objects.filter(email__iexact=email, is_verified=True).order_by('id')) users = [] if user_emails: users = User.objects.filter( id__in=set((ue.user_id for ue in user_emails)), is_active=True, sentry_orgmember_set__organization_id=organization.id) users = list(users[0:2]) if not users: options_jit = provider.config.get('options', {}).get('options_jit', False) if not options_jit: message = "The user with a verified email %s does not exist" % email return HttpResponseServerError(message) else: return HttpResponseServerError( "Just-in-Time provisioning not implemented yet") if len(users) > 1: return HttpResponseServerError( "Found several accounts related with %s on this organization" % email) user = users[0] user.backend = settings.AUTHENTICATION_BACKENDS[0] login_resp = login(request, user, after_2fa=request.build_absolute_uri(), organization_id=organization.id) if login_resp: request.session['saml'] = { 'nameid': nameid, 'nameid_format': auth.get_nameid_format(), 'session_index': auth.get_session_index() } return HttpResponseRedirect(get_login_redirect(request))