Example #1
0
    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 = "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)

        auth_identity.data.update(self.get_oauth_data(payload))
        auth_identity.update(data=auth_identity.data)
Example #2
0
    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"],
            "scopes": [],  # Google apps do not have user scopes
            "data": self.get_oauth_data(data),
        }
Example #3
0
    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)
Example #4
0
    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:
            # 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))
        return identity.update(data=identity.data)
Example #5
0
    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 = {}

        self.handle_refresh_error(req, payload)

        identity.data.update(self.get_oauth_data(payload))
        return identity.update(data=identity.data)
Example #6
0
    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():
            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([_f for _f in name if _f])

        return {
            "id": attributes[Attributes.IDENTIFIER],
            "email": attributes[Attributes.USER_EMAIL],
            "name": name,
        }
Example #7
0
    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))