Compare commits
	
		
			23 Commits
		
	
	
		
			version/20
			...
			version/20
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 5725e54334 | |||
| c20856ca17 | |||
| 9c73e9cf4e | |||
| b10c3db13d | |||
| fe290aa214 | |||
| a2e69bd250 | |||
| d2a35eb8de | |||
| 3437d8b4b0 | |||
| b862bf4284 | |||
| de22a367b1 | |||
| 17ab895652 | |||
| a4d5815e1b | |||
| e81d3dad3e | |||
| 5aabaebd96 | |||
| 7b60bca297 | |||
| a07d7456c8 | |||
| f33369bf0c | |||
| 1abcff39c7 | |||
| c1caf84d92 | |||
| 86c069fe64 | |||
| ce0140ef67 | |||
| bba43c5109 | |||
| d99a415502 | 
| @ -1,5 +1,5 @@ | |||||||
| [bumpversion] | [bumpversion] | ||||||
| current_version = 2021.2.4-stable | current_version = 2021.2.6-stable | ||||||
| tag = True | tag = True | ||||||
| commit = True | commit = True | ||||||
| parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*) | parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*) | ||||||
|  | |||||||
							
								
								
									
										14
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @ -18,11 +18,11 @@ jobs: | |||||||
|       - name: Building Docker Image |       - name: Building Docker Image | ||||||
|         run: docker build |         run: docker build | ||||||
|           --no-cache |           --no-cache | ||||||
|           -t beryju/authentik:2021.2.4-stable |           -t beryju/authentik:2021.2.6-stable | ||||||
|           -t beryju/authentik:latest |           -t beryju/authentik:latest | ||||||
|           -f Dockerfile . |           -f Dockerfile . | ||||||
|       - name: Push Docker Container to Registry (versioned) |       - name: Push Docker Container to Registry (versioned) | ||||||
|         run: docker push beryju/authentik:2021.2.4-stable |         run: docker push beryju/authentik:2021.2.6-stable | ||||||
|       - name: Push Docker Container to Registry (latest) |       - name: Push Docker Container to Registry (latest) | ||||||
|         run: docker push beryju/authentik:latest |         run: docker push beryju/authentik:latest | ||||||
|   build-proxy: |   build-proxy: | ||||||
| @ -48,11 +48,11 @@ jobs: | |||||||
|           cd outpost/ |           cd outpost/ | ||||||
|           docker build \ |           docker build \ | ||||||
|           --no-cache \ |           --no-cache \ | ||||||
|           -t beryju/authentik-proxy:2021.2.4-stable \ |           -t beryju/authentik-proxy:2021.2.6-stable \ | ||||||
|           -t beryju/authentik-proxy:latest \ |           -t beryju/authentik-proxy:latest \ | ||||||
|           -f proxy.Dockerfile . |           -f proxy.Dockerfile . | ||||||
|       - name: Push Docker Container to Registry (versioned) |       - name: Push Docker Container to Registry (versioned) | ||||||
|         run: docker push beryju/authentik-proxy:2021.2.4-stable |         run: docker push beryju/authentik-proxy:2021.2.6-stable | ||||||
|       - name: Push Docker Container to Registry (latest) |       - name: Push Docker Container to Registry (latest) | ||||||
|         run: docker push beryju/authentik-proxy:latest |         run: docker push beryju/authentik-proxy:latest | ||||||
|   build-static: |   build-static: | ||||||
| @ -69,11 +69,11 @@ jobs: | |||||||
|           cd web/ |           cd web/ | ||||||
|           docker build \ |           docker build \ | ||||||
|           --no-cache \ |           --no-cache \ | ||||||
|           -t beryju/authentik-static:2021.2.4-stable \ |           -t beryju/authentik-static:2021.2.6-stable \ | ||||||
|           -t beryju/authentik-static:latest \ |           -t beryju/authentik-static:latest \ | ||||||
|           -f Dockerfile . |           -f Dockerfile . | ||||||
|       - name: Push Docker Container to Registry (versioned) |       - name: Push Docker Container to Registry (versioned) | ||||||
|         run: docker push beryju/authentik-static:2021.2.4-stable |         run: docker push beryju/authentik-static:2021.2.6-stable | ||||||
|       - name: Push Docker Container to Registry (latest) |       - name: Push Docker Container to Registry (latest) | ||||||
|         run: docker push beryju/authentik-static:latest |         run: docker push beryju/authentik-static:latest | ||||||
|   test-release: |   test-release: | ||||||
| @ -107,5 +107,5 @@ jobs: | |||||||
|           SENTRY_PROJECT: authentik |           SENTRY_PROJECT: authentik | ||||||
|           SENTRY_URL: https://sentry.beryju.org |           SENTRY_URL: https://sentry.beryju.org | ||||||
|         with: |         with: | ||||||
|           tagName: 2021.2.4-stable |           tagName: 2021.2.6-stable | ||||||
|           environment: beryjuorg-prod |           environment: beryjuorg-prod | ||||||
|  | |||||||
| @ -1,2 +1,2 @@ | |||||||
| """authentik""" | """authentik""" | ||||||
| __version__ = "2021.2.4-stable" | __version__ = "2021.2.6-stable" | ||||||
|  | |||||||
| @ -41,6 +41,9 @@ | |||||||
|                             {% endfor %} |                             {% endfor %} | ||||||
|                         </ul> |                         </ul> | ||||||
|                     </ak-dropdown> |                     </ak-dropdown> | ||||||
|  |                     <button role="ak-refresh" class="pf-c-button pf-m-primary"> | ||||||
|  |                         {% trans 'Refresh' %} | ||||||
|  |                     </button> | ||||||
|                 </div> |                 </div> | ||||||
|                 {% include 'partials/pagination.html' %} |                 {% include 'partials/pagination.html' %} | ||||||
|             </div> |             </div> | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ class PolicyCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView): | |||||||
|     """View to clear Policy cache""" |     """View to clear Policy cache""" | ||||||
|  |  | ||||||
|     form_class = PolicyCacheClearForm |     form_class = PolicyCacheClearForm | ||||||
|  |     success_url = "/" | ||||||
|     template_name = "generic/form_non_model.html" |     template_name = "generic/form_non_model.html" | ||||||
|     success_message = _("Successfully cleared Policy cache") |     success_message = _("Successfully cleared Policy cache") | ||||||
|  |  | ||||||
| @ -36,7 +36,7 @@ class FlowCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView): | |||||||
|     """View to clear Flow cache""" |     """View to clear Flow cache""" | ||||||
|  |  | ||||||
|     form_class = FlowCacheClearForm |     form_class = FlowCacheClearForm | ||||||
|  |     success_url = "/" | ||||||
|     template_name = "generic/form_non_model.html" |     template_name = "generic/form_non_model.html" | ||||||
|     success_message = _("Successfully cleared Flow cache") |     success_message = _("Successfully cleared Flow cache") | ||||||
|  |  | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ from django.http import HttpRequest, HttpResponse | |||||||
| SESSION_IMPERSONATE_USER = "authentik_impersonate_user" | SESSION_IMPERSONATE_USER = "authentik_impersonate_user" | ||||||
| SESSION_IMPERSONATE_ORIGINAL_USER = "authentik_impersonate_original_user" | SESSION_IMPERSONATE_ORIGINAL_USER = "authentik_impersonate_original_user" | ||||||
| LOCAL = local() | LOCAL = local() | ||||||
|  | RESPONSE_HEADER_ID = "X-authentik-id" | ||||||
|  |  | ||||||
|  |  | ||||||
| class ImpersonateMiddleware: | class ImpersonateMiddleware: | ||||||
| @ -43,7 +44,7 @@ class RequestIDMiddleware: | |||||||
|             setattr(request, "request_id", request_id) |             setattr(request, "request_id", request_id) | ||||||
|             LOCAL.authentik = {"request_id": request_id} |             LOCAL.authentik = {"request_id": request_id} | ||||||
|         response = self.get_response(request) |         response = self.get_response(request) | ||||||
|         response["X-authentik-id"] = request.request_id |         response[RESPONSE_HEADER_ID] = request.request_id | ||||||
|         del LOCAL.authentik["request_id"] |         del LOCAL.authentik["request_id"] | ||||||
|         return response |         return response | ||||||
|  |  | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| from guardian.shortcuts import get_anonymous_user | from guardian.shortcuts import get_anonymous_user | ||||||
| from structlog import get_logger | from structlog import get_logger | ||||||
|  |  | ||||||
|  | from authentik.core.models import User | ||||||
| from authentik.events.models import ( | from authentik.events.models import ( | ||||||
|     Event, |     Event, | ||||||
|     Notification, |     Notification, | ||||||
| @ -29,7 +30,11 @@ def event_notification_handler(event_uuid: str): | |||||||
| @CELERY_APP.task() | @CELERY_APP.task() | ||||||
| def event_trigger_handler(event_uuid: str, trigger_name: str): | def event_trigger_handler(event_uuid: str, trigger_name: str): | ||||||
|     """Check if policies attached to NotificationRule match event""" |     """Check if policies attached to NotificationRule match event""" | ||||||
|     event: Event = Event.objects.get(event_uuid=event_uuid) |     events = Event.objects.filter(event_uuid=event_uuid) | ||||||
|  |     if not events.exists(): | ||||||
|  |         LOGGER.warning("event doesn't exist yet or anymore", event_uuid=event_uuid) | ||||||
|  |         return | ||||||
|  |     event: Event = events.first() | ||||||
|     trigger: NotificationRule = NotificationRule.objects.get(name=trigger_name) |     trigger: NotificationRule = NotificationRule.objects.get(name=trigger_name) | ||||||
|  |  | ||||||
|     if "policy_uuid" in event.context: |     if "policy_uuid" in event.context: | ||||||
| @ -53,7 +58,8 @@ def event_trigger_handler(event_uuid: str, trigger_name: str): | |||||||
|         return |         return | ||||||
|  |  | ||||||
|     LOGGER.debug("e(trigger): checking if trigger applies", trigger=trigger) |     LOGGER.debug("e(trigger): checking if trigger applies", trigger=trigger) | ||||||
|     policy_engine = PolicyEngine(trigger, get_anonymous_user()) |     user = User.objects.filter(pk=event.user.get("pk")).first() or get_anonymous_user() | ||||||
|  |     policy_engine = PolicyEngine(trigger, user) | ||||||
|     policy_engine.mode = PolicyEngineMode.MODE_OR |     policy_engine.mode = PolicyEngineMode.MODE_OR | ||||||
|     policy_engine.empty_result = False |     policy_engine.empty_result = False | ||||||
|     policy_engine.use_cache = False |     policy_engine.use_cache = False | ||||||
|  | |||||||
| @ -24,6 +24,6 @@ def pbflow_tester(file_name: str) -> Callable: | |||||||
|     return tester |     return tester | ||||||
|  |  | ||||||
|  |  | ||||||
| for flow_file in glob("website/static/flows/*.pbflow"): | for flow_file in glob("website/static/flows/*.akflow"): | ||||||
|     method_name = Path(flow_file).stem.replace("-", "_").replace(".", "_") |     method_name = Path(flow_file).stem.replace("-", "_").replace(".", "_") | ||||||
|     setattr(TestTransferDocs, f"test_flow_{method_name}", pbflow_tester(flow_file)) |     setattr(TestTransferDocs, f"test_flow_{method_name}", pbflow_tester(flow_file)) | ||||||
|  | |||||||
| @ -152,7 +152,13 @@ class FlowImporter: | |||||||
|         entries = deepcopy(self.__import.entries) |         entries = deepcopy(self.__import.entries) | ||||||
|         for entry in entries: |         for entry in entries: | ||||||
|             model_app_label, model_name = entry.model.split(".") |             model_app_label, model_name = entry.model.split(".") | ||||||
|  |             try: | ||||||
|                 model: SerializerModel = apps.get_model(model_app_label, model_name) |                 model: SerializerModel = apps.get_model(model_app_label, model_name) | ||||||
|  |             except LookupError: | ||||||
|  |                 self.logger.error( | ||||||
|  |                     "app or model does not exist", app=model_app_label, model=model_name | ||||||
|  |                 ) | ||||||
|  |                 return False | ||||||
|             # Validate each single entry |             # Validate each single entry | ||||||
|             try: |             try: | ||||||
|                 serializer = self._validate_single(entry) |                 serializer = self._validate_single(entry) | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
|  |  | ||||||
| from django import forms | from django import forms | ||||||
|  |  | ||||||
|  | from authentik.core.models import Group | ||||||
| from authentik.policies.forms import GENERAL_FIELDS | from authentik.policies.forms import GENERAL_FIELDS | ||||||
| from authentik.policies.group_membership.models import GroupMembershipPolicy | from authentik.policies.group_membership.models import GroupMembershipPolicy | ||||||
|  |  | ||||||
| @ -9,6 +10,8 @@ from authentik.policies.group_membership.models import GroupMembershipPolicy | |||||||
| class GroupMembershipPolicyForm(forms.ModelForm): | class GroupMembershipPolicyForm(forms.ModelForm): | ||||||
|     """GroupMembershipPolicy Form""" |     """GroupMembershipPolicy Form""" | ||||||
|  |  | ||||||
|  |     group = forms.ModelChoiceField(queryset=Group.objects.all().order_by("name")) | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|  |  | ||||||
|         model = GroupMembershipPolicy |         model = GroupMembershipPolicy | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ import binascii | |||||||
| import json | import json | ||||||
| import time | import time | ||||||
| from dataclasses import asdict, dataclass, field | from dataclasses import asdict, dataclass, field | ||||||
|  | from datetime import datetime | ||||||
| from hashlib import sha256 | from hashlib import sha256 | ||||||
| from typing import Any, Dict, List, Optional, Type | from typing import Any, Dict, List, Optional, Type | ||||||
| from urllib.parse import urlparse | from urllib.parse import urlparse | ||||||
| @ -480,10 +481,14 @@ class RefreshToken(ExpiringModel, BaseGrantModel): | |||||||
|             now + timedelta_from_string(self.provider.token_validity).seconds |             now + timedelta_from_string(self.provider.token_validity).seconds | ||||||
|         ) |         ) | ||||||
|         # We use the timestamp of the user's last successful login (EventAction.LOGIN) for auth_time |         # We use the timestamp of the user's last successful login (EventAction.LOGIN) for auth_time | ||||||
|         auth_event = Event.objects.filter( |         auth_events = Event.objects.filter( | ||||||
|             action=EventAction.LOGIN, user=get_user(user) |             action=EventAction.LOGIN, user=get_user(user) | ||||||
|         ).latest("created") |         ).order_by("-created") | ||||||
|         auth_time = int(dateformat.format(auth_event.created, "U")) |         # Fallback in case we can't find any login events | ||||||
|  |         auth_time = datetime.now() | ||||||
|  |         if auth_events.exists(): | ||||||
|  |             auth_time = auth_events.first().created | ||||||
|  |         auth_time = int(dateformat.format(auth_time, "U")) | ||||||
|  |  | ||||||
|         token = IDToken( |         token = IDToken( | ||||||
|             iss=self.provider.get_issuer(request), |             iss=self.provider.get_issuer(request), | ||||||
|  | |||||||
| @ -18,6 +18,8 @@ from django.core.asgi import get_asgi_application | |||||||
| from sentry_sdk.integrations.asgi import SentryAsgiMiddleware | from sentry_sdk.integrations.asgi import SentryAsgiMiddleware | ||||||
| from structlog.stdlib import get_logger | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
|  | from authentik.core.middleware import RESPONSE_HEADER_ID | ||||||
|  |  | ||||||
| # DJANGO_SETTINGS_MODULE is set in gunicorn.conf.py | # DJANGO_SETTINGS_MODULE is set in gunicorn.conf.py | ||||||
|  |  | ||||||
| defuse_stdlib() | defuse_stdlib() | ||||||
| @ -67,6 +69,7 @@ class ASGILogger: | |||||||
|     status_code: int |     status_code: int | ||||||
|     start: float |     start: float | ||||||
|     content_length: int |     content_length: int | ||||||
|  |     request_id: str | ||||||
|  |  | ||||||
|     def __init__(self, app: ASGIApp): |     def __init__(self, app: ASGIApp): | ||||||
|         self.app = app |         self.app = app | ||||||
| @ -75,23 +78,29 @@ class ASGILogger: | |||||||
|         self.scope = scope |         self.scope = scope | ||||||
|         self.content_length = 0 |         self.content_length = 0 | ||||||
|         self.headers = dict(scope.get("headers", [])) |         self.headers = dict(scope.get("headers", [])) | ||||||
|  |         self.request_id = "" | ||||||
|  |  | ||||||
|         async def send_hooked(message: Message) -> None: |         async def send_hooked(message: Message) -> None: | ||||||
|             """Hooked send method, which records status code and content-length, and for the final |             """Hooked send method, which records status code and content-length, and for the final | ||||||
|             requests logs it""" |             requests logs it""" | ||||||
|             headers = dict(message.get("headers", [])) |             headers = dict(message.get("headers", [])) | ||||||
|  |  | ||||||
|             if "status" in message: |             if "status" in message: | ||||||
|                 self.status_code = message["status"] |                 self.status_code = message["status"] | ||||||
|  |  | ||||||
|             if b"Content-Length" in headers: |             if b"Content-Length" in headers: | ||||||
|                 self.content_length += int(headers.get(b"Content-Length", b"0")) |                 self.content_length += int(headers.get(b"Content-Length", b"0")) | ||||||
|  |  | ||||||
|  |             if message["type"] == "http.response.start": | ||||||
|  |                 response_headers = dict(message["headers"]) | ||||||
|  |                 self.request_id = response_headers.get( | ||||||
|  |                     RESPONSE_HEADER_ID.encode(), b"" | ||||||
|  |                 ).decode() | ||||||
|  |  | ||||||
|             if message["type"] == "http.response.body" and not message.get( |             if message["type"] == "http.response.body" and not message.get( | ||||||
|                 "more_body", None |                 "more_body", True | ||||||
|             ): |             ): | ||||||
|                 runtime = int((time() - self.start) * 1000) |                 runtime = int((time() - self.start) * 1000) | ||||||
|                 self.log(runtime) |                 self.log(runtime, request_id=self.request_id) | ||||||
|             await send(message) |             await send(message) | ||||||
|  |  | ||||||
|         self.start = time() |         self.start = time() | ||||||
| @ -111,7 +120,7 @@ class ASGILogger: | |||||||
|         # Check if header has multiple values, and use the first one |         # Check if header has multiple values, and use the first one | ||||||
|         return client_ip.split(", ")[0] |         return client_ip.split(", ")[0] | ||||||
|  |  | ||||||
|     def log(self, runtime: float): |     def log(self, runtime: float, **kwargs): | ||||||
|         """Outpot access logs in a structured format""" |         """Outpot access logs in a structured format""" | ||||||
|         host = self._get_ip() |         host = self._get_ip() | ||||||
|         query_string = "" |         query_string = "" | ||||||
| @ -125,6 +134,7 @@ class ASGILogger: | |||||||
|             status=self.status_code, |             status=self.status_code, | ||||||
|             size=self.content_length / 1000 if self.content_length > 0 else 0, |             size=self.content_length / 1000 if self.content_length > 0 else 0, | ||||||
|             runtime=runtime, |             runtime=runtime, | ||||||
|  |             **kwargs, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| """Source API Views""" | """Source API Views""" | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
|  | from time import time | ||||||
|  |  | ||||||
| from django.core.cache import cache | from django.core.cache import cache | ||||||
| from django.db.models.base import Model | from django.db.models.base import Model | ||||||
| @ -68,7 +69,7 @@ class LDAPSourceViewSet(ModelViewSet): | |||||||
|     def sync_status(self, request: Request, slug: str) -> Response: |     def sync_status(self, request: Request, slug: str) -> Response: | ||||||
|         """Get source's sync status""" |         """Get source's sync status""" | ||||||
|         source = self.get_object() |         source = self.get_object() | ||||||
|         last_sync = cache.get(source.state_cache_prefix("last_sync"), None) |         last_sync = cache.get(source.state_cache_prefix("last_sync"), time()) | ||||||
|         return Response( |         return Response( | ||||||
|             LDAPSourceSyncStatusSerializer( |             LDAPSourceSyncStatusSerializer( | ||||||
|                 {"last_sync": datetime.fromtimestamp(last_sync)} |                 {"last_sync": datetime.fromtimestamp(last_sync)} | ||||||
|  | |||||||
| @ -77,7 +77,8 @@ class LDAPPasswordChanger: | |||||||
|         """Change user's password""" |         """Change user's password""" | ||||||
|         user_dn = user.attributes.get(LDAP_DISTINGUISHED_NAME, None) |         user_dn = user.attributes.get(LDAP_DISTINGUISHED_NAME, None) | ||||||
|         if not user_dn: |         if not user_dn: | ||||||
|             raise AttributeError(f"User has no {LDAP_DISTINGUISHED_NAME} set.") |             LOGGER.info(f"User has no {LDAP_DISTINGUISHED_NAME} set.") | ||||||
|  |             return | ||||||
|         self._source.connection.extend.microsoft.modify_password(user_dn, password) |         self._source.connection.extend.microsoft.modify_password(user_dn, password) | ||||||
|  |  | ||||||
|     def _ad_check_password_existing(self, password: str, user_dn: str) -> bool: |     def _ad_check_password_existing(self, password: str, user_dn: str) -> bool: | ||||||
|  | |||||||
| @ -9,13 +9,13 @@ | |||||||
|     <div class="pf-c-card__body"> |     <div class="pf-c-card__body"> | ||||||
|         {% if connections.exists %} |         {% if connections.exists %} | ||||||
|         <p>{% trans 'Connected.' %}</p> |         <p>{% trans 'Connected.' %}</p> | ||||||
|         <a class="pf-c-button pf-m-danger" |         <a class="pf-c-button pf-m-danger ak-root-link" | ||||||
|             href="{% url 'authentik_sources_oauth:oauth-client-disconnect' source_slug=source.slug %}"> |             href="{% url 'authentik_sources_oauth:oauth-client-disconnect' source_slug=source.slug %}"> | ||||||
|             {% trans 'Disconnect' %} |             {% trans 'Disconnect' %} | ||||||
|         </a> |         </a> | ||||||
|         {% else %} |         {% else %} | ||||||
|         <p>Not connected.</p> |         <p>Not connected.</p> | ||||||
|         <a class="pf-c-button pf-m-primary" |         <a class="pf-c-button pf-m-primary ak-root-link" | ||||||
|             href="{% url 'authentik_sources_oauth:oauth-client-login' source_slug=source.slug %}"> |             href="{% url 'authentik_sources_oauth:oauth-client-login' source_slug=source.slug %}"> | ||||||
|             {% trans 'Connect' %} |             {% trans 'Connect' %} | ||||||
|         </a> |         </a> | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ services: | |||||||
|     networks: |     networks: | ||||||
|       - internal |       - internal | ||||||
|   server: |   server: | ||||||
|     image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.4-stable} |     image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.6-stable} | ||||||
|     command: server |     command: server | ||||||
|     environment: |     environment: | ||||||
|       AUTHENTIK_REDIS__HOST: redis |       AUTHENTIK_REDIS__HOST: redis | ||||||
| @ -45,7 +45,7 @@ services: | |||||||
|     env_file: |     env_file: | ||||||
|       - .env |       - .env | ||||||
|   worker: |   worker: | ||||||
|     image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.4-stable} |     image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.6-stable} | ||||||
|     command: worker |     command: worker | ||||||
|     networks: |     networks: | ||||||
|       - internal |       - internal | ||||||
| @ -62,7 +62,7 @@ services: | |||||||
|     env_file: |     env_file: | ||||||
|       - .env |       - .env | ||||||
|   static: |   static: | ||||||
|     image: beryju/authentik-static:${AUTHENTIK_TAG:-2021.2.4-stable} |     image: beryju/authentik-static:${AUTHENTIK_TAG:-2021.2.6-stable} | ||||||
|     networks: |     networks: | ||||||
|       - internal |       - internal | ||||||
|     labels: |     labels: | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ name: authentik | |||||||
| home: https://goauthentik.io | home: https://goauthentik.io | ||||||
| sources: | sources: | ||||||
|   - https://github.com/BeryJu/authentik |   - https://github.com/BeryJu/authentik | ||||||
| version: "2021.2.4-stable" | version: "2021.2.6-stable" | ||||||
| icon: https://raw.githubusercontent.com/BeryJu/authentik/master/web/icons/icon.svg | icon: https://raw.githubusercontent.com/BeryJu/authentik/master/web/icons/icon.svg | ||||||
| dependencies: | dependencies: | ||||||
|   - name: postgresql |   - name: postgresql | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ | |||||||
| |-----------------------------------|-------------------------|-------------| | |-----------------------------------|-------------------------|-------------| | ||||||
| | image.name                        | beryju/authentik        | Image used to run the authentik server and worker | | | image.name                        | beryju/authentik        | Image used to run the authentik server and worker | | ||||||
| | image.name_static                 | beryju/authentik-static | Image used to run the authentik static server (CSS and JS Files) | | | image.name_static                 | beryju/authentik-static | Image used to run the authentik static server (CSS and JS Files) | | ||||||
| | image.tag                         | 2021.2.4-stable           | Image tag | | | image.tag                         | 2021.2.6-stable           | Image tag | | ||||||
| | image.pullPolicy                  | IfNotPresent            | Image Pull Policy used for all deployments | | | image.pullPolicy                  | IfNotPresent            | Image Pull Policy used for all deployments | | ||||||
| | serverReplicas                    | 1                       | Replicas for the Server deployment | | | serverReplicas                    | 1                       | Replicas for the Server deployment | | ||||||
| | workerReplicas                    | 1                       | Replicas for the Worker deployment | | | workerReplicas                    | 1                       | Replicas for the Worker deployment | | ||||||
|  | |||||||
| @ -99,10 +99,12 @@ spec: | |||||||
|             httpGet: |             httpGet: | ||||||
|               path: /-/health/live/ |               path: /-/health/live/ | ||||||
|               port: http |               port: http | ||||||
|  |               initialDelaySeconds: 15 | ||||||
|           readinessProbe: |           readinessProbe: | ||||||
|             httpGet: |             httpGet: | ||||||
|               path: /-/health/ready/ |               path: /-/health/ready/ | ||||||
|               port: http |               port: http | ||||||
|  |               initialDelaySeconds: 15 | ||||||
|           resources: |           resources: | ||||||
|             requests: |             requests: | ||||||
|               cpu: 100m |               cpu: 100m | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ image: | |||||||
|   name: beryju/authentik |   name: beryju/authentik | ||||||
|   name_static: beryju/authentik-static |   name_static: beryju/authentik-static | ||||||
|   name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended |   name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended | ||||||
|   tag: 2021.2.4-stable |   tag: 2021.2.6-stable | ||||||
|   pullPolicy: IfNotPresent |   pullPolicy: IfNotPresent | ||||||
|  |  | ||||||
| serverReplicas: 1 | serverReplicas: 1 | ||||||
|  | |||||||
| @ -49,12 +49,14 @@ func NewAPIController(pbURL url.URL, token string) *APIController { | |||||||
| 	// create the API client, with the transport | 	// create the API client, with the transport | ||||||
| 	apiClient := client.New(transport, strfmt.Default) | 	apiClient := client.New(transport, strfmt.Default) | ||||||
|  |  | ||||||
|  | 	log := log.WithField("logger", "authentik.outpost.ak-api-controller") | ||||||
|  |  | ||||||
| 	// Because we don't know the outpost UUID, we simply do a list and pick the first | 	// Because we don't know the outpost UUID, we simply do a list and pick the first | ||||||
| 	// The service account this token belongs to should only have access to a single outpost | 	// The service account this token belongs to should only have access to a single outpost | ||||||
| 	outposts, err := apiClient.Outposts.OutpostsOutpostsList(outposts.NewOutpostsOutpostsListParams(), auth) | 	outposts, err := apiClient.Outposts.OutpostsOutpostsList(outposts.NewOutpostsOutpostsListParams(), auth) | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		panic(err) | 		log.WithError(err).Panic("Failed to fetch configuration") | ||||||
| 	} | 	} | ||||||
| 	outpost := outposts.Payload.Results[0] | 	outpost := outposts.Payload.Results[0] | ||||||
| 	doGlobalSetup(outpost.Config.(map[string]interface{})) | 	doGlobalSetup(outpost.Config.(map[string]interface{})) | ||||||
| @ -64,7 +66,7 @@ func NewAPIController(pbURL url.URL, token string) *APIController { | |||||||
| 		Auth:   auth, | 		Auth:   auth, | ||||||
| 		token:  token, | 		token:  token, | ||||||
|  |  | ||||||
| 		logger: log.WithField("component", "ak-api-controller"), | 		logger: log, | ||||||
|  |  | ||||||
| 		reloadOffset: time.Duration(rand.Intn(10)) * time.Second, | 		reloadOffset: time.Duration(rand.Intn(10)) * time.Second, | ||||||
|  |  | ||||||
|  | |||||||
| @ -40,7 +40,7 @@ func (ac *APIController) initWS(pbURL url.URL, outpostUUID strfmt.UUID) { | |||||||
| 	} | 	} | ||||||
| 	ws.Dial(fmt.Sprintf(pathTemplate, scheme, pbURL.Host, outpostUUID.String()), header) | 	ws.Dial(fmt.Sprintf(pathTemplate, scheme, pbURL.Host, outpostUUID.String()), header) | ||||||
|  |  | ||||||
| 	ac.logger.WithField("component", "ak-ws").WithField("outpost", outpostUUID.String()).Debug("connecting to authentik") | 	ac.logger.WithField("logger", "authentik.outpost.ak-ws").WithField("outpost", outpostUUID.String()).Debug("connecting to authentik") | ||||||
|  |  | ||||||
| 	ac.wsConn = ws | 	ac.wsConn = ws | ||||||
| 	// Send hello message with our version | 	// Send hello message with our version | ||||||
| @ -52,7 +52,7 @@ func (ac *APIController) initWS(pbURL url.URL, outpostUUID strfmt.UUID) { | |||||||
| 	} | 	} | ||||||
| 	err := ws.WriteJSON(msg) | 	err := ws.WriteJSON(msg) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ac.logger.WithField("component", "ak-ws").WithError(err).Warning("Failed to hello to authentik") | 		ac.logger.WithField("logger", "authentik.outpost.ak-ws").WithError(err).Warning("Failed to hello to authentik") | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -13,7 +13,12 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| func doGlobalSetup(config map[string]interface{}) { | func doGlobalSetup(config map[string]interface{}) { | ||||||
| 	log.SetFormatter(&log.JSONFormatter{}) | 	log.SetFormatter(&log.JSONFormatter{ | ||||||
|  | 		FieldMap: log.FieldMap{ | ||||||
|  | 			log.FieldKeyMsg:  "event", | ||||||
|  | 			log.FieldKeyTime: "timestamp", | ||||||
|  | 		}, | ||||||
|  | 	}) | ||||||
| 	switch config[ConfigLogLevel].(string) { | 	switch config[ConfigLogLevel].(string) { | ||||||
| 	case "debug": | 	case "debug": | ||||||
| 		log.SetLevel(log.DebugLevel) | 		log.SetLevel(log.DebugLevel) | ||||||
|  | |||||||
| @ -31,7 +31,7 @@ func (s *Server) bundleProviders(providers []*models.ProxyOutpostConfig) []*prov | |||||||
| 		bundles[idx] = &providerBundle{ | 		bundles[idx] = &providerBundle{ | ||||||
| 			s:    s, | 			s:    s, | ||||||
| 			Host: externalHost.Host, | 			Host: externalHost.Host, | ||||||
| 			log:  log.WithField("component", "proxy-bundle").WithField("provider", provider.Name), | 			log:  log.WithField("logger", "authentik.outpost.proxy-bundle").WithField("provider", provider.Name), | ||||||
| 		} | 		} | ||||||
| 		bundles[idx].Build(provider) | 		bundles[idx].Build(provider) | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -129,7 +129,7 @@ func (pb *providerBundle) Build(provider *models.ProxyOutpostConfig) { | |||||||
| 		log.Printf("%s", err) | 		log.Printf("%s", err) | ||||||
| 		os.Exit(1) | 		os.Exit(1) | ||||||
| 	} | 	} | ||||||
| 	oauthproxy, err := NewOAuthProxy(opts) | 	oauthproxy, err := NewOAuthProxy(opts, provider) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Errorf("ERROR: Failed to initialise OAuth2 Proxy: %v", err) | 		log.Errorf("ERROR: Failed to initialise OAuth2 Proxy: %v", err) | ||||||
| 		os.Exit(1) | 		os.Exit(1) | ||||||
|  | |||||||
| @ -95,7 +95,7 @@ type loggingHandler struct { | |||||||
| func LoggingHandler(h http.Handler) http.Handler { | func LoggingHandler(h http.Handler) http.Handler { | ||||||
| 	return loggingHandler{ | 	return loggingHandler{ | ||||||
| 		handler: h, | 		handler: h, | ||||||
| 		logger:  log.WithField("component", "proxy-http-server"), | 		logger:  log.WithField("logger", "authentik.outpost.proxy-http-server"), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -104,19 +104,17 @@ func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { | |||||||
| 	url := *req.URL | 	url := *req.URL | ||||||
| 	responseLogger := &responseLogger{w: w} | 	responseLogger := &responseLogger{w: w} | ||||||
| 	h.handler.ServeHTTP(responseLogger, req) | 	h.handler.ServeHTTP(responseLogger, req) | ||||||
| 	duration := float64(time.Since(t)) / float64(time.Second) | 	duration := float64(time.Since(t)) / float64(time.Millisecond) | ||||||
| 	h.logger.WithFields(log.Fields{ | 	h.logger.WithFields(log.Fields{ | ||||||
| 		"Client":          req.RemoteAddr, | 		"host":              req.RemoteAddr, | ||||||
| 		"Host":            req.Host, | 		"vhost":             req.Host, | ||||||
| 		"Protocol":        req.Proto, | 		"request_protocol":  req.Proto, | ||||||
| 		"RequestDuration": fmt.Sprintf("%0.3f", duration), | 		"runtime":           fmt.Sprintf("%0.3f", duration), | ||||||
| 		"RequestMethod":   req.Method, | 		"method":            req.Method, | ||||||
| 		"ResponseSize":    responseLogger.Size(), | 		"size":              responseLogger.Size(), | ||||||
| 		"StatusCode":      responseLogger.Status(), | 		"status":            responseLogger.Status(), | ||||||
| 		"Timestamp":       t, | 		"upstream":          responseLogger.upstream, | ||||||
| 		"Upstream":        responseLogger.upstream, | 		"request_useragent": req.UserAgent(), | ||||||
| 		"UserAgent":       req.UserAgent(), | 		"request_username":  responseLogger.authInfo, | ||||||
| 		"Username":        responseLogger.authInfo, |  | ||||||
| 	}).Info(url.RequestURI()) | 	}).Info(url.RequestURI()) | ||||||
| 	// logger.PrintReq(responseLogger.authInfo, responseLogger.upstream, req, url, t, , ) |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -21,6 +21,7 @@ import ( | |||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/pkg/sessions" | 	"github.com/oauth2-proxy/oauth2-proxy/pkg/sessions" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/pkg/upstream" | 	"github.com/oauth2-proxy/oauth2-proxy/pkg/upstream" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/providers" | 	"github.com/oauth2-proxy/oauth2-proxy/providers" | ||||||
|  | 	"goauthentik.io/outpost/pkg/models" | ||||||
|  |  | ||||||
| 	log "github.com/sirupsen/logrus" | 	log "github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
| @ -92,8 +93,8 @@ type OAuthProxy struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| // NewOAuthProxy creates a new instance of OAuthProxy from the options provided | // NewOAuthProxy creates a new instance of OAuthProxy from the options provided | ||||||
| func NewOAuthProxy(opts *options.Options) (*OAuthProxy, error) { | func NewOAuthProxy(opts *options.Options, provider *models.ProxyOutpostConfig) (*OAuthProxy, error) { | ||||||
| 	logger := log.WithField("component", "proxy").WithField("client-id", opts.ClientID) | 	logger := log.WithField("logger", "authentik.outpost.proxy").WithField("provider", provider.Name) | ||||||
| 	sessionStore, err := sessions.NewSessionStore(&opts.Session, &opts.Cookie) | 	sessionStore, err := sessions.NewSessionStore(&opts.Session, &opts.Cookie) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("error initialising session store: %v", err) | 		return nil, fmt.Errorf("error initialising session store: %v", err) | ||||||
| @ -434,6 +435,7 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req | |||||||
| 		authVal := b64.StdEncoding.EncodeToString([]byte(username + ":" + password)) | 		authVal := b64.StdEncoding.EncodeToString([]byte(username + ":" + password)) | ||||||
| 		req.Header["Authorization"] = []string{fmt.Sprintf("Basic %s", authVal)} | 		req.Header["Authorization"] = []string{fmt.Sprintf("Basic %s", authVal)} | ||||||
| 	} | 	} | ||||||
|  | 	rw.Header().Set("GAP-Auth", session.PreferredUsername) | ||||||
| 	// Check if user has additional headers set that we should sent | 	// Check if user has additional headers set that we should sent | ||||||
| 	if additionalHeaders, ok := userAttributes["additionalHeaders"].(map[string]string); ok { | 	if additionalHeaders, ok := userAttributes["additionalHeaders"].(map[string]string); ok { | ||||||
| 		if additionalHeaders == nil { | 		if additionalHeaders == nil { | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ import ( | |||||||
| 	"errors" | 	"errors" | ||||||
| 	"net" | 	"net" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	log "github.com/sirupsen/logrus" | 	log "github.com/sirupsen/logrus" | ||||||
| @ -30,7 +31,7 @@ func NewServer(ac *ak.APIController) *Server { | |||||||
| 	} | 	} | ||||||
| 	return &Server{ | 	return &Server{ | ||||||
| 		Handlers:    make(map[string]*providerBundle), | 		Handlers:    make(map[string]*providerBundle), | ||||||
| 		logger:      log.WithField("component", "proxy-http-server"), | 		logger:      log.WithField("logger", "authentik.outpost.proxy-http-server"), | ||||||
| 		defaultCert: defaultCert, | 		defaultCert: defaultCert, | ||||||
| 		ak:          ac, | 		ak:          ac, | ||||||
| 	} | 	} | ||||||
| @ -50,12 +51,15 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) { | |||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		s.logger.WithField("host", r.Host).Debug("Host header does not match any we know of") | 		// Get a list of all host keys we know | ||||||
| 		s.logger.Printf("%v+\n", s.Handlers) | 		hostKeys := make([]string, 0, len(s.Handlers)) | ||||||
| 		w.WriteHeader(400) | 		for k := range s.Handlers { | ||||||
|  | 			hostKeys = append(hostKeys, k) | ||||||
|  | 		} | ||||||
|  | 		s.logger.WithField("host", r.Host).WithField("known-hosts", strings.Join(hostKeys, ", ")).Debug("Host header does not match any we know of") | ||||||
|  | 		w.WriteHeader(404) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	s.logger.WithField("host", r.Host).Debug("passing request from host head") |  | ||||||
| 	handler.ServeHTTP(w, r) | 	handler.ServeHTTP(w, r) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,3 +1,3 @@ | |||||||
| package pkg | package pkg | ||||||
|  |  | ||||||
| const VERSION = "2021.2.4-stable" | const VERSION = "2021.2.6-stable" | ||||||
|  | |||||||
							
								
								
									
										39
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										39
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -245,6 +245,24 @@ | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "@sentry/integrations": { | ||||||
|  |             "version": "6.2.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-6.2.0.tgz", | ||||||
|  |             "integrity": "sha512-gvAhP61qs2fog2xCTDs94ZT8cZbWEjFZmOWfT1VXlZDIVopIporj5Qe6IgrLTiCWL61Yko5h5nFnPZ4mpjf+0w==", | ||||||
|  |             "requires": { | ||||||
|  |                 "@sentry/types": "6.2.0", | ||||||
|  |                 "@sentry/utils": "6.2.0", | ||||||
|  |                 "localforage": "^1.8.1", | ||||||
|  |                 "tslib": "^1.9.3" | ||||||
|  |             }, | ||||||
|  |             "dependencies": { | ||||||
|  |                 "tslib": { | ||||||
|  |                     "version": "1.14.1", | ||||||
|  |                     "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", | ||||||
|  |                     "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "@sentry/minimal": { |         "@sentry/minimal": { | ||||||
|             "version": "6.1.0", |             "version": "6.1.0", | ||||||
|             "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.1.0.tgz", |             "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.1.0.tgz", | ||||||
| @ -1756,6 +1774,11 @@ | |||||||
|             "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", |             "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", | ||||||
|             "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" |             "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" | ||||||
|         }, |         }, | ||||||
|  |         "immediate": { | ||||||
|  |             "version": "3.0.6", | ||||||
|  |             "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", | ||||||
|  |             "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" | ||||||
|  |         }, | ||||||
|         "import-fresh": { |         "import-fresh": { | ||||||
|             "version": "3.3.0", |             "version": "3.3.0", | ||||||
|             "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", |             "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", | ||||||
| @ -2010,6 +2033,14 @@ | |||||||
|                 "type-check": "~0.4.0" |                 "type-check": "~0.4.0" | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |         "lie": { | ||||||
|  |             "version": "3.1.1", | ||||||
|  |             "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", | ||||||
|  |             "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", | ||||||
|  |             "requires": { | ||||||
|  |                 "immediate": "~3.0.5" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "lit-analyzer": { |         "lit-analyzer": { | ||||||
|             "version": "1.2.1", |             "version": "1.2.1", | ||||||
|             "resolved": "https://registry.npmjs.org/lit-analyzer/-/lit-analyzer-1.2.1.tgz", |             "resolved": "https://registry.npmjs.org/lit-analyzer/-/lit-analyzer-1.2.1.tgz", | ||||||
| @ -2191,6 +2222,14 @@ | |||||||
|             "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-1.3.0.tgz", |             "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-1.3.0.tgz", | ||||||
|             "integrity": "sha512-0Q1bwmaFH9O14vycPHw8C/IeHMk/uSDldVLIefu/kfbTBGIc44KGH6A8p1bDfxUfHdc8q6Ct7kQklWoHgr4t1Q==" |             "integrity": "sha512-0Q1bwmaFH9O14vycPHw8C/IeHMk/uSDldVLIefu/kfbTBGIc44KGH6A8p1bDfxUfHdc8q6Ct7kQklWoHgr4t1Q==" | ||||||
|         }, |         }, | ||||||
|  |         "localforage": { | ||||||
|  |             "version": "1.9.0", | ||||||
|  |             "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.9.0.tgz", | ||||||
|  |             "integrity": "sha512-rR1oyNrKulpe+VM9cYmcFn6tsHuokyVHFaCM3+osEmxaHTbEk8oQu6eGDfS6DQLWi/N67XRmB8ECG37OES368g==", | ||||||
|  |             "requires": { | ||||||
|  |                 "lie": "3.1.1" | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|         "locate-path": { |         "locate-path": { | ||||||
|             "version": "5.0.0", |             "version": "5.0.0", | ||||||
|             "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", |             "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", | ||||||
|  | |||||||
| @ -14,7 +14,8 @@ | |||||||
|         "@patternfly/patternfly": "^4.80.3", |         "@patternfly/patternfly": "^4.80.3", | ||||||
|         "@sentry/browser": "^6.1.0", |         "@sentry/browser": "^6.1.0", | ||||||
|         "@sentry/tracing": "^6.1.0", |         "@sentry/tracing": "^6.1.0", | ||||||
|         "@types/chart.js": "^2.9.30", |         "@sentry/integrations": "^6.2.0", | ||||||
|  |         "@types/chart.js": "^2.9.31", | ||||||
|         "@types/codemirror": "0.0.108", |         "@types/codemirror": "0.0.108", | ||||||
|         "chart.js": "^2.9.4", |         "chart.js": "^2.9.4", | ||||||
|         "codemirror": "^5.59.2", |         "codemirror": "^5.59.2", | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ import * as Sentry from "@sentry/browser"; | |||||||
| import { Integrations } from "@sentry/tracing"; | import { Integrations } from "@sentry/tracing"; | ||||||
| import { VERSION } from "../constants"; | import { VERSION } from "../constants"; | ||||||
| import { SentryIgnoredError } from "../common/errors"; | import { SentryIgnoredError } from "../common/errors"; | ||||||
|  | import { CaptureConsole as CaptureConsoleIntegration } from "@sentry/integrations"; | ||||||
|  |  | ||||||
| export class Config { | export class Config { | ||||||
|     branding_logo: string; |     branding_logo: string; | ||||||
| @ -22,7 +23,10 @@ export class Config { | |||||||
|                 Sentry.init({ |                 Sentry.init({ | ||||||
|                     dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8", |                     dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8", | ||||||
|                     release: `authentik@${VERSION}`, |                     release: `authentik@${VERSION}`, | ||||||
|                     integrations: [new Integrations.BrowserTracing()], |                     integrations: [ | ||||||
|  |                         new Integrations.BrowserTracing(), | ||||||
|  |                         new CaptureConsoleIntegration(), | ||||||
|  |                     ], | ||||||
|                     tracesSampleRate: 0.6, |                     tracesSampleRate: 0.6, | ||||||
|                     environment: config.error_reporting_environment, |                     environment: config.error_reporting_environment, | ||||||
|                     beforeSend(event: Sentry.Event, hint: Sentry.EventHint) { |                     beforeSend(event: Sentry.Event, hint: Sentry.EventHint) { | ||||||
|  | |||||||
| @ -1,3 +1,4 @@ | |||||||
|  | import { css, CSSResult } from "lit-element"; | ||||||
| // @ts-ignore | // @ts-ignore | ||||||
| import PF from "@patternfly/patternfly/patternfly.css"; | import PF from "@patternfly/patternfly/patternfly.css"; | ||||||
| // @ts-ignore | // @ts-ignore | ||||||
| @ -6,10 +7,31 @@ import PFAddons from "@patternfly/patternfly/patternfly-addons.css"; | |||||||
| import FA from "@fortawesome/fontawesome-free/css/fontawesome.css"; | import FA from "@fortawesome/fontawesome-free/css/fontawesome.css"; | ||||||
| // @ts-ignore | // @ts-ignore | ||||||
| import AKGlobal from "../authentik.css"; | import AKGlobal from "../authentik.css"; | ||||||
| import { CSSResult } from "lit-element"; |  | ||||||
| // @ts-ignore | // @ts-ignore | ||||||
| import CodeMirrorStyle from "codemirror/lib/codemirror.css"; | import CodeMirrorStyle from "codemirror/lib/codemirror.css"; | ||||||
| // @ts-ignore | // @ts-ignore | ||||||
| import CodeMirrorTheme from "codemirror/theme/monokai.css"; | import CodeMirrorTheme from "codemirror/theme/monokai.css"; | ||||||
|  | export const ColorStyles = css` | ||||||
| export const COMMON_STYLES: CSSResult[] = [PF, PFAddons, FA, AKGlobal, CodeMirrorStyle, CodeMirrorTheme]; |     .pf-m-success { | ||||||
|  |         color: var(--pf-global--success-color--100); | ||||||
|  |     } | ||||||
|  |     .pf-c-button.pf-m-success { | ||||||
|  |         color: var(--pf-c-button--m-primary--Color); | ||||||
|  |         background-color: var(--pf-global--success-color--100); | ||||||
|  |     } | ||||||
|  |     .pf-m-warning { | ||||||
|  |         color: var(--pf-global--warning-color--100); | ||||||
|  |     } | ||||||
|  |     .pf-c-button.pf-m-warning { | ||||||
|  |         color: var(--pf-c-button--m-primary--Color); | ||||||
|  |         background-color: var(--pf-global--warning-color--100); | ||||||
|  |     } | ||||||
|  |     .pf-m-danger { | ||||||
|  |         color: var(--pf-global--danger-color--100); | ||||||
|  |     } | ||||||
|  |     .pf-c-button.pf-m-danger { | ||||||
|  |         color: var(--pf-c-button--m-primary--Color); | ||||||
|  |         background-color: var(--pf-global--danger-color--100); | ||||||
|  |     } | ||||||
|  | `; | ||||||
|  | export const COMMON_STYLES: CSSResult[] = [PF, PFAddons, FA, AKGlobal, CodeMirrorStyle, CodeMirrorTheme, ColorStyles]; | ||||||
|  | |||||||
| @ -1,31 +1,6 @@ | |||||||
| import { css } from "lit-element"; |  | ||||||
|  |  | ||||||
| export const PRIMARY_CLASS = "pf-m-primary"; | export const PRIMARY_CLASS = "pf-m-primary"; | ||||||
| export const SUCCESS_CLASS = "pf-m-success"; | export const SUCCESS_CLASS = "pf-m-success"; | ||||||
| export const ERROR_CLASS = "pf-m-danger"; | export const ERROR_CLASS = "pf-m-danger"; | ||||||
| export const PROGRESS_CLASS = "pf-m-in-progress"; | export const PROGRESS_CLASS = "pf-m-in-progress"; | ||||||
| export const CURRENT_CLASS = "pf-m-current"; | export const CURRENT_CLASS = "pf-m-current"; | ||||||
| export const ColorStyles = css` | export const VERSION = "2021.2.6-stable"; | ||||||
|     .pf-m-success { |  | ||||||
|         color: var(--pf-global--success-color--100); |  | ||||||
|     } |  | ||||||
|     .pf-c-button.pf-m-success { |  | ||||||
|         color: var(--pf-c-button--m-primary--Color); |  | ||||||
|         background-color: var(--pf-global--success-color--100); |  | ||||||
|     } |  | ||||||
|     .pf-m-warning { |  | ||||||
|         color: var(--pf-global--warning-color--100); |  | ||||||
|     } |  | ||||||
|     .pf-c-button.pf-m-warning { |  | ||||||
|         color: var(--pf-c-button--m-primary--Color); |  | ||||||
|         background-color: var(--pf-global--warning-color--100); |  | ||||||
|     } |  | ||||||
|     .pf-m-danger { |  | ||||||
|         color: var(--pf-global--danger-color--100); |  | ||||||
|     } |  | ||||||
|     .pf-c-button.pf-m-danger { |  | ||||||
|         color: var(--pf-c-button--m-primary--Color); |  | ||||||
|         background-color: var(--pf-global--danger-color--100); |  | ||||||
|     } |  | ||||||
| `; |  | ||||||
| export const VERSION = "2021.2.4-stable"; |  | ||||||
|  | |||||||
| @ -5,7 +5,8 @@ import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; | |||||||
| import ButtonStyle from "@patternfly/patternfly/components/Button/button.css"; | import ButtonStyle from "@patternfly/patternfly/components/Button/button.css"; | ||||||
| // @ts-ignore | // @ts-ignore | ||||||
| import SpinnerStyle from "@patternfly/patternfly/components/Spinner/spinner.css"; | import SpinnerStyle from "@patternfly/patternfly/components/Spinner/spinner.css"; | ||||||
| import { ColorStyles, PRIMARY_CLASS, PROGRESS_CLASS } from "../../constants"; | import { PRIMARY_CLASS, PROGRESS_CLASS } from "../../constants"; | ||||||
|  | import { ColorStyles } from "../../common/styles"; | ||||||
|  |  | ||||||
| @customElement("ak-spinner-button") | @customElement("ak-spinner-button") | ||||||
| export class SpinnerButton extends LitElement { | export class SpinnerButton extends LitElement { | ||||||
|  | |||||||
| @ -4,7 +4,8 @@ import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; | |||||||
| // @ts-ignore | // @ts-ignore | ||||||
| import ButtonStyle from "@patternfly/patternfly/components/Button/button.css"; | import ButtonStyle from "@patternfly/patternfly/components/Button/button.css"; | ||||||
| import { tokenByIdentifier } from "../../api/Tokens"; | import { tokenByIdentifier } from "../../api/Tokens"; | ||||||
| import { ColorStyles, ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants"; | import { ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants"; | ||||||
|  | import { ColorStyles } from "../../common/styles"; | ||||||
|  |  | ||||||
| @customElement("ak-token-copy-button") | @customElement("ak-token-copy-button") | ||||||
| export class TokenCopyButton extends LitElement { | export class TokenCopyButton extends LitElement { | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ import { gettext } from "django"; | |||||||
| import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element"; | ||||||
| import { ifDefined } from "lit-html/directives/if-defined"; | import { ifDefined } from "lit-html/directives/if-defined"; | ||||||
| import { COMMON_STYLES } from "../../common/styles"; | import { COMMON_STYLES } from "../../common/styles"; | ||||||
| import { ColorStyles } from "../../constants"; |  | ||||||
|  |  | ||||||
| @customElement("ak-aggregate-card") | @customElement("ak-aggregate-card") | ||||||
| export class AggregateCard extends LitElement { | export class AggregateCard extends LitElement { | ||||||
| @ -25,7 +24,7 @@ export class AggregateCard extends LitElement { | |||||||
|                 text-align: center; |                 text-align: center; | ||||||
|                 color: var(--pf-global--Color--100); |                 color: var(--pf-global--Color--100); | ||||||
|             } |             } | ||||||
|         `, ColorStyles]); |         `]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     renderInner(): TemplateResult { |     renderInner(): TemplateResult { | ||||||
|  | |||||||
| @ -3,7 +3,6 @@ import { css, CSSResult, customElement, html, LitElement, property, TemplateResu | |||||||
| import CodeMirrorStyle from "codemirror/lib/codemirror.css"; | import CodeMirrorStyle from "codemirror/lib/codemirror.css"; | ||||||
| // @ts-ignore | // @ts-ignore | ||||||
| import CodeMirrorTheme from "codemirror/theme/monokai.css"; | import CodeMirrorTheme from "codemirror/theme/monokai.css"; | ||||||
| import { ColorStyles } from "../../constants"; |  | ||||||
| import { COMMON_STYLES } from "../../common/styles"; | import { COMMON_STYLES } from "../../common/styles"; | ||||||
| import { Route } from "./Route"; | import { Route } from "./Route"; | ||||||
| import { ROUTES } from "../../routes"; | import { ROUTES } from "../../routes"; | ||||||
| @ -23,7 +22,6 @@ export class RouterOutlet extends LitElement { | |||||||
|         return [ |         return [ | ||||||
|             CodeMirrorStyle, |             CodeMirrorStyle, | ||||||
|             CodeMirrorTheme, |             CodeMirrorTheme, | ||||||
|             ColorStyles, |  | ||||||
|             css` |             css` | ||||||
|                 :host { |                 :host { | ||||||
|                     height: 100vh; |                     height: 100vh; | ||||||
|  | |||||||
| @ -55,7 +55,12 @@ export class LibraryPage extends LitElement { | |||||||
|     apps?: AKResponse<Application>; |     apps?: AKResponse<Application>; | ||||||
|  |  | ||||||
|     static get styles(): CSSResult[] { |     static get styles(): CSSResult[] { | ||||||
|         return COMMON_STYLES; |         return COMMON_STYLES.concat(css` | ||||||
|  |             :host, | ||||||
|  |             main { | ||||||
|  |                 height: 100%; | ||||||
|  |             } | ||||||
|  |         `); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     firstUpdated(): void { |     firstUpdated(): void { | ||||||
|  | |||||||
| @ -89,10 +89,14 @@ export class SiteShell extends LitElement { | |||||||
|             if (a.href === "") { |             if (a.href === "") { | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|  |             if (a.href.startsWith("#")) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|             try { |             try { | ||||||
|                 const url = new URL(a.href); |                 const url = new URL(a.href); | ||||||
|                 const qs = url.search || ""; |                 const qs = url.search || ""; | ||||||
|                 a.href = `#${url.pathname}${qs}`; |                 const hash = (url.hash || "#").substring(2, Infinity); | ||||||
|  |                 a.href = `#${url.pathname}${qs}${hash}`; | ||||||
|             } catch (e) { |             } catch (e) { | ||||||
|                 console.debug(`authentik/site-shell: error ${e}`); |                 console.debug(`authentik/site-shell: error ${e}`); | ||||||
|                 a.href = `#${a.href}`; |                 a.href = `#${a.href}`; | ||||||
|  | |||||||
| @ -52,13 +52,13 @@ export class OutpostListPage extends TablePage<Outpost> { | |||||||
|             })}</ul>`, |             })}</ul>`, | ||||||
|             html`<ak-outpost-health outpostId=${item.pk}></ak-outpost-health>`, |             html`<ak-outpost-health outpostId=${item.pk}></ak-outpost-health>`, | ||||||
|             html` |             html` | ||||||
|             <ak-modal-button href="${Outpost.adminUrl(`${item.pk}/update`)}"> |             <ak-modal-button href="${Outpost.adminUrl(`${item.pk}/update/`)}"> | ||||||
|                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> |                 <ak-spinner-button slot="trigger" class="pf-m-secondary"> | ||||||
|                     ${gettext("Edit")} |                     ${gettext("Edit")} | ||||||
|                 </ak-spinner-button> |                 </ak-spinner-button> | ||||||
|                 <div slot="modal"></div> |                 <div slot="modal"></div> | ||||||
|             </ak-modal-button>  |             </ak-modal-button>  | ||||||
|             <ak-modal-button href="${Outpost.adminUrl(`${item.pk}/delete`)}"> |             <ak-modal-button href="${Outpost.adminUrl(`${item.pk}/delete/`)}"> | ||||||
|                 <ak-spinner-button slot="trigger" class="pf-m-danger"> |                 <ak-spinner-button slot="trigger" class="pf-m-danger"> | ||||||
|                     ${gettext("Delete")} |                     ${gettext("Delete")} | ||||||
|                 </ak-spinner-button> |                 </ak-spinner-button> | ||||||
|  | |||||||
| @ -15,7 +15,7 @@ Download the latest `docker-compose.yml` from [here](https://raw.githubuserconte | |||||||
|  |  | ||||||
| To optionally enable error-reporting, run `echo AUTHENTIK_ERROR_REPORTING__ENABLED=true >> .env` | To optionally enable error-reporting, run `echo AUTHENTIK_ERROR_REPORTING__ENABLED=true >> .env` | ||||||
|  |  | ||||||
| To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.2.4-stable >> .env` | To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.2.6-stable >> .env` | ||||||
|  |  | ||||||
| If this is a fresh authentik install run the following commands to generate a password: | If this is a fresh authentik install run the following commands to generate a password: | ||||||
|  |  | ||||||
|  | |||||||
| @ -24,7 +24,7 @@ image: | |||||||
|     name: beryju/authentik |     name: beryju/authentik | ||||||
|     name_static: beryju/authentik-static |     name_static: beryju/authentik-static | ||||||
|     name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended |     name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended | ||||||
|     tag: 2021.2.4-stable |     tag: 2021.2.6-stable | ||||||
|  |  | ||||||
| serverReplicas: 1 | serverReplicas: 1 | ||||||
| workerReplicas: 1 | workerReplicas: 1 | ||||||
|  | |||||||
							
								
								
									
										14
									
								
								website/docs/releases/next.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								website/docs/releases/next.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | --- | ||||||
|  | title: Next release | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | ## Headline Changes | ||||||
|  |  | ||||||
|  | - Simplify role-based access | ||||||
|  |  | ||||||
|  |     Instead of having to create a Group Membership policy for every group you want to use, you can now select a Group and even a User directly in a binding. | ||||||
|  |  | ||||||
|  |     When a group is selected, the binding behaves the same as if a Group Membership policy exists. | ||||||
|  |  | ||||||
|  |     When a user is selected, the binding checks the user of the request, and denies the request when the user doesn't match. | ||||||
|  |  | ||||||
		Reference in New Issue
	
	Block a user
	