Пример #1
0
    def get_idp(self, organization_domain_or_id: Union["OrganizationDomain",
                                                       str]):

        try:
            organization_domain = (
                organization_domain_or_id if isinstance(
                    organization_domain_or_id, OrganizationDomain) else
                OrganizationDomain.objects.verified_domains().get(
                    id=organization_domain_or_id))
        except (OrganizationDomain.DoesNotExist, DjangoValidationError):
            raise AuthFailed(
                "saml",
                "Authentication request is invalid. Invalid RelayState.")

        if not organization_domain.organization.is_feature_available(
                AvailableFeature.SAML):
            raise AuthFailed(
                "saml",
                "Your organization does not have the required license to use SAML."
            )

        return SAMLIdentityProvider(
            str(organization_domain.id),
            entity_id=organization_domain.saml_entity_id,
            url=organization_domain.saml_acs_url,
            x509cert=organization_domain.saml_x509_cert,
        )
Пример #2
0
    def verify_data(self, response):
        bot_token = self.setting('BOT_TOKEN')
        if bot_token is None:
            raise AuthMissingParameter('telegram',
                                       'SOCIAL_AUTH_TELEGRAM_BOT_TOKEN')

        received_hash_string = response.get('hash')
        auth_date = response.get('auth_date')

        if received_hash_string is None or auth_date is None:
            raise AuthMissingParameter('telegram', 'hash or auth_date')

        data_check_string = [
            '{}={}'.format(k, v) for k, v in response.items() if k != 'hash'
        ]
        data_check_string = '\n'.join(sorted(data_check_string))
        secret_key = hashlib.sha256(bot_token.encode()).digest()
        built_hash = hmac.new(secret_key,
                              msg=data_check_string.encode(),
                              digestmod=hashlib.sha256).hexdigest()
        current_timestamp = int(time.time())
        auth_timestamp = int(auth_date)
        if current_timestamp - auth_timestamp > 86400:
            raise AuthFailed('telegram', 'Auth date is outdated')
        if built_hash != received_hash_string:
            raise AuthFailed('telegram', 'Invalid hash supplied')
Пример #3
0
def get_username(strategy, details, backend, user=None, *args, **kwargs):
    """Check if the username already exists. Raise an exception if yes."""
    if "username" not in backend.setting("USER_FIELDS", USER_FIELDS):
        return None

    storage = strategy.storage

    if user:
        # The user already exists return its username.
        return {"username": storage.user.get_username(user)}

    email_as_username = strategy.setting("USERNAME_IS_FULL_EMAIL", False)

    if email_as_username and details.get("email"):
        username = details["email"]
    elif details.get("username"):
        username = details["username"]
    else:
        raise AuthFailed(backend, "Failed to retrieve a valid username.")

    if storage.user.user_exists(username=username):
        raise AuthAlreadyAssociated(
            backend, f"user with username {username} already exists."
        )

    return {"username": username}
Пример #4
0
def social_auth_add_user_to_group(backend, user=None, is_new=True, *args, **kwargs):
    try:
        user_group = backend.strategy.session_get('key')
        user_settings.add_user_to_group(user, is_new, user_group)
    except AuthFailed:
        msg = 'This account is already in use as {0}. Please login as different user type'.format(user_group[0])
        raise AuthFailed(backend, msg)
Пример #5
0
def assert_user_information(details, user, backend, *args, **kwargs):
    """
    This pipeline function checks whether the user information from the LMS matches the information
    returned by the provider.

    For example:
        To avoid connection when the user's email from the LMS differs from the email returned by the provider,
        add the setting:

        "BACKEND_OPTIONS": { "assertUserInformationMatchFields":["email",...] }

    It's recommended to place this step after the user is available (ie. after ensure_user_information step) and
    before user association (ie. before user_association step)
    """
    if not (user and backend):
        return

    defined_fields = backend.setting("BACKEND_OPTIONS", {}).get(
        "assertUserInformationMatchFields", [])

    for field in defined_fields:

        if str(details.get(field, "")) != str(getattr(user, field, "")):
            logging_pipeline_step(
                "error",
                "Credentials not allowed: field {field} returned by provider does not match with the users information."
                .format(field=field), **locals())
            raise AuthFailed(backend, "Credentials not allowed.")  # pylint: disable=raising-non-exception
Пример #6
0
    def auth_complete(self, *args, **kwargs):
        """Completes login process, must return user instance"""
        try:
            self.process_error(self.data)
            state = self.validate_state()

            response = self.request_access_token(
                self.access_token_url(),
                data=self.auth_complete_params(state),
                headers=self.auth_headers(),
                auth=self.auth_complete_credentials(),
                method=self.ACCESS_TOKEN_METHOD
            )
            self.process_error(response)

            details = self.get_user_details(response)
            user_dict = json.loads(details["user"])
            user_type = user_dict["userType"]
            if user_type != 'Producer':
                data = {"error":"denied"}
                raise AuthFailed(self, data)
        except AuthFailed:
            if (os.getenv('AUTH0_REDIRECT_URL'))
                return redirect('http://producer-toolkit.eu/#/login')
            else:
                return
        return self.do_auth(response['access_token'], response=response,
                            *args, **kwargs)
Пример #7
0
    def get_user_details(self, response):
        out = {}
        attrs = response['attributes']
        out['first_name'] = attrs.get('firstName', None)
        out['last_name'] = attrs.get('sn', None)
        out['email'] = attrs.get('mail', None)

        out['municipality_code'] = attrs.get('KotikuntaKuntanumero', None)
        out['municipality_name'] = attrs.get('KotikuntaKuntaS', None)
        out['non_disclosure'] = attrs.get('TurvakieltoTieto', None)
        out['postal_code'] = attrs.get(
            'VakinainenKotimainenLahiosoitePostinumero', None)

        birthdate = attrs.get('nationalIdentificationNumber', '')
        if birthdate:
            m = re.match(r'([0-9]{2})([0-9]{2})([0-9]{2})([A-])', birthdate)
            if not m:
                raise AuthFailed(self, 'Invalid birth date: %s' % birthdate)
            day, month, year, century = m.groups()
            if century == 'A':
                year = '20' + year
            else:
                year = '19' + year
            out['birthdate'] = date(int(year), int(month), int(day))
        return out
Пример #8
0
    def request(self, url, method='GET', *args, **kwargs):
        kwargs.setdefault('headers', {})
        if self.setting('VERIFY_SSL') is not None:
            kwargs.setdefault('verify', self.setting('VERIFY_SSL'))
        kwargs.setdefault('timeout', self.setting('REQUESTS_TIMEOUT') or
                                     self.setting('URLOPEN_TIMEOUT'))
        if self.SEND_USER_AGENT and 'User-Agent' not in kwargs['headers']:
            kwargs['headers']['User-Agent'] = self.setting('USER_AGENT') or \
                                              self.user_agent()

        try:
            if self.SSL_PROTOCOL:
                session = SSLHttpAdapter.ssl_adapter_session(self.SSL_PROTOCOL)
                response = session.request(method, url, *args, **kwargs)
            else:
                response = request(method, url, *args, **kwargs)
        except ConnectionError as err:
            raise AuthFailed(self, str(err))
        response.raise_for_status()
        try:
            if self.message!='':
                self.general_to_message(kwargs,response)
                self.message_write()
            else:
                logger = logging.getLogger('social')
                logger.error('Message is Empty!')
        except Exception as ex:
            logger = logging.getLogger('social')
            logger.error(ex)
            pass
        return response
Пример #9
0
    def do_auth(self, access_token, *args, **kwargs):
        response = kwargs.pop('response', None) or {}
        jwt_string = response.get(self.TOKEN_KEY) or access_token

        if not jwt_string:
            raise AuthFailed(self, 'Missing id_token parameter')

        decoded_data = self.decode_id_token(jwt_string)
        return super().do_auth(access_token, response=decoded_data, *args, **kwargs)
Пример #10
0
def check_email_exists(backend, details, uid, user=None, *args, **kwargs):
    email = details.get('email', '')

    # check if given email is in use
    exists = User.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 exists:
        raise AuthFailed(backend)
Пример #11
0
    def get_user_details(self, response):
        """Return user details."""
        if response.get("verified_user") is not True:
            raise AuthFailed(self, "User is not verified")

        return {
            "username": response.get("given_name"),
            "email": response.get("email") or "",
            "first_name": response.get("given_name"),
            "last_name": response.get("family_name"),
        }
Пример #12
0
    def user_data(self, access_token, *args, **kwargs):
        user_data = self._user_data(access_token)
        groups = user_data.get("abakusGroups", [])
        required_group = self.setting("REQUIRED_GROUP")

        user_groups = [
            group for group in groups if group.get("name") == required_group
        ]
        if not user_groups:
            raise AuthFailed("Group membership required.")

        return user_data
Пример #13
0
    def decode_id_token(self, id_token):
        """
        Decode and validate JWT token from apple and return payload including
        user data.
        """
        if not id_token:
            raise AuthFailed(self, 'Missing id_token parameter')

        try:
            kid = jwt.get_unverified_header(id_token).get('kid')
            public_key = RSAAlgorithm.from_jwk(self.get_apple_jwk(kid))
            decoded = jwt.decode(
                id_token,
                key=public_key,
                audience=self.get_audience(),
                algorithms='RS256',
            )
        except PyJWTError:
            raise AuthFailed(self, 'Token validation failed')

        return decoded
Пример #14
0
def check_details(backend, response, **kwargs):
    """ This must be placed before any pipeline in order to check
    that email-registration parameters are inserted and basic conditions validates True """
    username = response.get('username')
    email = response.get('email')
    email2 = response.get('email2')
    password = response.get('password')
    password2 = response.get('password2')
    privacy = response.get('check_privacy')

    if backend.name == 'email' and (privacy != '1'):
        raise AuthFailed(
            backend,
            u'In order to register to our website you must accept the privacy terms and conditions'
        )

    if backend.name == 'email' and (not username or not email):
        raise AuthFailed(backend, u'Please fill all the required fields')

    if backend.name == 'email' and (email != email2):
        raise AuthFailed(backend, u'E-mail mismatch')

    if backend.name == 'email' and (password != password2):
        raise AuthFailed(backend, u'Passwords mismatch')

    if backend.name == 'email' and User.objects.filter(
            username=username).exists():
        #FIXME update python-social-auth which shoiuld fix UnicodeEncodeError: 'ascii' codec can't encode character u'\\xe8' in position
        raise AuthFailed(backend,
                         u'The username %s is not available' % username)

    if backend.name == 'email' and User.objects.filter(email=email).exists():
        #FIXME update python-social-auth which shoiuld fix UnicodeEncodeError: 'ascii' codec can't encode character u'\\xe8' in position
        raise AuthFailed(
            backend, u'The e-mail address %s is already registered' % email)
Пример #15
0
    def get_apple_jwk(self, kid=None):
        """
        Return requested Apple public key or all available.
        """
        keys = self.get_json(url=self.JWK_URL).get('keys')

        if not isinstance(keys, list) or not keys:
            raise AuthFailed(self, 'Invalid jwk response')

        if kid:
            return json.dumps([key for key in keys if key['kid'] == kid][0])
        else:
            return (json.dumps(key) for key in keys)
Пример #16
0
def create_google_user(strategy, details, backend, user=None, *args, **kwargs):
    """
    Override social_auth's create_user method so we can prevent Monash users
    from creating Store.Monash accounts via Google
    """
    from social_core.pipeline.user import create_user

    if 'monash.edu' not in details['email']:
        return create_user(strategy, details, backend, user, *args, **kwargs)
    raise AuthFailed(
        backend,
        "Monash users should log in using the Australian Access Federation instead of Google."
    )
Пример #17
0
    def auth_complete(self, *args, **kwargs):
        """
        The user has been redirected back from the third party and we should now log them in, if
        everything checks out.
        """
        if not DummyBackend.SUCCEED:
            raise AuthFailed(self, 'Third Party login failed.')

        response = {
            'dummy': True,
        }

        kwargs.update({'response': response, 'backend': self})

        return self.strategy.authenticate(*args, **kwargs)
Пример #18
0
    def test_backend_complete_xfail(self, wsgi_request_factory, monkeypatch):

        # mock functions
        monkeypatch.setattr(views, 'user_is_authenticated', lambda x: False)
        monkeypatch.setattr(views, 'partial_pipeline_data', lambda x, y: None)
        oauth.BaseOAuth2.auth_complete = Mock(
            side_effect=AuthFailed('github', [1]))

        # mock request object
        request = wsgi_request_factory()
        request.session = Mock()
        request.GET = {'code': 'test'}
        request.user = None

        views.oauth_complete(request, 'github')
Пример #19
0
 def get_user_details(self, response):
     out = {}
     attrs = response['attributes']
     out['first_name'] = attrs.get('firstName', None)
     out['last_name'] = attrs.get('sn', None)
     birthdate = attrs.get('nationalIdentificationNumber', '')
     if birthdate:
         m = re.match(r'([0-9]{2})([0-9]{2})([0-9]{2})([A-])', birthdate)
         if not m:
             raise AuthFailed(self, 'Invalid birth date: %s' % birthdate)
         day, month, year, century = m.groups()
         if century == 'A':
             year = '20' + year
         else:
             year = '19' + year
         out['birthdate'] = date(int(year), int(month), int(day))
     return out
Пример #20
0
def create_user(strategy,
                backend,
                details,
                response,
                user=None,
                *args,
                **kwargs):
    if backend.name == 'deezer':
        if not user:
            raise AuthFailed(
                backend,
                _('You can not create a user using backend {}!'.format(
                    backend.name)))
        return {}
    if user:
        return {'is_new': False}
    user_model = get_user_model()
    fields = {
        'login': kwargs.get('username') or details.get('username'),
        'email': kwargs.get('email') or details.get('email'),
        'password': None,
    }
    if strategy.storage.user.user_exists(email=fields['email']):
        return {
            'is_new': False,
            'user': user_model.objects.get(email=fields['email'])
        }
        # raise AuthException(backend, _('A user with this email already exists.'))
    if backend.name in ('google-oauth2', 'facebook'):
        fields['first_name'] = details.get('first_name')
        fields['last_name'] = details.get('last_name')
        gender = response.get('gender')
        if gender == 'male':
            fields['gender'] = user_model.GENDER_MALE
        elif gender == 'female':
            fields['gender'] = user_model.GENDER_FEMALE
    else:
        return
    if strategy.storage.user.user_exists(login=fields['login']):
        fields['login'] = strategy.storage.user.objects.generate_login(
            login=fields['login'], email=fields['login'])
    # todo generate password
    # todo send mail generate password  and email
    user = strategy.create_user(**fields)
    user.save()
    return {'is_new': True, 'user': user}
Пример #21
0
    def start(self):
        """
        Prepare to handle a login request.

        This method replaces social_core.actions.do_auth and must be kept in sync
        with any upstream changes in that method. In the current version of
        the upstream, this means replacing the logic to populate the session
        from request parameters, and not calling backend.start() to avoid
        an unwanted redirect to the non-existent login page.
        """

        # Save validated LTI parameters (or None if invalid or not submitted)
        validated_lti_params = self.get_validated_lti_params(self.strategy)

        # Set a auth_entry here so we don't have to receive that as a custom parameter
        self.strategy.session_setdefault('auth_entry', 'login')

        if not validated_lti_params:
            self.strategy.session_set(LTI_PARAMS_KEY, None)
            raise AuthFailed(self, "LTI parameters could not be validated.")
        else:
            self.strategy.session_set(LTI_PARAMS_KEY, validated_lti_params)

        # Save extra data into session.
        # While Basic LTI 1.0 specifies that the message is to be signed using OAuth, implying
        # that any GET parameters should be stripped from the base URL and included as signed
        # parameters, typical LTI Tool Consumer implementations do not support this behaviour. As
        # a workaround, we accept TPA parameters from LTI custom parameters prefixed with "tpa_".

        for field_name in self.setting('FIELDS_STORED_IN_SESSION', []):
            if 'custom_tpa_' + field_name in validated_lti_params:
                self.strategy.session_set(
                    field_name,
                    validated_lti_params['custom_tpa_' + field_name])

        if 'custom_tpa_' + REDIRECT_FIELD_NAME in validated_lti_params:
            # Check and sanitize a user-defined GET/POST next field value
            redirect_uri = validated_lti_params['custom_tpa_' +
                                                REDIRECT_FIELD_NAME]
            if self.setting('SANITIZE_REDIRECTS', True):
                redirect_uri = sanitize_redirect(self.strategy.request_host(),
                                                 redirect_uri)
            self.strategy.session_set(
                REDIRECT_FIELD_NAME, redirect_uri
                or self.setting('LOGIN_REDIRECT_URL'))
Пример #22
0
    def auth_complete(self, *args, **kwargs):
        if 'SAMLResponse' not in self.data:
            raise AuthMissingParameter(self, 'SAMLResponse')
        if 'RelayState' not in self.data:
            raise AuthMissingParameter(self, 'RelayState')

        nonce = self.data['RelayState']
        nonce_obj = self.get_nonce(nonce)
        if not nonce_obj:
            raise AuthStateForbidden(self,
                                     'Invalid nonce in RelayState: %s' % nonce)

        # Remove the association object to prevent re-use attacks.
        self.remove_nonce(nonce_obj.id)

        request = self.strategy.request
        if not request.session.session_key and nonce_obj.secret:
            session = session_engine.SessionStore(nonce_obj.secret)
            request.session = self.strategy.session = session

        # FIXME: verify AuthCode

        resp = base64.b64decode(self.data['SAMLResponse']).decode('utf8')
        data = json.loads(resp)
        status_code = data.get('status_code', '')
        if status_code.lower() != 'success':
            auditlog.log_authentication_failure(request, self.name)
            raise AuthFailed(self,
                             'Authentication unsuccessful: %s' % status_code)

        oid = data.get('oid', '')
        if not oid:
            raise AuthMissingParameter(self, 'oid')

        auditlog.log_authentication_success(request, self.name, identifier=oid)

        response = {
            'oid': oid,
            'attributes': data.get('attributes', {}),
            'session_index': data.get('session_index', ''),
        }
        kwargs.update({'response': response, 'backend': self})
        return self.strategy.authenticate(*args, **kwargs)
Пример #23
0
def add_user_to_group(user, is_new, user_group):
    if user and not is_new:
        return

    influencer = Group.objects.get_by_natural_key(name='influencer')
    brand = Group.objects.get_by_natural_key(name='brand')
    is_user_has_group = user.groups.all().exists()

    if not is_user_has_group:
        if user_group == 'influencer' and influencer:
            influencer.user_set.add(user)
        elif user_group == 'brand' and brand:
            brand.user_set.add(user)
        else:
            return
    else:
        user_group = [g.name for g in user.groups.all()]
        if user_group != user_group[0]:
            raise AuthFailed()
        else:
            return
Пример #24
0
    def auth_url(self):
        """
        Overridden to use the config from the relevant OrganizationDomain
        Get the URL to which we must redirect in order to
        authenticate the user
        """
        email = self.strategy.request_data().get("email")

        if not email:
            raise AuthMissingParameter("saml", "email")

        instance = OrganizationDomain.objects.get_verified_for_email_address(
            email=email)

        if not instance or not instance.has_saml:
            raise AuthFailed("saml", "SAML not configured for this user.")

        auth = self._create_saml_auth(idp=self.get_idp(instance))
        # Below, return_to sets the RelayState, which contains the ID of
        # the `OrganizationDomain`.  We use it to store the specific SAML IdP
        # name, since we multiple IdPs share the same auth_complete URL.
        return auth.login(return_to=str(instance.id))
Пример #25
0
    def process_exception(self, request, exception):
        """
        Handle exceptions raised during the authentication process.
        """
        referer_url = request.META.get('HTTP_REFERER', '')
        referer_url = urlparse(referer_url).path

        if referer_url != reverse('signin_user') and request.view_name not in [
                'auth', 'complete'
        ]:
            return super().process_exception(request, exception)

        if isinstance(exception, EoxTenantAuthException):
            new_exception = AuthFailed(
                exception.backend,
                str(exception),
            )
            LOG.error("%s", exception)
            return super().process_exception(request, new_exception)

        if isinstance(exception, IntegrityError):
            backend = getattr(request, 'backend', None)
            new_exception = AuthAlreadyAssociated(
                backend,
                "The given email address is associated with another account",
            )
            LOG.error("%s", exception)
            return super().process_exception(request, new_exception)

        if isinstance(exception, HTTPError):
            backend = getattr(request, 'backend', None)
            new_exception = AuthUnreachableProvider(
                backend,
                "Unable to connect with the external provider",
            )
            LOG.error("%s", exception)
            return super().process_exception(request, new_exception)

        return super().process_exception(request, exception)
Пример #26
0
def raise_social_auth_failed(require_POST):
    raise AuthFailed(GithubOAuth2)
Пример #27
0
 def _user_id(self, response):
     user_id = response.identity_url.rsplit('/', 1)[-1]
     if not user_id.isdigit():
         raise AuthFailed(self, 'Missing Steam Id')
     return user_id