def test_with_signing_cert(self): """Test Metadata with signing cert""" create_test_cert() metadata = ServiceProviderMetadataParser().parse(METADATA_CERT) provider = metadata.to_provider("test", self.flow) self.assertEqual(provider.acs_url, "http://localhost:8080/apps/user_saml/saml/acs") self.assertEqual(provider.issuer, "http://localhost:8080/apps/user_saml/saml/metadata") self.assertEqual(provider.sp_binding, SAMLBindings.POST) self.assertEqual(provider.verification_kp.certificate_data, CERT) self.assertIsNotNone(provider.signing_kp) self.assertEqual(provider.audience, "")
def test_redirect_uri_error(self): """test OpenID Provider flow (invalid redirect URI, check error message)""" sleep(1) # Bootstrap all needed objects authorization_flow = Flow.objects.get( slug="default-provider-authorization-implicit-consent" ) provider = OAuth2Provider.objects.create( name=self.application_slug, client_type=ClientTypes.CONFIDENTIAL, client_id=self.client_id, client_secret=self.client_secret, signing_key=create_test_cert(), redirect_uris="http://localhost:9009/", authorization_flow=authorization_flow, ) provider.property_mappings.set( ScopeMapping.objects.filter( scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE] ) ) provider.save() Application.objects.create( name=self.application_slug, slug=self.application_slug, provider=provider, ) self.container = self.setup_client() self.driver.get("http://localhost:9009/implicit/") sleep(2) self.assertEqual( self.driver.find_element(By.CLASS_NAME, "pf-c-title").text, "Redirect URI Error", )
def test_request_auth_code(self): """test request param""" provider = OAuth2Provider.objects.create( name="test", client_id=generate_id(), client_secret=generate_key(), authorization_flow=create_test_flow(), redirect_uris="http://testserver", signing_key=create_test_cert(), ) header = b64encode(f"{provider.client_id}:{provider.client_secret}". encode()).decode() user = create_test_admin_user() code = AuthorizationCode.objects.create(code="foobar", provider=provider, user=user) request = self.factory.post( "/", data={ "grant_type": GRANT_TYPE_AUTHORIZATION_CODE, "code": code.code, "redirect_uri": "http://testserver", }, HTTP_AUTHORIZATION=f"Basic {header}", ) params = TokenParams.parse(request, provider, provider.client_id, provider.client_secret) self.assertEqual(params.provider, provider) with self.assertRaises(TokenError): TokenParams.parse(request, provider, provider.client_id, generate_key())
def test_request_refresh_token(self): """test request param""" provider = OAuth2Provider.objects.create( name="test", client_id=generate_id(), client_secret=generate_key(), authorization_flow=create_test_flow(), redirect_uris="http://local.invalid", signing_key=create_test_cert(), ) header = b64encode(f"{provider.client_id}:{provider.client_secret}". encode()).decode() user = create_test_admin_user() token: RefreshToken = RefreshToken.objects.create( provider=provider, user=user, refresh_token=generate_id(), ) request = self.factory.post( "/", data={ "grant_type": GRANT_TYPE_REFRESH_TOKEN, "refresh_token": token.refresh_token, "redirect_uri": "http://local.invalid", }, HTTP_AUTHORIZATION=f"Basic {header}", ) params = TokenParams.parse(request, provider, provider.client_id, provider.client_secret) self.assertEqual(params.provider, provider)
def test_used_by(self): """Test used_by endpoint""" self.client.force_login(create_test_admin_user()) keypair = create_test_cert() provider = OAuth2Provider.objects.create( name="test", client_id="test", client_secret=generate_key(), authorization_flow=create_test_flow(), redirect_uris="http://localhost", signing_key=keypair, ) response = self.client.get( reverse( "authentik_api:certificatekeypair-used-by", kwargs={"pk": keypair.pk}, )) self.assertEqual(200, response.status_code) self.assertJSONEqual( response.content.decode(), [{ "app": "authentik_providers_oauth2", "model_name": "oauth2provider", "pk": str(provider.pk), "name": str(provider), "action": DeleteAction.SET_NULL.name, }], )
def setUp(self) -> None: super().setUp() ObjectManager().run() self.app = Application.objects.create(name="test", slug="test") self.provider: OAuth2Provider = OAuth2Provider.objects.create( name="test", client_id=generate_id(), client_secret=generate_key(), authorization_flow=create_test_flow(), redirect_uris="", signing_key=create_test_cert(), ) self.provider.property_mappings.set(ScopeMapping.objects.all()) # Needs to be assigned to an application for iss to be set self.app.provider = self.provider self.app.save() self.user = create_test_admin_user() self.token: RefreshToken = RefreshToken.objects.create( provider=self.provider, user=self.user, access_token=generate_id(), refresh_token=generate_id(), _scope="openid user profile", _id_token=json.dumps(asdict(IDToken("foo", "bar"), )), )
def test_refresh_token_revoke(self): """test request param""" provider = OAuth2Provider.objects.create( name="test", client_id=generate_id(), client_secret=generate_key(), authorization_flow=create_test_flow(), redirect_uris="http://testserver", signing_key=create_test_cert(), ) # Needs to be assigned to an application for iss to be set self.app.provider = provider self.app.save() header = b64encode(f"{provider.client_id}:{provider.client_secret}". encode()).decode() user = create_test_admin_user() token: RefreshToken = RefreshToken.objects.create( provider=provider, user=user, refresh_token=generate_id(), ) # Create initial refresh token response = self.client.post( reverse("authentik_providers_oauth2:token"), data={ "grant_type": GRANT_TYPE_REFRESH_TOKEN, "refresh_token": token.refresh_token, "redirect_uri": "http://testserver", }, HTTP_AUTHORIZATION=f"Basic {header}", ) new_token: RefreshToken = (RefreshToken.objects.filter( user=user).exclude(pk=token.pk).first()) # Post again with initial token -> get new refresh token # and revoke old one response = self.client.post( reverse("authentik_providers_oauth2:token"), data={ "grant_type": GRANT_TYPE_REFRESH_TOKEN, "refresh_token": new_token.refresh_token, "redirect_uri": "http://local.invalid", }, HTTP_AUTHORIZATION=f"Basic {header}", ) self.assertEqual(response.status_code, 200) # Post again with old token, is now revoked and should error response = self.client.post( reverse("authentik_providers_oauth2:token"), data={ "grant_type": GRANT_TYPE_REFRESH_TOKEN, "refresh_token": new_token.refresh_token, "redirect_uri": "http://local.invalid", }, HTTP_AUTHORIZATION=f"Basic {header}", ) self.assertEqual(response.status_code, 400) self.assertTrue( Event.objects.filter( action=EventAction.SUSPICIOUS_REQUEST).exists())
def test_authorization_consent_explicit(self): """test OpenID Provider flow (default authorization flow with explicit consent)""" sleep(1) # Bootstrap all needed objects authorization_flow = Flow.objects.get( slug="default-provider-authorization-explicit-consent") provider = OAuth2Provider.objects.create( name=self.application_slug, authorization_flow=authorization_flow, client_type=ClientTypes.CONFIDENTIAL, client_id=self.client_id, client_secret=self.client_secret, signing_key=create_test_cert(), redirect_uris="http://localhost:9009/auth/callback", ) provider.property_mappings.set( ScopeMapping.objects.filter(scope_name__in=[ SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE ])) provider.save() app = Application.objects.create( name=self.application_slug, slug=self.application_slug, provider=provider, ) self.container = self.setup_client() self.driver.get("http://localhost:9009") self.login() self.wait.until( ec.presence_of_element_located( (By.CSS_SELECTOR, "ak-flow-executor"))) flow_executor = self.get_shadow_root("ak-flow-executor") consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor) self.assertIn( app.name, consent_stage.find_element(By.CSS_SELECTOR, "#header-text").text, ) consent_stage.find_element( By.CSS_SELECTOR, ("[type=submit]"), ).click() self.wait.until( ec.presence_of_element_located((By.CSS_SELECTOR, "pre"))) body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text) self.assertEqual(body["IDTokenClaims"]["nickname"], self.user.username) self.assertEqual(body["UserInfo"]["nickname"], self.user.username) self.assertEqual(body["IDTokenClaims"]["name"], self.user.name) self.assertEqual(body["UserInfo"]["name"], self.user.name) self.assertEqual(body["IDTokenClaims"]["email"], self.user.email) self.assertEqual(body["UserInfo"]["email"], self.user.email)
def test_authorization_logout(self): """test OpenID Provider flow with logout""" sleep(1) # Bootstrap all needed objects authorization_flow = Flow.objects.get( slug="default-provider-authorization-implicit-consent") provider = OAuth2Provider.objects.create( name="grafana", client_type=ClientTypes.CONFIDENTIAL, client_id=self.client_id, client_secret=self.client_secret, signing_key=create_test_cert(), redirect_uris="http://localhost:3000/login/generic_oauth", authorization_flow=authorization_flow, ) provider.property_mappings.set( ScopeMapping.objects.filter(scope_name__in=[ SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE ])) provider.save() Application.objects.create( name="Grafana", slug=self.app_slug, provider=provider, ) self.driver.get("http://localhost:3000") self.driver.find_element(By.CLASS_NAME, "btn-service--oauth").click() self.login() self.wait_for_url("http://localhost:3000/?orgId=1") self.driver.get("http://localhost:3000/profile") self.assertEqual( self.driver.find_element(By.CLASS_NAME, "page-header__title").text, self.user.name, ) self.assertEqual( self.driver.find_element( By.CSS_SELECTOR, "input[name=name]").get_attribute("value"), self.user.name, ) self.assertEqual( self.driver.find_element( By.CSS_SELECTOR, "input[name=email]").get_attribute("value"), self.user.email, ) self.assertEqual( self.driver.find_element( By.CSS_SELECTOR, "input[name=login]").get_attribute("value"), self.user.email, ) self.driver.get("http://localhost:3000/logout") self.wait_for_url( self.url( "authentik_core:if-session-end", application_slug=self.app_slug, )) self.driver.find_element(By.ID, "logout").click()
def test_metadata(self): """Test Metadata generation being valid""" source = SAMLSource.objects.create( slug="provider", issuer="authentik", signing_kp=create_test_cert(), pre_authentication_flow=create_test_flow(), ) request = self.factory.get("/") xml = MetadataProcessor(source, request).build_entity_descriptor() metadata = ElementTree.fromstring(xml) self.assertEqual(metadata.attrib["entityID"], "authentik")
def test_metadata_schema(self): """Test Metadata generation being valid""" source = SAMLSource.objects.create( slug="provider", issuer="authentik", signing_kp=create_test_cert(), pre_authentication_flow=create_test_flow(), ) request = self.factory.get("/") xml = MetadataProcessor(source, request).build_entity_descriptor() metadata = etree.fromstring(xml) # nosec schema = etree.XMLSchema( etree.parse("xml/saml-schema-metadata-2.0.xsd")) # nosec self.assertTrue(schema.validate(metadata))
def test_signed_static(self): """Test post request with static request""" provider = SAMLProvider( name="aws", authorization_flow=create_test_flow(), acs_url=("https://eu-central-1.signin.aws.amazon.com/platform/" "saml/acs/2d737f96-55fb-4035-953e-5e24134eb778"), audience="https://10.120.20.200/saml-sp/SAML2/POST", issuer="https://10.120.20.200/saml-sp/SAML2/POST", signing_kp=create_test_cert(), ) parsed_request = AuthNRequestParser(provider).parse(POST_REQUEST) self.assertEqual(parsed_request.id, "aws_LDxLGeubpc5lx12gxCgS6uPbix1yd5re") self.assertEqual(parsed_request.name_id_policy, SAML_NAME_ID_FORMAT_EMAIL)
def test_refresh_token_view(self): """test request param""" provider = OAuth2Provider.objects.create( name="test", client_id=generate_id(), client_secret=generate_key(), authorization_flow=create_test_flow(), redirect_uris="http://local.invalid", signing_key=create_test_cert(), ) # Needs to be assigned to an application for iss to be set self.app.provider = provider self.app.save() header = b64encode(f"{provider.client_id}:{provider.client_secret}". encode()).decode() user = create_test_admin_user() token: RefreshToken = RefreshToken.objects.create( provider=provider, user=user, refresh_token=generate_id(), ) response = self.client.post( reverse("authentik_providers_oauth2:token"), data={ "grant_type": GRANT_TYPE_REFRESH_TOKEN, "refresh_token": token.refresh_token, "redirect_uri": "http://local.invalid", }, HTTP_AUTHORIZATION=f"Basic {header}", HTTP_ORIGIN="http://local.invalid", ) new_token: RefreshToken = (RefreshToken.objects.filter( user=user).exclude(pk=token.pk).first()) self.assertEqual(response["Access-Control-Allow-Credentials"], "true") self.assertEqual(response["Access-Control-Allow-Origin"], "http://local.invalid") self.assertJSONEqual( response.content.decode(), { "access_token": new_token.access_token, "refresh_token": new_token.refresh_token, "token_type": "bearer", "expires_in": 2592000, "id_token": provider.encode(new_token.id_token.to_dict(), ), }, ) self.validate_jwt(new_token, provider)
def test_rs256(self): """Test JWKS request with RS256""" provider = OAuth2Provider.objects.create( name="test", client_id="test", authorization_flow=create_test_flow(), redirect_uris="http://local.invalid", signing_key=create_test_cert(), ) app = Application.objects.create(name="test", slug="test", provider=provider) response = self.client.get( reverse("authentik_providers_oauth2:jwks", kwargs={"application_slug": app.slug})) body = json.loads(response.content.decode()) self.assertEqual(len(body["keys"]), 1)
def test_certificate_download(self): """Test certificate export (download)""" self.client.force_login(create_test_admin_user()) keypair = create_test_cert() response = self.client.get( reverse( "authentik_api:certificatekeypair-view-certificate", kwargs={"pk": keypair.pk}, )) self.assertEqual(200, response.status_code) response = self.client.get( reverse( "authentik_api:certificatekeypair-view-certificate", kwargs={"pk": keypair.pk}, ) + "?download", ) self.assertEqual(200, response.status_code) self.assertIn("Content-Disposition", response)
def test_serializer(self): """Test API Validation""" keypair = create_test_cert() self.assertTrue( CertificateKeyPairSerializer( data={ "name": keypair.name, "certificate_data": keypair.certificate_data, "key_data": keypair.key_data, }).is_valid()) self.assertFalse( CertificateKeyPairSerializer( data={ "name": keypair.name, "certificate_data": "test", "key_data": "test", }).is_valid())
def setUp(self): ObjectManager().run() cert = create_test_cert() self.provider: SAMLProvider = SAMLProvider.objects.create( authorization_flow=create_test_flow(), acs_url="http://testserver/source/saml/provider/acs/", signing_kp=cert, verification_kp=cert, ) self.provider.property_mappings.set(SAMLPropertyMapping.objects.all()) self.provider.save() self.source = SAMLSource.objects.create( slug="provider", issuer="authentik", signing_kp=cert, pre_authentication_flow=create_test_flow(), ) self.factory = RequestFactory()
def test_full_implicit(self): """Test full authorization""" flow = Flow.objects.create(slug="empty") provider = OAuth2Provider.objects.create( name="test", client_id="test", client_secret=generate_key(), authorization_flow=flow, redirect_uris="http://localhost", signing_key=create_test_cert(), ) Application.objects.create(name="app", slug="app", provider=provider) state = generate_id() user = create_test_admin_user() self.client.force_login(user) # Step 1, initiate params and get redirect to flow self.client.get( reverse("authentik_providers_oauth2:authorize"), data={ "response_type": "id_token", "client_id": "test", "state": state, "scope": "openid", "redirect_uri": "http://localhost", }, ) response = self.client.get( reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), ) token: RefreshToken = RefreshToken.objects.filter(user=user).first() self.assertJSONEqual( response.content.decode(), { "component": "xak-flow-redirect", "type": ChallengeTypes.REDIRECT.value, "to": (f"http://localhost#access_token={token.access_token}" f"&id_token={provider.encode(token.id_token.to_dict())}&token_type=bearer" f"&expires_in=60&state={state}"), }, ) self.validate_jwt(token, provider)
def test_service_account_permissions(self): """Test that the service account has correct permissions""" provider: ProxyProvider = ProxyProvider.objects.create( name="test", internal_host="http://localhost", external_host="http://localhost", authorization_flow=create_test_flow(), ) outpost: Outpost = Outpost.objects.create( name="test", type=OutpostType.PROXY, ) # Before we add a provider, the user should only have access to the outpost permissions = UserObjectPermission.objects.filter(user=outpost.user) self.assertEqual(len(permissions), 1) self.assertEqual(permissions[0].object_pk, str(outpost.pk)) # We add a provider, user should only have access to outpost and provider outpost.providers.add(provider) permissions = UserObjectPermission.objects.filter(user=outpost.user).order_by( "content_type__model" ) self.assertEqual(len(permissions), 2) self.assertEqual(permissions[0].object_pk, str(outpost.pk)) self.assertEqual(permissions[1].object_pk, str(provider.pk)) # Provider requires a certificate-key-pair, user should have permissions for it keypair = create_test_cert() provider.certificate = keypair provider.save() permissions = UserObjectPermission.objects.filter(user=outpost.user).order_by( "content_type__model" ) self.assertEqual(len(permissions), 3) self.assertEqual(permissions[0].object_pk, str(keypair.pk)) self.assertEqual(permissions[1].object_pk, str(outpost.pk)) self.assertEqual(permissions[2].object_pk, str(provider.pk)) # Remove provider from outpost, user should only have access to outpost outpost.providers.remove(provider) permissions = UserObjectPermission.objects.filter(user=outpost.user) self.assertEqual(len(permissions), 1) self.assertEqual(permissions[0].object_pk, str(outpost.pk))
def test_authorization_denied(self): """test OpenID Provider flow (default authorization with access deny)""" sleep(1) # Bootstrap all needed objects authorization_flow = Flow.objects.get( slug="default-provider-authorization-explicit-consent" ) provider = OAuth2Provider.objects.create( name=self.application_slug, authorization_flow=authorization_flow, client_type=ClientTypes.CONFIDENTIAL, client_id=self.client_id, client_secret=self.client_secret, signing_key=create_test_cert(), redirect_uris="http://localhost:9009/implicit/", ) provider.property_mappings.set( ScopeMapping.objects.filter( scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE] ) ) provider.save() app = Application.objects.create( name=self.application_slug, slug=self.application_slug, provider=provider, ) negative_policy = ExpressionPolicy.objects.create( name="negative-static", expression="return False" ) PolicyBinding.objects.create(target=app, policy=negative_policy, order=0) self.container = self.setup_client() self.driver.get("http://localhost:9009/implicit/") sleep(2) self.login() self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "header > h1"))) self.assertEqual( self.driver.find_element(By.CSS_SELECTOR, "header > h1").text, "Permission denied", )
def test_auth_code_view(self): """test request param""" provider = OAuth2Provider.objects.create( name="test", client_id=generate_id(), client_secret=generate_key(), authorization_flow=create_test_flow(), redirect_uris="http://local.invalid", signing_key=create_test_cert(), ) # Needs to be assigned to an application for iss to be set self.app.provider = provider self.app.save() header = b64encode(f"{provider.client_id}:{provider.client_secret}". encode()).decode() user = create_test_admin_user() code = AuthorizationCode.objects.create(code="foobar", provider=provider, user=user, is_open_id=True) response = self.client.post( reverse("authentik_providers_oauth2:token"), data={ "grant_type": GRANT_TYPE_AUTHORIZATION_CODE, "code": code.code, "redirect_uri": "http://local.invalid", }, HTTP_AUTHORIZATION=f"Basic {header}", ) new_token: RefreshToken = RefreshToken.objects.filter( user=user).first() self.assertJSONEqual( response.content.decode(), { "access_token": new_token.access_token, "refresh_token": new_token.refresh_token, "token_type": "bearer", "expires_in": 2592000, "id_token": provider.encode(new_token.id_token.to_dict(), ), }, ) self.validate_jwt(new_token, provider)
def test_authorization_consent_implied(self): """test OpenID Provider flow (default authorization flow with implied consent)""" sleep(1) # Bootstrap all needed objects authorization_flow = Flow.objects.get( slug="default-provider-authorization-implicit-consent" ) provider = OAuth2Provider.objects.create( name=self.application_slug, client_type=ClientTypes.CONFIDENTIAL, client_id=self.client_id, client_secret=self.client_secret, signing_key=create_test_cert(), redirect_uris="http://localhost:9009/implicit/", authorization_flow=authorization_flow, ) provider.property_mappings.set( ScopeMapping.objects.filter( scope_name__in=[SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE] ) ) provider.save() Application.objects.create( name=self.application_slug, slug=self.application_slug, provider=provider, ) self.container = self.setup_client() self.driver.get("http://localhost:9009/implicit/") sleep(2) self.login() self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "pre"))) sleep(1) body = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text) self.assertEqual(body["profile"]["nickname"], self.user.username) self.assertEqual(body["profile"]["name"], self.user.name) self.assertEqual(body["profile"]["email"], self.user.email)
def test_authorization_consent_explicit(self): """test OpenID Provider flow (default authorization flow with explicit consent)""" sleep(1) # Bootstrap all needed objects authorization_flow = Flow.objects.get( slug="default-provider-authorization-explicit-consent") provider = OAuth2Provider.objects.create( name="grafana", authorization_flow=authorization_flow, client_type=ClientTypes.CONFIDENTIAL, client_id=self.client_id, client_secret=self.client_secret, signing_key=create_test_cert(), redirect_uris="http://localhost:3000/login/generic_oauth", ) provider.property_mappings.set( ScopeMapping.objects.filter(scope_name__in=[ SCOPE_OPENID, SCOPE_OPENID_EMAIL, SCOPE_OPENID_PROFILE ])) provider.save() app = Application.objects.create( name="Grafana", slug=self.app_slug, provider=provider, ) self.driver.get("http://localhost:3000") self.driver.find_element(By.CLASS_NAME, "btn-service--oauth").click() self.login() self.wait.until( ec.presence_of_element_located( (By.CSS_SELECTOR, "ak-flow-executor"))) sleep(1) flow_executor = self.get_shadow_root("ak-flow-executor") consent_stage = self.get_shadow_root("ak-stage-consent", flow_executor) self.assertIn( app.name, consent_stage.find_element(By.CSS_SELECTOR, "#header-text").text, ) consent_stage.find_element( By.CSS_SELECTOR, ("[type=submit]"), ).click() self.wait_for_url("http://localhost:3000/?orgId=1") self.driver.get("http://localhost:3000/profile") self.assertEqual( self.driver.find_element(By.CLASS_NAME, "page-header__title").text, self.user.name, ) self.assertEqual( self.driver.find_element( By.CSS_SELECTOR, "input[name=name]").get_attribute("value"), self.user.name, ) self.assertEqual( self.driver.find_element( By.CSS_SELECTOR, "input[name=email]").get_attribute("value"), self.user.email, ) self.assertEqual( self.driver.find_element( By.CSS_SELECTOR, "input[name=login]").get_attribute("value"), self.user.email, )