def auth_complete(self, *args, **kwargs): """Completes loging process, must return user instance""" try: context_id = self.strategy.request.session.get('_krb5', None) if context_id not in self._krb5: raise AuthException( self.name, 'Authentication failed. context_id not found!') if not self._krb5[context_id].complete: raise AuthException(self.name, 'Authentication failed') kwargs.update({ 'response': { 'krb5_initiator': str(self._krb5[context_id].initiator_name), }, 'backend': self, }) finally: if context_id in self._krb5: del self._krb5[context_id] if '_krb5' in self.strategy.request.session: del self.strategy.request.session['_krb5'] return self.strategy.authenticate(*args, **kwargs)
def save_profile(backend, user, response, *args, **kwargs): userpic_key, user_about_url = None, None if backend.name == 'google-oauth2': userpic_key = 'picture' elif backend.name == 'vk-oauth2': userpic_key = 'user_photo' user_about_requested_fields = ('bdate',) user_about_url = urlunparse( ('https', # scheme 'api.vk.com', # netloc '/method/users.get', # path None, # params urlencode({ 'fields': ','.join(user_about_requested_fields), 'access_token': response.get('access_token', None), 'version': '5.95', }), # query None # fragment ) ) if userpic_key and \ not user.shopuserextended.udpate_userpic(response.get(userpic_key)): raise AuthException("Cannot update userpic") if user_about_url: age_status = user.validate_age(user_about_url) if age_status is None: logger.error(f"Cannot define age by URL {user_about_url}") elif not age_status: raise AuthException("Age must be more than 17")
def verify_open(strategy, backend, user=None, **kwargs): """Check whether it is possible to create new user.""" if not user and not settings.REGISTRATION_OPEN: raise AuthException(backend, _('New registrations are disabled!')) if user and settings.DEMO_SERVER and user.username == 'demo': raise AuthException(backend, _('Can not change authentication for demo!'))
def verify_open(strategy, backend, user=None, **kwargs): """Check whether it is possible to create new user.""" # Check whether registration is open if not user and not settings.REGISTRATION_OPEN: raise AuthException(backend, _('New registrations are disabled!')) # Avoid adding associations to demo user if user and settings.DEMO_SERVER and user.username == 'demo': raise AuthException(backend, _('Can not change authentication for demo!')) # Ensure it's still same user request = strategy.request if request.user.pk != request.session.get('social_auth_user'): raise AuthStateForbidden(backend, 'user')
def verify_open(strategy, backend, user=None, **kwargs): """Check whether it is possible to create new user.""" if not user and not settings.REGISTRATION_OPEN: raise AuthException(backend, _('New registrations are disabled!')) if user and settings.DEMO_SERVER and user.username == 'demo': raise AuthException(backend, _('Can not change authentication for demo!')) # This is mostly fix for lack of next validation in Python Social Auth # see https://github.com/python-social-auth/social-core/issues/62 url = backend.strategy.session_get('next') if url and not is_safe_url(url): backend.strategy.session_set('next', None)
def check_user_allowed(strategy, backend, details, *args, **kwargs): """ Check if a social platform account is being attached to a Django account. If it is, the Django account must be active in order to continue. """ user_id = strategy.session_get('user_id') if not user_id: return None user = backend.strategy.storage.user.get_user(pk=user_id) if user is None: raise AuthException(backend, 'Account not found') if not user.is_active: raise AuthException(backend, 'Account is not activated')
def associate_by_email(backend, details, user=None, *args, **kwargs): """ Associate current auth with a user with the same email address in the DB. This pipeline entry is not 100% secure unless you know that the providers enabled enforce email verification on their side, otherwise a user can attempt to take over another user account by using the same (not validated) email address on some provider. This pipeline entry is disabled by default. """ if user: return None email = details.get('email') if email: # Try to associate accounts registered with the same email address, # only if it's a single object. AuthException is raised if multiple # objects are returned. users = list(backend.strategy.storage.user.get_users_by_email(email)) if len(users) == 0: social = UserSocialAuth.objects.filter( uid=email, provider='email' ).first() if social: return {'user': social.user} else: return None elif len(users) > 1: raise AuthException( backend, 'The given email address is associated with another account' ) else: return {'user': users[0]}
def verify_open(strategy, backend, user=None, **kwargs): ''' Checks whether it is possible to create new user. ''' if not user and not settings.REGISTRATION_OPEN: raise AuthException(backend, _('New registrations are disabled!'))
def get_engine(self, request=None): """ Retrieves dataset service engine """ # Get Token for HydroShare interactions if self.engine == self.HYDROSHARE: # Constants HYDROSHARE_OAUTH_PROVIDER_NAME = 'hydroshare' user = request.user try: # social = user.social_auth.get(provider='google-oauth2') social = user.social_auth.get( provider=HYDROSHARE_OAUTH_PROVIDER_NAME) apikey = social.extra_data['access_token'] # noqa: F841 except ObjectDoesNotExist: # User is not associated with that provider # Need to prompt for association raise AuthException( "HydroShare authentication required. To automate the authentication prompt " "decorate your controller function with the @ensure_oauth('hydroshare') decorator." ) return HydroShareDatasetEngine(endpoint=self.endpoint, username=self.username, password=self.password, apikey=self.apikey) return CkanDatasetEngine(endpoint=self.endpoint, username=self.username, password=self.password, apikey=self.apikey)
def unauthorized_token(self): """ Return request for unauthorized token (first stage) Mediawiki request token is requested from e.g.: * https://en.wikipedia.org/w/index.php?title=Special:OAuth/initiate """ params = self.request_token_extra_arguments() params.update(self.get_scope_argument()) params['title'] = 'Special:OAuth/initiate' key, secret = self.get_key_and_secret() print(key, secret) print(self.setting('URL')) response = self.request(self.setting('URL'), params=params, auth=OAuth1( key, secret, callback_uri=self.setting('CALLBACK')), method=self.REQUEST_TOKEN_METHOD) if response.content.decode().startswith('Error'): raise AuthException(self, response.content.decode()) return response.content.decode()
def check_social_data(backend, details, *args, **kwargs): if backend.name != 'deezer' and not (kwargs.get('email') or details.get('email')): raise AuthException( backend, _('Email address is required in order to complete the registration!' )) return {}
def user_data(self): """ Returns user data from the JWT """ jwt_decode_handler = api_settings.JWT_DECODE_HANDLER cookies = self.strategy.request.COOKIES if api_settings.JWT_AUTH_COOKIE not in cookies: raise AuthException(self, "Authorization header not present") auth = cookies.get(api_settings.JWT_AUTH_COOKIE) try: # this should have the remote username in the ID_KEY field return jwt_decode_handler(auth) except jwt.InvalidTokenError as exc: raise AuthException(self, "Invalid JWT") from exc
def associate_by_name_id(backend, details, response, request, user=None, *args, **kwargs): """ Этот пайплайн занимается поиском пользователя по nameId в зависимости от параметра name_id_type из конфига """ idp = backend.get_idp(response['idp_name']) name_id_type = idp.conf['name_id_type'] name_id = response['attributes']['name_id'] request.session['saml:nameid'] = name_id # Дополнительные параметры на основе IDP saml_superuser_ou = idp.conf.get('verme_superuser_ou', None) saml_create_user_employee = idp.conf.get('verme_create_user_employee', False) saml_update_user = idp.conf.get('verme_update_user', False) saml_update_employee = idp.conf.get('verme_update_employee', False) kwargs = None if name_id_type == 'username': kwargs = {'username': name_id} elif name_id_type == 'number': kwargs = {'employee__number': name_id} elif name_id_type == 'phone': kwargs = {'employee__notify_phone': name_id} else: # email по умолчанию kwargs = {'email': name_id} users = list(User.objects.filter(**kwargs)) params = response['attributes'] # Если не нашли пользователя if len(users) == 0: # Если включено создание пользователя if saml_create_user_employee: user = create_or_update_employee(backend, saml_superuser_ou, **params) else: return None # Если нашли пользователя if len(users) == 1: # Если включено обновление пользователя if saml_update_user: user = create_or_update_employee(backend, saml_superuser_ou, saml_update_user, saml_update_employee, users[0], **params) else: user = users[0] # Если нашли несколько пользователей if len(users) > 1: raise AuthException( backend, f'Parameters {kwargs} are associated multiple users') return {'user': user, 'is_new': False}
def test_exception_handling(self): """Assert an AuthException results in a 400 error""" err = AuthException('openid', 'Auth error') exp_msg = ( "Error: There was an error during authentication 'Auth error', " "please check the provided url.") msg, errno = app.auth_error_handler(err) self.assertEqual(400, errno) self.assertEqual(exp_msg, msg)
def forbid_hijack(strategy, backend, **kwargs): # pylint: disable=unused-argument """ Forbid an admin user from trying to login/register while hijacking another user Args: strategy (social_django.strategy.DjangoStrategy): the strategy used to authenticate backend (social_core.backends.base.BaseAuth): the backend being used to authenticate """ # As first step in pipeline, stop a hijacking admin from going any further if strategy.session_get("is_hijacked_user"): raise AuthException("You are hijacking another user, don't try to login again") return {}
def test_post_facebook_provider_code_validation_fails(self): data = {'code': 'XYZ', 'state': 'ABC'} mock.patch( 'social_core.backends.facebook.FacebookOAuth2.auth_complete', side_effect=AuthException(backend=None)).start() mock.patch('social_core.backends.oauth.OAuthAuth.get_session_state', return_value=data['state']).start() request = self.factory.post(data=data) response = self.view(request, provider='facebook') self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST)
def get_user_details(self, response): """ Returns detail about the user account from the service """ current_req = get_current_request() site = getattr(current_req, "site", None) domain = getattr(site, "domain", None) try: user_site_domain = response["companyInfo"]["url"] if not domain: LOGGER.exception("Domain not found in request attributes") raise AuthException("Colaraz", "Error while authentication") elif user_site_domain.lower() not in domain: LOGGER.exception("User can only login through {} site".format( user_site_domain)) raise AuthException( "Colaraz", "Your account belongs to {}".format(user_site_domain)) details = { "fullname": u"{} {}".format(response["firstName"], response["lastName"]).encode( "ascii", "ignore"), "email": response["email"], "first_name": response["firstName"], "last_name": response["lastName"], "username": response["email"] } return details except KeyError: LOGGER.exception("User profile data is unappropriate or not given") raise AuthException( "Colaraz", "User profile data is unappropriate or not given")
def check_email_exists(backend, details, uid, user=None, *args, **kwargs): email = details.get('email', '') provider = backend.name # check if social user exists to allow logging in (not sure if this is necessary) social = backend.strategy.storage.user.get_social_auth(provider, uid) # check if given email is in use exists = Account.objects.filter(email=email).exists() # user is not logged in, social profile with given uid doesn't exist # and email is in use if not user and not social and exists: raise AuthException(backend)
def initialize_engine_object(engine, endpoint, apikey=None, username=None, password=None, request=None): """ Initialize a DatasetEngine object from a string that points at the engine class. """ # Constants HYDROSHARE_OAUTH_PROVIDER_NAME = 'hydroshare' # Derive import parts from engine string engine_split = engine.split('.') module_string = '.'.join(engine_split[:-1]) engine_class_string = engine_split[-1] # Import module = __import__(module_string, fromlist=[engine_class_string]) EngineClass = getattr(module, engine_class_string) # Get Token for HydroShare interactions if EngineClass is HydroShareDatasetEngine: user = request.user try: # social = user.social_auth.get(provider='google-oauth2') social = user.social_auth.get( provider=HYDROSHARE_OAUTH_PROVIDER_NAME) apikey = social.extra_data['access_token'] except ObjectDoesNotExist: # User is not associated with that provider # Need to prompt for association raise AuthException( "HydroShare authentication required. To automate the authentication prompt decorate " "your controller function with the @ensure_oauth('hydroshare') decorator." ) except AttributeError: # Anonymous User... raise except AuthAlreadyAssociated: raise except: raise # Create Engine Object engine_instance = EngineClass(endpoint=endpoint, apikey=apikey, username=username, password=password) return engine_instance
def check_email_exists(backend, user, response, *args, **kwargs): # TODO twitter, google, facebook, github email = response.get('email') print(email) if not email: return # check if given email is in use count = User.objects.filter(username=email).count() # user is not logged in, social profile with given uid doesn't exist # and email is in use if not user and count: # TODO messages raise AuthException(backend, 'This email is already in use.')
def auth_complete(self, request, *args, **kwargs): print "Auth complete!", args, kwargs ssoParams = request.GET.get('sso') ssoSignature = request.GET.get('sig') paramSignature = hmac.new(settings.DISCOURSE_SSO_SECRET, ssoParams, sha256).hexdigest() if ssoSignature != paramSignature: raise AuthException('Could not verify discourse login') decodedParams = b64decode(ssoParams) kwargs.update({'sso':'', 'sig': '', 'backend': self, 'response': urlparse.parse_qs(decodedParams)}) return self.strategy.authenticate(*args, **kwargs)
def check_user_backend(is_new, user, *args, **kwargs): backend = kwargs['backend'].name user_email = kwargs['details']['email'] if backend == GOOGLE_OAUTH2_SOCIAL_DJANGO_BACKEND and not User.objects.filter( email=user_email).exists(): user_email_domain = user_email.split("@")[1] if user_email_domain == 'eventbrite.com': language = kwargs['request'].LANGUAGE_CODE user = User.objects.create_user(user_email, None, preferred_language=language) else: raise AuthException(backend, 'Invalid Login') return {'is_new': is_new, 'user': user}
def unique_by_email(backend, details, user=None, *args, **kwargs): if user: return None email = details.get('email') if email: users = list(backend.strategy.storage.user.get_users_by_email(email)) if len(users) == 0: return None else: raise AuthException( backend, 'The given email address already exists' )
def test_post_facebook_provider_code_validation_fails(self): data = {"code": "XYZ", "state": "ABC"} mock.patch( "social_core.backends.facebook.FacebookOAuth2.auth_complete", side_effect=AuthException(backend=None), ).start() mock.patch( "social_core.backends.oauth.OAuthAuth.get_session_state", return_value=data["state"], ).start() request = self.factory.post() request.GET = {k: v for k, v in six.iteritems(data)} response = self.view(request, provider="facebook") self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST)
def policy(self): request = self.strategy.request if request and hasattr(request, "policy"): #if request has a property policy, use that policy directly, #The features(mfa set, mfa reset, and password reset) use this proerty 'policy' to specific the customized policy policy = request.policy else: domain = (utils.get_domain( request.session.get(utils.REDIRECT_FIELD_NAME)) or utils.get_host(request)) if request else None if not domain or domain == settings.AUTH2_DOMAIN: #Domain is None or dmain is auth2, use the user flow's default policy userflow = CustomizableUserflow.get_userflow(None) logger.debug( "Use the default userflow({1}.{2}) for domain({0})".format( domain, userflow.domain, userflow.default)) policy = userflow.default else: userflow = CustomizableUserflow.get_userflow(domain) if userflow.fixed: #A fixed policy is configured for the domain related userflow, use it directly logger.debug( "Use the fixed userflow({1}.{2}) for domain({0})". format(domain, userflow.domain, userflow.fixed)) policy = userflow.fixed else: #if the cookie 'PREFERED_IDP_COOKIE' exists, and try to get the policy from the latest used idp idpid = request.COOKIES.get( settings.PREFERED_IDP_COOKIE_NAME, None) if request else None idp = IdentityProvider.get_idp(idpid) if idpid else None if idp and idp.userflow: #Found the latest used idp, and that idp has a configued user flow, use the user flow directly policy = idp.userflow else: #Can't find the latest used idp, user the domain related userflow's default policy policy = userflow.default logger.debug( "Prefered idp is '{}', Choosed userflow is '{}', request domain is '{}' " .format(idp, policy, domain)) if not policy or not policy.lower().startswith('b2c_'): raise AuthException('SOCIAL_AUTH_AZUREAD_B2C_OAUTH2_POLICY is ' 'required and should start with `b2c_`') return policy
def get_redirect_uri(self, state=None): """ Returns redirect uri for oauth redirection """ current_req = get_current_request() environ = getattr(current_req, "environ", {}) schema = environ.get("wsgi.url_scheme", "http") site = getattr(current_req, "site", None) domain = getattr(site, "domain", None) if not domain: LOGGER.exception("Domain not found in request attributes") raise AuthException("Colaraz", "Error while authentication") return "{}://{}/auth/complete/{}".format(schema, domain, self.name)
def registrate_user(*args, **kwargs): print("===========================") pprint(kwargs) email = kwargs.get('details', {}).get('email', None) user = kwargs.get('user', None) is_new = kwargs.get('is_new', None) if not user: if email: user, is_new = User.objects.get_or_create(username=email, email=email) return {"username": email, "user": user, "is_new": is_new} else: raise AuthException(kwargs['backend']) else: pass print("===========================")
def link_to_existing_user_by_email_if_backend_is_trusted( backend, details, user=None, *args, **kwargs): """Return user entry with same email address as one returned on details.""" if user or not _is_trusted_email_backend(backend): return email = details.get('email') if email: # try to link accounts registered with the same email address, # only if it's a single object. AuthException is raised if multiple # objects are returned try: return {'user': EmailAddress.objects.get(email=email).user} except MultipleObjectsReturned: raise AuthException(kwargs['backend'], 'Not unique email address.') except ObjectDoesNotExist: pass
def associate_by_username(backend, details, user=None, *args, **kwargs): if user: return None username = details.get('username') if username: # Try to associate accounts registered with the same username, # only if it's a single object. AuthException is raised if multiple # objects are returned. users = list(User.objects.filter(agol_info__agol_username=username)) if len(users) == 0: return None elif len(users) > 1: raise AuthException( backend, 'The given username is associated with another account') else: return {'user': users[0], 'is_new': False}
def associate_by_email_or_pause(strategy, details, user=None, backend=None, is_new=False, *args, **kwargs): """ Associate current auth with a user with the same email address in the DB. This pipeline entry is not 100% secure unless you know that the providers enabled enforce email verification on their side, otherwise a user can attempt to take over another user account by using the same (not validated) email address on some provider. This pipeline entry is disabled by default. We redirect the user to a access denied page where they can resume the login in the case an admin grants them access. """ if user: return None email = details.get('email') if email: # Try to associate accounts registered with the same email address, # only if it's a single object. AuthException is raised if multiple # objects are returned. users = list(strategy.storage.user.get_users_by_email(email)) if len(users) == 0: # Redirect to an error page notifying user their email hasn't # been added by the admins. This page can be re-visited once the # user account has been added to the system. current_partial = kwargs.get('current_partial') return strategy.redirect( '/request-access?partial_token={0}&backend={1}'.format( current_partial.token, backend.name)) elif len(users) > 1: raise AuthException( backend, 'The given email address is associated with another account') else: return {'user': users[0], 'is_new': False}