예제 #1
0
def do_start(httpretty, auth_options):
    """
    Do all the things to setup the authentication but don't actually complete it.
    - generate code, state, nonce and call authorization url
    - build target url
    - request access token, mock JWT signation verification request
    - mock userinfo request
    Return the expected request data
    """
    # backend.start() generates the nonce and creates the Association
    start_url = auth_options.backend.start().url
    assert Association.objects.count() == 1

    # set up the mocked auth requests
    nonce = auth_options.nonce or Association.objects.first().handle
    access_token_body = {
        "access_token": auth_options.access_token,
        "token_type": "bearer",
        "id_token": prepare_id_token(nonce, auth_options),
    }
    target_url = do_auth(httpretty, start_url, auth_options, access_token_body)

    # Now that the auth is all set up, get the start url. `requests` will follow the
    # redirect, so the response text is the body from the mock target url
    response = requests.get(start_url)
    assert response.url == target_url
    assert response.text == "foobar"

    # format the expected request data from the start and target urls
    request_data = parse_qs(urlparse(start_url).query)
    target_request_data = parse_qs(urlparse(target_url).query)
    request_data.update(target_request_data)
    return request_data
예제 #2
0
 def post(self, request, *args, **kwargs):
     input_data = self.get_serializer_in_data()
     provider_name = self.get_provider_name(input_data)
     if not provider_name:
         return self.respond_error("Provider is not specified")
     self.set_input_data(request, input_data)
     decorate_request(request, provider_name)
     serializer_in = self.get_serializer_in(data=input_data)
     if self.oauth_v1(
     ) and request.backend.OAUTH_TOKEN_PARAMETER_NAME not in input_data:
         # oauth1 first stage (1st is get request_token, 2nd is get access_token)
         manual_redirect_uri = self.request.auth_data.pop(
             'redirect_uri', None)
         manual_redirect_uri = self.get_redirect_uri(manual_redirect_uri)
         if manual_redirect_uri:
             self.request.backend.redirect_uri = manual_redirect_uri
         request_token = parse_qs(request.backend.set_unauthorized_token())
         return Response(request_token)
     serializer_in.is_valid(raise_exception=True)
     try:
         user = self.get_object()
     except (AuthException, HTTPError) as e:
         return self.respond_error(e)
     if isinstance(user, HttpResponse):
         # error happened and pipeline returned HttpResponse instead of user
         return user
     resp_data = self.get_serializer(instance=user)
     self.do_login(request.backend, user)
     return Response(resp_data.data)
예제 #3
0
    def post(self, request, *args, **kwargs):
        input_data = self.get_serializer_in_data()
        provider_name = self.get_provider_name(input_data)
        if not provider_name:
            return self.respond_error("Provider is not specified")
        self.set_input_data(request, input_data)
        decorate_request(request, provider_name)
        serializer_in = self.get_serializer_in(data=input_data)
        if self.oauth_v1() and request.backend.OAUTH_TOKEN_PARAMETER_NAME not in input_data:
            # oauth1 first stage (1st is get request_token, 2nd is get access_token)
            request_token = parse_qs(request.backend.set_unauthorized_token())
            return Response(request_token)
        serializer_in.is_valid(raise_exception=True)
        try:
            user = self.get_object()
        except (AuthException, HTTPError) as e:
            return self.respond_error(e)
        if isinstance(user, HttpResponse):  # An error happened and pipeline returned HttpResponse instead of user
            return user
        resp_data = self.get_serializer(instance=user)
        self.do_login(request.backend, user)

        # Validating / re-generating the token
        token, _ = ExpiringToken.objects.get_or_create(
                user=user
            )
        if token.expired():
           token.delete()
           token = ExpiringToken.objects.create(
               user=user
           )
        return Response(resp_data.data)
예제 #4
0
 def test_login_social_oauth1_jwt(self):
     resp = self.client.post(
         reverse('login_social_jwt_user'), data={'provider': 'twitter'})
     self.assertEqual(resp.status_code, 200)
     self.assertEqual(resp.data, parse_qs(self.request_token_body))
     resp = self.client.post(reverse('login_social_jwt_user'), data={
         'provider': 'twitter',
         'oauth_token': 'foobar',
         'oauth_verifier': 'overifier'
     })
     self.assertEqual(resp.status_code, 200)
 def test_login_social_oauth1_session(self):
     resp = self.client.post(
         reverse('login_social_session'), data={'provider': 'twitter'})
     self.assertEqual(resp.status_code, 200)
     self.assertEqual(resp.data, parse_qs(self.request_token_body))
     resp = self.client.post(reverse('login_social_session'), data={
         'provider': 'twitter',
         'oauth_token': 'foobar',
         'oauth_verifier': 'overifier'
     })
     self.assertEqual(resp.status_code, 200)
예제 #6
0
 def oauth_authorization_request(self, token):
     """Generate OAuth request to authorize token."""
     if not isinstance(token, dict):
         token = parse_qs(token)
     params = self.auth_extra_arguments() or {}
     params.update(self.get_scope_argument())
     params[self.OAUTH_TOKEN_PARAMETER_NAME] = token.get(
         self.OAUTH_TOKEN_PARAMETER_NAME)
     params['oauth_token_secret'] = token.get('oauth_token_secret')
     state = self.get_or_create_state()
     params[self.REDIRECT_URI_PARAMETER_NAME] = self.get_redirect_uri(state)
     return '{0}?{1}'.format(self.authorization_url(), urlencode(params))
예제 #7
0
 def test_login_social_oauth1_token(self):
     """
     Currently oauth1 works only if session is enabled.
     Probably it is possible to make it work without session, but
     it will be needed to change the logic in python-social-auth.
     """
     resp = self.client.post(
         reverse('login_social_token_user'), data={'provider': 'twitter'})
     self.assertEqual(resp.status_code, 200)
     self.assertEqual(resp.data, parse_qs(self.request_token_body))
     resp = self.client.post(reverse('login_social_token_user'), data={
         'provider': 'twitter',
         'oauth_token': 'foobar',
         'oauth_verifier': 'overifier'
     })
     self.assertEqual(resp.status_code, 200)
예제 #8
0
 def test_login_social_oauth1_knox(self):
     """
     Currently oauth1 works only if session is enabled.
     Probably it is possible to make it work without session, but
     it will be needed to change the logic in python-social-auth.
     """
     resp = self.client.post(
         reverse('login_social_knox_user'), data={'provider': 'twitter'})
     self.assertEqual(resp.status_code, 200)
     self.assertEqual(resp.data, parse_qs(self.request_token_body))
     resp = self.client.post(reverse('login_social_knox_user'), data={
         'provider': 'twitter',
         'oauth_token': 'foobar',
         'oauth_verifier': 'overifier'
     })
     self.assertEqual(resp.status_code, 200)
예제 #9
0
def do_auth(httpretty, start_url, auth_options, access_token_body):
    """
    Mock all the relevant uris for auth
    Return the target url with the expected code, nonce, redirect uri and params
    """
    complete_url = reverse("gateway:nhsid_complete")
    start_query = parse_qs(urlparse(start_url).query)
    target_url = auth_options.strategy.build_absolute_uri(complete_url)
    target_url = url_add_parameters(target_url,
                                    {"state": start_query["state"]})

    # mock the authorization call and its redirect to the target_url
    httpretty.register_uri(httpretty.GET,
                           start_url,
                           status=301,
                           location=target_url)
    httpretty.register_uri(httpretty.GET,
                           target_url,
                           status=200,
                           body="foobar")

    # Mock the JWK keys request (used to validate JWT id_token); JWK_PUBLIC_KEYS includes
    # the real key and an unsupported one to ensure we can deal with unsupported keys
    httpretty.register_uri(
        httpretty.GET,
        auth_options.backend.jwks_uri(),
        status=200,
        body=json.dumps({"keys": JWK_PUBLIC_KEYS}),
    )
    # Mock the call to get the access token
    httpretty.register_uri(
        httpretty.POST,
        uri=auth_options.backend.access_token_url(),
        status=auth_options.access_token_status,
        body=json.dumps(access_token_body) or "",
        content_type="text/json",
    )
    # Mock the call to the userinfo url
    httpretty.register_uri(
        httpretty.GET,
        auth_options.backend.userinfo_url(),
        body=json.dumps(auth_options.user_data_body) or "",
        content_type="text/json",
    )
    return target_url
예제 #10
0
 def test_login_social_oauth1_jwt(self):
     try:
         import rest_framework_jwt
     except ImportError:
         return
     assert rest_framework_jwt is not None
     resp = self.client.post(reverse('login_social_jwt_user'),
                             data={'provider': 'twitter'})
     self.assertEqual(resp.status_code, 200)
     self.assertEqual(resp.data, parse_qs(self.request_token_body))
     resp = self.client.post(reverse('login_social_token_user'),
                             data={
                                 'provider': 'twitter',
                                 'oauth_token': 'foobar',
                                 'oauth_token_secret': 'boofar',
                                 'oauth_verifier': 'overifier'
                             })
     self.assertEqual(resp.status_code, 200)
예제 #11
0
    def get_unauthorized_token(self):
        """Get unauthorized token from session passed on state parameter."""
        unauthed_tokens = self.session.get('_utoken')
        if not unauthed_tokens:
            raise AuthTokenError(self, 'Missing unauthorized token')

        data_token = self.data.get(self.OAUTH_TOKEN_PARAMETER_NAME)

        if data_token is None:
            raise AuthTokenError(self, 'Missing unauthorized token')

        token = None
        utoken = unauthed_tokens
        orig_utoken = utoken
        if not isinstance(utoken, dict):
            utoken = parse_qs(utoken)
        if utoken.get(self.OAUTH_TOKEN_PARAMETER_NAME) == data_token:
            token = utoken
        else:
            raise AuthTokenError(self, 'Incorrect tokens')
        return token
예제 #12
0
 def post(self, request, *args, **kwargs):
     input_data = self.get_serializer_in_data()
     provider_name = self.get_provider_name(input_data)
     if not provider_name:
         return self.respond_error("Provider is not specified")
     self.set_input_data(request, input_data)
     decorate_request(request, provider_name)
     serializer_in = self.get_serializer_in(data=input_data)
     if self.oauth_v1() and request.backend.OAUTH_TOKEN_PARAMETER_NAME not in input_data:
         # oauth1 first stage (1st is get request_token, 2nd is get access_token)
         request_token = parse_qs(request.backend.set_unauthorized_token())
         return Response(request_token)
     serializer_in.is_valid(raise_exception=True)
     try:
         user = self.get_object()
     except (AuthException, HTTPError) as e:
         return self.respond_error(e)
     if isinstance(user, HttpResponse):  # An error happened and pipeline returned HttpResponse instead of user
         return user
     resp_data = self.get_serializer(instance=user)
     self.do_login(request.backend, user)
     return Response(resp_data.data)
예제 #13
0
 def post(self, request, *args, **kwargs):
     input_data = self.get_serializer_in_data()
     provider_name = self.get_provider_name(input_data)
     if not provider_name:
         return self.respond_error("Provider is not specified")
     self.set_input_data(request, input_data)
     decorate_request(request, provider_name)
     serializer_in = self.get_serializer_in(data=input_data)
     if self.oauth_v1(
     ) and request.backend.OAUTH_TOKEN_PARAMETER_NAME not in input_data:
         # oauth1 first stage (1st is get request_token, 2nd is get access_token)
         manual_redirect_uri = self.request.auth_data.pop(
             'redirect_uri', None)
         manual_redirect_uri = self.get_redirect_uri(manual_redirect_uri)
         if manual_redirect_uri:
             self.request.backend.redirect_uri = manual_redirect_uri
         request_token = parse_qs(request.backend.set_unauthorized_token())
         return Response(request_token)
     serializer_in.is_valid(raise_exception=True)
     try:
         user = self.get_object()
     except (AuthException, HTTPError) as e:
         if isinstance(e, AuthForbidden):
             return Response(
                 {
                     'message':
                     'Tài khoản đăng nhập không phải VNPAY, TRIPI or TEKO.'
                 },
                 status=status.HTTP_400_BAD_REQUEST)
         return Response({'message': 'Đăng nhập thất bại!'},
                         status=status.HTTP_400_BAD_REQUEST)
     if isinstance(
             user, HttpResponse
     ):  # An error happened and pipeline returned HttpResponse instead of user
         return user
     resp_data = self.get_serializer(instance=user)
     # self.do_login(request.backend, user)
     if user.is_active is False:
         return Response(
             {
                 'message':
                 'Tài khoản đã bị khóa, vui lòng liên hệ admin để được hỗ trợ.'
             },
             status=status.HTTP_400_BAD_REQUEST)
     if not user.groups.all():
         if not user.is_superuser:
             return Response(
                 {'message': 'Bạn không có bất kì nhóm quyền nào'},
                 status=status.HTTP_400_BAD_REQUEST)
     elif len(user.groups.all()) > 1:
         return Response(
             {
                 'message':
                 'Bạn có nhiều hơn 1 nhóm quyền, liên hệ admin để được giải quyết'
             },
             status=status.HTTP_400_BAD_REQUEST)
     else:
         group = user.get_group()
         if group is None:
             return Response(
                 {
                     'message':
                     'Có lỗi xảy ra với nhóm quyền của bạn. Vui lòng liên hệ Admin để được hỗ trợ'
                 },
                 status=status.HTTP_400_BAD_REQUEST)
         elif not group.status:
             return Response(
                 {
                     'message':
                     'Nhóm quyền gắn với tài khoản của bạn đang tạm khóa. Vui lòng liên hệ Admin để được hỗ trợ'
                 },
                 status=status.HTTP_400_BAD_REQUEST)
     return Response(resp_data.data)
예제 #14
0
    def test_full_login_process(self):
        """Asserts the nominal login process works."""
        sso_location = "http://testserver/account/saml/local-accepting-idp/sso/"
        entity_descriptor_list = [
            generate_idp_metadata(
                entity_id=sso_location,
                sso_location=sso_location,
                ui_info_display_names=format_mdui_display_name(
                    "Local accepting IdP"),
            ),
        ]

        # 1/ Select Idp in the provider list
        with mock.patch("urllib.request.urlopen") as urlopen_mock:

            class UrlOpenMock:
                """Mockin object for the urlopen"""
                def read(self):
                    """Allow object to be read several times."""
                    return generate_idp_federation_metadata(
                        entity_descriptor_list=entity_descriptor_list,
                    ).encode("utf-8")

            urlopen_mock.return_value = UrlOpenMock()

            response = self.client.get(
                reverse("account:saml_fer_idp_choice"), )

            self.assertContains(
                response,
                f'action="{reverse("account:social:begin", args=("saml_fer",))}"',
            )
            self.assertContains(response, "local-accepting-idp")

            response = self.client.get(
                f'{reverse("account:social:begin", args=("saml_fer",))}?idp=local-accepting-idp',
            )

            self.assertEqual(response.status_code, 302)
            self.assertTrue(response["Location"].startswith(
                "http://testserver/account/saml/local-accepting-idp/sso/?SAMLRequest="
            ))

            # 2/ Fake the redirection to the SSO
            response = self.client.get(
                f'{reverse("account:social:begin", args=("saml_fer",))}?idp=local-accepting-idp',
                follow=False,
            )

            # 3/ Generate SAML response using SAML request
            query_values = parse_qs(urlparse(response["Location"]).query)
            saml_request = query_values["SAMLRequest"]
            saml_relay_state = query_values["RelayState"]
            readable_saml_request = OneLogin_Saml2_Utils.decode_base64_and_inflate(
                saml_request, )
            saml_request = OneLogin_Saml2_XML.to_etree(readable_saml_request)
            saml_acs_url = saml_request.get("AssertionConsumerServiceURL")
            request_id = saml_request.get("ID")

            auth_response = OneLogin_Saml2_Utils.b64encode(
                generate_auth_response(
                    request_id,
                    saml_acs_url,
                    issuer=
                    "http://testserver/account/saml/local-accepting-idp/sso/",
                ))

            # 4/ POST the data to our endpoint
            response = self.client.post(
                saml_acs_url,
                data={
                    "RelayState": saml_relay_state,
                    "SAMLResponse": auth_response,
                },
            )
            self.assertEqual(response.status_code, 302)
            self.assertEqual(response["Location"], "/")

        # Assert the user is authenticated
        user = auth_get_user(self.client)
        self.assertTrue(user.is_authenticated)

        # Assert the user has an organization
        organization_access = user.organization_accesses.select_related(
            "organization").get()  # also assert there is only one organization
        self.assertEqual(organization_access.role, STUDENT)
        self.assertEqual(organization_access.organization.name,
                         "OrganizationDName")