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_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_export_validate_import(self): """Test export and validate it""" flow_slug = generate_id() with transaction_rollback(): login_stage = UserLoginStage.objects.create(name=generate_id()) flow = Flow.objects.create( slug=flow_slug, designation=FlowDesignation.AUTHENTICATION, name=generate_id(), title=generate_id(), ) FlowStageBinding.objects.update_or_create( target=flow, stage=login_stage, order=0, ) exporter = FlowExporter(flow) export = exporter.export() self.assertEqual(len(export.entries), 3) export_json = exporter.export_to_string() importer = FlowImporter(export_json) self.assertTrue(importer.validate()) self.assertTrue(importer.apply()) self.assertTrue(Flow.objects.filter(slug=flow_slug).exists())
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_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_device_challenge_duo(self): """Test duo""" request = self.request_factory.get("/") stage = AuthenticatorDuoStage.objects.create( name="test", client_id=generate_id(), client_secret=generate_key(), api_hostname="", ) duo_device = DuoDevice.objects.create( user=self.user, stage=stage, ) duo_mock = MagicMock(auth=MagicMock( return_value={ "result": "allow", "status": "allow", "status_msg": "Success. Logging you in...", })) failed_duo_mock = MagicMock(auth=MagicMock( return_value={"result": "deny"})) with patch( "authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.client", duo_mock, ): self.assertEqual( duo_device.pk, validate_challenge_duo(duo_device.pk, request, self.user)) with patch( "authentik.stages.authenticator_duo.models.AuthenticatorDuoStage.client", failed_duo_mock, ): with self.assertRaises(ValidationError): validate_challenge_duo(duo_device.pk, request, self.user)
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_export_validate_import_prompt(self): """Test export and validate it""" with transaction_rollback(): # First stage fields username_prompt = Prompt.objects.create(field_key="username", label="Username", order=0, type=FieldTypes.TEXT) password = Prompt.objects.create( field_key="password", label="Password", order=1, type=FieldTypes.PASSWORD, ) password_repeat = Prompt.objects.create( field_key="password_repeat", label="Password (repeat)", order=2, type=FieldTypes.PASSWORD, ) # Stages first_stage = PromptStage.objects.create(name=generate_id()) first_stage.fields.set( [username_prompt, password, password_repeat]) first_stage.save() flow = Flow.objects.create( name=generate_id(), slug=generate_id(), designation=FlowDesignation.ENROLLMENT, title=generate_id(), ) FlowStageBinding.objects.create(target=flow, stage=first_stage, order=0) exporter = FlowExporter(flow) export = exporter.export() export_json = dumps(export, cls=DataclassEncoder) importer = FlowImporter(export_json) self.assertTrue(importer.validate()) self.assertTrue(importer.apply())
def create_test_flow(designation: FlowDesignation = FlowDesignation.STAGE_CONFIGURATION) -> Flow: """Generate a flow that can be used for testing""" uid = generate_id(10) return Flow.objects.create( name=uid, title=uid, slug=slugify(uid), designation=designation, )
def create_test_admin_user(name: Optional[str] = None) -> User: """Generate a test-admin user""" uid = generate_id(20) if not name else name group = Group.objects.create(name=uid, is_superuser=True) user: User = User.objects.create( username=uid, name=uid, email=f"{uid}@goauthentik.io", ) user.set_password(uid) user.save() group.users.add(user) return user
def test_export_validate_import_policies(self): """Test export and validate it""" flow_slug = generate_id() stage_name = generate_id() with transaction_rollback(): flow_policy = ExpressionPolicy.objects.create( name=generate_id(), expression="return True", ) flow = Flow.objects.create( slug=flow_slug, designation=FlowDesignation.AUTHENTICATION, name=generate_id(), title=generate_id(), ) PolicyBinding.objects.create(policy=flow_policy, target=flow, order=0) user_login = UserLoginStage.objects.create(name=stage_name) fsb = FlowStageBinding.objects.create(target=flow, stage=user_login, order=0) PolicyBinding.objects.create(policy=flow_policy, target=fsb, order=0) exporter = FlowExporter(flow) export = exporter.export() export_json = dumps(export, cls=DataclassEncoder) importer = FlowImporter(export_json) self.assertTrue(importer.validate()) self.assertTrue(importer.apply()) self.assertTrue( UserLoginStage.objects.filter(name=stage_name).exists()) self.assertTrue(Flow.objects.filter(slug=flow_slug).exists())
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_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_full_code(self): """Test full authorization""" flow = Flow.objects.create(slug="empty") provider = OAuth2Provider.objects.create( name="test", client_id="test", authorization_flow=flow, redirect_uris="foo://localhost", ) 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": "code", "client_id": "test", "state": state, "redirect_uri": "foo://localhost", }, ) response = self.client.get( reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), ) code: AuthorizationCode = AuthorizationCode.objects.filter( user=user).first() self.assertJSONEqual( response.content.decode(), { "component": "xak-flow-redirect", "type": ChallengeTypes.REDIRECT.value, "to": f"foo://localhost?code={code.code}&state={state}", }, )
def default_token_key(): """Default token key""" # We use generate_id since the chars in the key should be easy # to use in Emails (for verification) and URLs (for recovery) return generate_id(128)
def create_test_tenant() -> Tenant: """Generate a test tenant, removing all other tenants to make sure this one matches.""" uid = generate_id(20) Tenant.objects.all().delete() return Tenant.objects.create(domain=uid, default=True)
def setUp(self) -> None: super().setUp() self.source: OAuthSource = OAuthSource.objects.create(name="test") self.factory = RequestFactory() self.identifier = generate_id()
def setUp(self): self.client_id = generate_id() self.client_secret = generate_key() self.app_slug = generate_id(20) super().setUp()
def setUp(self) -> None: self.client_id = generate_id() self.client_secret = generate_key() self.source_slug = "oauth1-test" super().setUp()
def setUp(self): self.client_id = generate_id() self.client_secret = generate_key() self.application_slug = "test" super().setUp()
"""Utility script to generate a config for CI runs""" from authentik.lib.generators import generate_id from yaml import safe_dump with open("local.env.yml", "w") as _config: safe_dump({ "log_level": "debug", "secret_key": generate_id(), }, _config, default_flow_style=False)