Skip to content

Commit

Permalink
more api auth tests
Browse files Browse the repository at this point in the history
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
  • Loading branch information
BeryJu committed Jun 26, 2022
1 parent 797e713 commit f5e0eea
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 19 deletions.
2 changes: 1 addition & 1 deletion authentik/api/authentication.py
Expand Up @@ -26,7 +26,7 @@ def validate_auth(header: bytes) -> str:
if auth_type.lower() != "bearer":
LOGGER.debug("Unsupported authentication type, denying", type=auth_type.lower())
raise AuthenticationFailed("Unsupported authentication type")
if auth_credentials == "": # nosec
if auth_credentials == "": # nosec # noqa
raise AuthenticationFailed("Malformed header")
return auth_credentials

Expand Down
46 changes: 41 additions & 5 deletions authentik/api/tests/test_auth.py
Expand Up @@ -8,28 +8,37 @@

from authentik.api.authentication import bearer_auth
from authentik.core.models import USER_ATTRIBUTE_SA, Token, TokenIntents
from authentik.core.tests.utils import create_test_flow
from authentik.lib.generators import generate_id
from authentik.outposts.managed import OutpostManager
from authentik.providers.oauth2.constants import SCOPE_AUTHENTIK_API
from authentik.providers.oauth2.models import OAuth2Provider, RefreshToken


class TestAPIAuth(TestCase):
"""Test API Authentication"""

def test_valid_bearer(self):
"""Test valid token"""
token = Token.objects.create(intent=TokenIntents.INTENT_API, user=get_anonymous_user())
self.assertEqual(bearer_auth(f"Bearer {token.key}".encode()), token.user)

def test_invalid_type(self):
"""Test invalid type"""
with self.assertRaises(AuthenticationFailed):
bearer_auth("foo bar".encode())

def test_invalid_empty(self):
"""Test invalid type"""
self.assertIsNone(bearer_auth("Bearer ".encode()))
self.assertIsNone(bearer_auth("".encode()))

def test_invalid_no_token(self):
"""Test invalid with no token"""
with self.assertRaises(AuthenticationFailed):
auth = b64encode(":abc".encode()).decode()
self.assertIsNone(bearer_auth(f"Basic :{auth}".encode()))

def test_bearer_valid(self):
"""Test valid token"""
token = Token.objects.create(intent=TokenIntents.INTENT_API, user=get_anonymous_user())
self.assertEqual(bearer_auth(f"Bearer {token.key}".encode()), token.user)

def test_managed_outpost(self):
"""Test managed outpost"""
with self.assertRaises(AuthenticationFailed):
Expand All @@ -38,3 +47,30 @@ def test_managed_outpost(self):
OutpostManager().run()
user = bearer_auth(f"Bearer {settings.SECRET_KEY}".encode())
self.assertEqual(user.attributes[USER_ATTRIBUTE_SA], True)

def test_jwt_valid(self):
"""Test valid JWT"""
provider = OAuth2Provider.objects.create(
name=generate_id(), client_id=generate_id(), authorization_flow=create_test_flow()
)
refresh = RefreshToken.objects.create(
user=get_anonymous_user(),
provider=provider,
refresh_token=generate_id(),
_scope=SCOPE_AUTHENTIK_API,
)
self.assertEqual(bearer_auth(f"Bearer {refresh.refresh_token}".encode()), refresh.user)

def test_jwt_missing_scope(self):
"""Test valid JWT"""
provider = OAuth2Provider.objects.create(
name=generate_id(), client_id=generate_id(), authorization_flow=create_test_flow()
)
refresh = RefreshToken.objects.create(
user=get_anonymous_user(),
provider=provider,
refresh_token=generate_id(),
_scope="",
)
with self.assertRaises(AuthenticationFailed):
self.assertEqual(bearer_auth(f"Bearer {refresh.refresh_token}".encode()), refresh.user)
14 changes: 7 additions & 7 deletions authentik/stages/consent/stage.py
@@ -1,5 +1,5 @@
"""authentik consent stage"""
from typing import Optional, TypedDict
from typing import Optional

from django.http import HttpRequest, HttpResponse
from django.utils.timezone import now
Expand All @@ -20,7 +20,7 @@
PLAN_CONTEXT_CONSENT_TITLE = "consent_title"
PLAN_CONTEXT_CONSENT_HEADER = "consent_header"
PLAN_CONTEXT_CONSENT_PERMISSIONS = "consent_permissions"
PLAN_CONTEXT_CONSNET_ADDITIONAL_PERMISSIONS = "consent_additional_permissions"
PLAN_CONTEXT_CONSNET_EXTRA_PERMISSIONS = "consent_additional_permissions"


class ConsentChallenge(WithUserInfoChallenge):
Expand Down Expand Up @@ -48,7 +48,7 @@ def get_challenge(self) -> Challenge:
"type": ChallengeTypes.NATIVE.value,
"permissions": self.executor.plan.context.get(PLAN_CONTEXT_CONSENT_PERMISSIONS, []),
"additional_permissions": self.executor.plan.context.get(
PLAN_CONTEXT_CONSNET_ADDITIONAL_PERMISSIONS, []
PLAN_CONTEXT_CONSNET_EXTRA_PERMISSIONS, []
),
}
if PLAN_CONTEXT_CONSENT_TITLE in self.executor.plan.context:
Expand Down Expand Up @@ -92,7 +92,7 @@ def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
self.executor.plan.context[PLAN_CONTEXT_CONSENT_PERMISSIONS] = [
x for x in perms if x["id"] in allowed_perms
]
self.executor.plan.context[PLAN_CONTEXT_CONSNET_ADDITIONAL_PERMISSIONS] = [
self.executor.plan.context[PLAN_CONTEXT_CONSNET_EXTRA_PERMISSIONS] = [
x for x in perms if x["id"] in requested_perms.difference(allowed_perms)
]
return super().get(request, *args, **kwargs)
Expand All @@ -106,9 +106,9 @@ def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
if PLAN_CONTEXT_APPLICATION not in self.executor.plan.context:
return self.executor.stage_ok()
application = self.executor.plan.context[PLAN_CONTEXT_APPLICATION]
permissions = self.executor.plan.context[
PLAN_CONTEXT_CONSENT_PERMISSIONS
] + self.executor.plan.context.get(PLAN_CONTEXT_CONSNET_ADDITIONAL_PERMISSIONS, [])
permissions = self.executor.plan.context.get(
PLAN_CONTEXT_CONSENT_PERMISSIONS, []
) + self.executor.plan.context.get(PLAN_CONTEXT_CONSNET_EXTRA_PERMISSIONS, [])
permissions_string = " ".join(x["id"] for x in permissions)
# Make this StageView work when injected, in which case `current_stage` is an instance
# of the base class, and we don't save any consent, as it is assumed to be a one-time
Expand Down
9 changes: 3 additions & 6 deletions web/src/flows/stages/consent/ConsentStage.ts
Expand Up @@ -121,12 +121,9 @@ export class ConsentStage extends BaseStage<ConsentChallenge, ConsentChallengeRe
>
</div>
</ak-form-static>
<div class="pf-c-form__group">
<p id="header-text" class="pf-u-mb-xl">${t`Application`}</p>
${this.challenge.additionalPermissions.length > 0
? this.renderAdditional()
: this.renderNoPrevious()}
</div>
${this.challenge.additionalPermissions.length > 0
? this.renderAdditional()
: this.renderNoPrevious()}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
Expand Down

0 comments on commit f5e0eea

Please sign in to comment.