def from_http( self, request: HttpRequest, user: Optional[settings.AUTH_USER_MODEL] = None ) -> "Event": """Add data from a Django-HttpRequest, allowing the creation of Events independently from requests. `user` arguments optionally overrides user from requests.""" if hasattr(request, "user"): original_user = None if hasattr(request, "session"): original_user = request.session.get( SESSION_IMPERSONATE_ORIGINAL_USER, None ) self.user = get_user(request.user, original_user) if user: self.user = get_user(user) # Check if we're currently impersonating, and add that user if hasattr(request, "session"): if SESSION_IMPERSONATE_ORIGINAL_USER in request.session: self.user = get_user(request.session[SESSION_IMPERSONATE_ORIGINAL_USER]) self.user["on_behalf_of"] = get_user( request.session[SESSION_IMPERSONATE_USER] ) # User 255.255.255.255 as fallback if IP cannot be determined self.client_ip = get_client_ip(request) # Apply GeoIP Data, when enabled self.with_geoip() # If there's no app set, we get it from the requests too if not self.app: self.app = Event._get_app_from_request(request) self.save() return self
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: """Start FlowPLanner, return to flow executor shell""" # After we've checked permissions, and the user has access, check if we need # to re-authenticate the user if self.params.max_age: current_age: timedelta = ( timezone.now() - Event.objects.filter( action=EventAction.LOGIN, user=get_user(self.request.user) ) .latest("created") .created ) if current_age.total_seconds() > self.params.max_age: return self.handle_no_permission() # If prompt=login, we need to re-authenticate the user regardless if ( PROMPT_LOGIN in self.params.prompt and SESSION_NEEDS_LOGIN not in self.request.session ): self.request.session[SESSION_NEEDS_LOGIN] = True return self.handle_no_permission() # Regardless, we start the planner and return to it planner = FlowPlanner(self.provider.authorization_flow) # planner.use_cache = False planner.allow_empty_flows = True scope_descriptions = UserInfoView().get_scope_descriptions(self.params.scope) plan: FlowPlan = planner.plan( self.request, { PLAN_CONTEXT_SSO: True, PLAN_CONTEXT_APPLICATION: self.application, # OAuth2 related params PLAN_CONTEXT_PARAMS: self.params, # Consent related params PLAN_CONTEXT_CONSENT_HEADER: _( "You're about to sign into %(application)s." ) % {"application": self.application.name}, PLAN_CONTEXT_CONSENT_PERMISSIONS: scope_descriptions, }, ) # OpenID clients can specify a `prompt` parameter, and if its set to consent we # need to inject a consent stage if PROMPT_CONSNET in self.params.prompt: if not any(isinstance(x, ConsentStageView) for x in plan.stages): # Plan does not have any consent stage, so we add an in-memory one stage = ConsentStage( name="OAuth2 Provider In-memory consent stage", mode=ConsentMode.ALWAYS_REQUIRE, ) plan.append(stage) plan.append(in_memory_stage(OAuthFulfillmentStage)) self.request.session[SESSION_KEY_PLAN] = plan return redirect_with_qs( "authentik_core:if-flow", self.request.GET, flow_slug=self.provider.authorization_flow.slug, )
def from_http(self, request: HttpRequest, user: Optional[settings.AUTH_USER_MODEL] = None) -> "Event": """Add data from a Django-HttpRequest, allowing the creation of Events independently from requests. `user` arguments optionally overrides user from requests.""" if request: self.context["http_request"] = { "path": request.path, "method": request.method, "args": QueryDict(request.META.get("QUERY_STRING", "")), } if hasattr(request, "tenant"): tenant: Tenant = request.tenant # Because self.created only gets set on save, we can't use it's value here # hence we set self.created to now and then use it self.created = now() self.expires = self.created + timedelta_from_string( tenant.event_retention) self.tenant = sanitize_dict(model_to_dict(tenant)) if hasattr(request, "user"): original_user = None if hasattr(request, "session"): original_user = request.session.get( SESSION_IMPERSONATE_ORIGINAL_USER, None) self.user = get_user(request.user, original_user) if user: self.user = get_user(user) # Check if we're currently impersonating, and add that user if hasattr(request, "session"): if SESSION_IMPERSONATE_ORIGINAL_USER in request.session: self.user = get_user( request.session[SESSION_IMPERSONATE_ORIGINAL_USER]) self.user["on_behalf_of"] = get_user( request.session[SESSION_IMPERSONATE_USER]) # User 255.255.255.255 as fallback if IP cannot be determined self.client_ip = get_client_ip(request) # Apply GeoIP Data, when enabled self.with_geoip() # If there's no app set, we get it from the requests too if not self.app: self.app = Event._get_app_from_request(request) self.save() return self
def create_id_token(self, user: User, request: HttpRequest) -> IDToken: """Creates the id_token. See: http://openid.net/specs/openid-connect-core-1_0.html#IDToken""" sub = "" if self.provider.sub_mode == SubModes.HASHED_USER_ID: sub = sha256(f"{user.id}-{settings.SECRET_KEY}".encode( "ascii")).hexdigest() elif self.provider.sub_mode == SubModes.USER_EMAIL: sub = user.email elif self.provider.sub_mode == SubModes.USER_USERNAME: sub = user.username elif self.provider.sub_mode == SubModes.USER_UPN: sub = user.attributes["upn"] else: raise ValueError((f"Provider {self.provider} has invalid sub_mode " f"selected: {self.provider.sub_mode}")) # Convert datetimes into timestamps. now = int(time.time()) iat_time = now exp_time = int( 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 auth_events = Event.objects.filter( action=EventAction.LOGIN, user=get_user(user)).order_by("-created") # 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( iss=self.provider.get_issuer(request), sub=sub, aud=self.provider.client_id, exp=exp_time, iat=iat_time, auth_time=auth_time, ) # Include (or not) user standard claims in the id_token. if self.provider.include_claims_in_id_token: from authentik.providers.oauth2.views.userinfo import UserInfoView user_info = UserInfoView() user_info.request = request claims = user_info.get_claims(self) token.claims = claims return token
def test(self, request: Request, pk=None) -> Response: """Send example notification using selected transport. Requires Modify permissions.""" transport: NotificationTransport = self.get_object() notification = Notification( severity=NotificationSeverity.NOTICE, body=f"Test Notification from transport {transport.name}", user=request.user, event=Event( action="Test", user=get_user(request.user), app=self.__class__.__module__, context={"foo": "bar"}, ), ) try: response = NotificationTransportTestSerializer( data={"messages": transport.send(notification)}) response.is_valid() return Response(response.data) except NotificationTransportError as exc: return Response(str(exc.__cause__ or None), status=500)
def set_user(self, user: User) -> "Event": """Set `.user` based on user, ensuring the correct attributes are copied. This should only be used when self.from_http is *not* used.""" self.user = get_user(user) return self