예제 #1
0
    def dispatch(self, request: HttpRequest, *_, **kwargs) -> HttpResponse:
        """View Get handler"""
        slug = kwargs.get("source_slug", "")
        try:
            self.source = OAuthSource.objects.get(slug=slug)
        except OAuthSource.DoesNotExist:
            raise Http404(f"Unknown OAuth source '{slug}'.")

        if not self.source.enabled:
            raise Http404(f"Source {slug} is not enabled.")
        client = self.get_client(self.source, callback=self.get_callback_url(self.source))
        # Fetch access token
        token = client.get_access_token()
        if token is None:
            return self.handle_login_failure("Could not retrieve token.")
        if "error" in token:
            return self.handle_login_failure(token["error"])
        # Fetch profile info
        try:
            raw_info = client.get_profile_info(token)
            if raw_info is None:
                return self.handle_login_failure("Could not retrieve profile.")
        except JSONDecodeError as exc:
            Event.new(
                EventAction.CONFIGURATION_ERROR,
                message="Failed to JSON-decode profile.",
                raw_profile=exc.doc,
            ).from_http(self.request)
            return self.handle_login_failure("Could not retrieve profile.")
        identifier = self.get_user_id(raw_info)
        if identifier is None:
            return self.handle_login_failure("Could not determine id.")
        # Get or create access record
        enroll_info = self.get_user_enroll_context(raw_info)
        sfm = OAuthSourceFlowManager(
            source=self.source,
            request=self.request,
            identifier=identifier,
            enroll_info=enroll_info,
        )
        sfm.policy_context = {"oauth_userinfo": raw_info}
        return sfm.get_flow(
            access_token=token.get("access_token"),
        )
예제 #2
0
    def validate_stage_templates(self):
        """Ensure all stage's templates actually exist"""
        from authentik.events.models import Event, EventAction
        from authentik.stages.email.models import EmailStage, EmailTemplates

        for stage in EmailStage.objects.all():
            try:
                get_template(stage.template)
            except TemplateDoesNotExist:
                LOGGER.warning("Stage template does not exist, resetting",
                               path=stage.template)
                Event.new(
                    EventAction.CONFIGURATION_ERROR,
                    stage=stage,
                    message=
                    (f"Template {stage.template} does not exist, resetting to default."
                     f" (Stage {stage.name})"),
                ).save()
                stage.template = EmailTemplates.ACCOUNT_CONFIRM
                stage.save()
예제 #3
0
 def get_redirect_url(self, parameters=None):
     "Build authentication redirect url."
     args = self.get_redirect_args()
     additional = parameters or {}
     args.update(additional)
     # Special handling for scope, since it's set as array
     # to make additional scopes easier
     args["scope"] = " ".join(sorted(set(args["scope"])))
     params = urlencode(args, quote_via=quote)
     LOGGER.info("redirect args", **args)
     authorization_url = self.source.type.authorization_url or ""
     if self.source.type.urls_customizable and self.source.authorization_url:
         authorization_url = self.source.authorization_url
     if authorization_url == "":
         Event.new(
             EventAction.CONFIGURATION_ERROR,
             source=self.source,
             message="Source has an empty authorization URL.",
         ).save()
     return f"{authorization_url}?{params}"
예제 #4
0
 def get_challenge(self, *args, **kwargs) -> Challenge:
     user = self.get_pending_user()
     stage: AuthenticatorDuoStage = self.executor.current_stage
     try:
         enroll = stage.client.enroll(user.username)
     except RuntimeError as exc:
         Event.new(
             EventAction.CONFIGURATION_ERROR,
             message=f"Failed to enroll user: {str(exc)}",
             user=user,
         ).from_http(self.request, user)
         raise InvalidStageError(str(exc)) from exc
     user_id = enroll["user_id"]
     self.request.session[SESSION_KEY_DUO_USER_ID] = user_id
     self.request.session[SESSION_KEY_DUO_ACTIVATION_CODE] = enroll[
         "activation_code"]
     return AuthenticatorDuoChallenge(
         data={
             "type": ChallengeTypes.NATIVE.value,
             "activation_barcode": enroll["activation_barcode"],
             "activation_code": enroll["activation_code"],
             "stage_uuid": str(stage.stage_uuid),
         })
예제 #5
0
def update_latest_version(self: MonitoredTask):
    """Update latest version info"""
    if CONFIG.y_bool("disable_update_check"):
        cache.set(VERSION_CACHE_KEY, "0.0.0", VERSION_CACHE_TIMEOUT)
        self.set_status(
            TaskResult(TaskResultStatus.WARNING,
                       messages=["Version check disabled."]))
        return
    try:
        response = get_http_session().get(
            "https://version.goauthentik.io/version.json", )
        response.raise_for_status()
        data = response.json()
        upstream_version = data.get("stable", {}).get("version")
        cache.set(VERSION_CACHE_KEY, upstream_version, VERSION_CACHE_TIMEOUT)
        self.set_status(
            TaskResult(TaskResultStatus.SUCCESSFUL,
                       ["Successfully updated latest Version"]))
        _set_prom_info()
        # Check if upstream version is newer than what we're running,
        # and if no event exists yet, create one.
        if LOCAL_VERSION < parse(upstream_version):
            # Event has already been created, don't create duplicate
            if Event.objects.filter(
                    action=EventAction.UPDATE_AVAILABLE,
                    context__new_version=upstream_version,
            ).exists():
                return
            event_dict = {"new_version": upstream_version}
            if match := re.search(URL_FINDER,
                                  data.get("stable", {}).get("changelog", "")):
                event_dict["message"] = f"Changelog: {match.group()}"
            Event.new(EventAction.UPDATE_AVAILABLE, **event_dict).save()
    except (RequestException, IndexError) as exc:
        cache.set(VERSION_CACHE_KEY, "0.0.0", VERSION_CACHE_TIMEOUT)
        self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
예제 #6
0
 def sync(self) -> int:
     """Iterate over all LDAP Groups and create authentik_core.Group instances"""
     if not self._source.sync_groups:
         self.message("Group syncing is disabled for this Source")
         return -1
     groups = self._source.connection.extend.standard.paged_search(
         search_base=self.base_dn_groups,
         search_filter=self._source.group_object_filter,
         search_scope=ldap3.SUBTREE,
         attributes=[
             ldap3.ALL_ATTRIBUTES, ldap3.ALL_OPERATIONAL_ATTRIBUTES
         ],
     )
     group_count = 0
     for group in groups:
         attributes = group.get("attributes", {})
         group_dn = self._flatten(
             self._flatten(group.get("entryDN", group.get("dn"))))
         if self._source.object_uniqueness_field not in attributes:
             self.message(
                 f"Cannot find uniqueness field in attributes: '{group_dn}'",
                 attributes=attributes.keys(),
                 dn=group_dn,
             )
             continue
         uniq = self._flatten(
             attributes[self._source.object_uniqueness_field])
         try:
             defaults = self.build_group_properties(group_dn, **attributes)
             self._logger.debug("Creating group with attributes",
                                **defaults)
             if "name" not in defaults:
                 raise IntegrityError(
                     "Name was not set by propertymappings")
             # Special check for `users` field, as this is an M2M relation, and cannot be sync'd
             if "users" in defaults:
                 del defaults["users"]
             ak_group, created = self.update_or_create_attributes(
                 Group,
                 {
                     f"attributes__{LDAP_UNIQUENESS}": uniq,
                     "parent": self._source.sync_parent_group,
                 },
                 defaults,
             )
         except (IntegrityError, FieldError, TypeError) as exc:
             Event.new(
                 EventAction.CONFIGURATION_ERROR,
                 message=
                 (f"Failed to create group: {str(exc)} "
                  "To merge new group with existing group, set the groups's "
                  f"Attribute '{LDAP_UNIQUENESS}' to '{uniq}'"),
                 source=self._source,
                 dn=group_dn,
             ).save()
         else:
             self._logger.debug("Synced group",
                                group=ak_group.name,
                                created=created)
             group_count += 1
     return group_count
예제 #7
0
 def view_key(self, request: Request, identifier: str) -> Response:
     """Return token key and log access"""
     token: Token = self.get_object()
     Event.new(EventAction.SECRET_VIEW,
               secret=token).from_http(request)  # noqa # nosec
     return Response(TokenViewSerializer({"key": token.key}).data)