Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enterprise/providers/rac: add alert that enterprise is required for RAC #8057

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions authentik/core/api/propertymappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from authentik.core.api.utils import MetaNameSerializer, PassiveSerializer, TypeCreateSerializer
from authentik.core.expression.evaluator import PropertyMappingEvaluator
from authentik.core.models import PropertyMapping
from authentik.enterprise.apps import EnterpriseConfig
from authentik.events.utils import sanitize_item
from authentik.lib.utils.reflection import all_subclasses
from authentik.policies.api.exec import PolicyTestSerializer
Expand Down Expand Up @@ -95,6 +96,7 @@ def types(self, request: Request) -> Response:
"description": subclass.__doc__,
"component": subclass().component,
"model_name": subclass._meta.model_name,
"requires_enterprise": isinstance(subclass._meta.app_config, EnterpriseConfig),
}
)
return Response(TypeCreateSerializer(data, many=True).data)
Expand Down
2 changes: 2 additions & 0 deletions authentik/core/api/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
from authentik.core.models import Provider
from authentik.enterprise.apps import EnterpriseConfig
from authentik.lib.utils.reflection import all_subclasses


Expand Down Expand Up @@ -113,6 +114,7 @@ def types(self, request: Request) -> Response:
"description": subclass.__doc__,
"component": subclass().component,
"model_name": subclass._meta.model_name,
"requires_enterprise": isinstance(subclass._meta.app_config, EnterpriseConfig),
}
)
data.append(
Expand Down
3 changes: 2 additions & 1 deletion authentik/core/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from drf_spectacular.extensions import OpenApiSerializerFieldExtension
from drf_spectacular.plumbing import build_basic_type
from drf_spectacular.types import OpenApiTypes
from rest_framework.fields import CharField, IntegerField, JSONField
from rest_framework.fields import BooleanField, CharField, IntegerField, JSONField
from rest_framework.serializers import Serializer, SerializerMethodField, ValidationError


Expand Down Expand Up @@ -74,6 +74,7 @@ class TypeCreateSerializer(PassiveSerializer):
description = CharField(required=True)
component = CharField(required=True)
model_name = CharField(required=True)
requires_enterprise = BooleanField(default=False)


class CacheSerializer(PassiveSerializer):
Expand Down
14 changes: 14 additions & 0 deletions authentik/enterprise/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
from datetime import datetime, timedelta

from django.utils.timezone import now
from django.utils.translation import gettext as _
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema, inline_serializer
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.fields import BooleanField, CharField, DateTimeField, IntegerField
from rest_framework.permissions import IsAuthenticated
from rest_framework.request import Request
Expand All @@ -20,6 +22,18 @@
from authentik.root.install_id import get_install_id


class EnterpriseRequiredMixin:
"""Mixin to validate that a valid enterprise license
exists before allowing to safe the object"""

def validate(self, attrs: dict) -> dict:
"""Check that a valid license exists"""
total = LicenseKey.get_total()
if not total.is_valid():
raise ValidationError(_("Enterprise is required to create/update this object."))
return super().validate(attrs)

Check warning on line 34 in authentik/enterprise/api.py

View check run for this annotation

Codecov / codecov/patch

authentik/enterprise/api.py#L31-L34

Added lines #L31 - L34 were not covered by tests


class LicenseSerializer(ModelSerializer):
"""License Serializer"""

Expand Down
6 changes: 5 additions & 1 deletion authentik/enterprise/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
from authentik.blueprints.apps import ManagedAppConfig


class AuthentikEnterpriseConfig(ManagedAppConfig):
class EnterpriseConfig(ManagedAppConfig):
"""Base app config for all enterprise apps"""


class AuthentikEnterpriseConfig(EnterpriseConfig):
"""Enterprise app config"""

name = "authentik.enterprise"
Expand Down
3 changes: 2 additions & 1 deletion authentik/enterprise/providers/rac/api/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from authentik.core.api.used_by import UsedByMixin
from authentik.core.models import Provider
from authentik.enterprise.api import EnterpriseRequiredMixin
from authentik.enterprise.providers.rac.api.providers import RACProviderSerializer
from authentik.enterprise.providers.rac.models import Endpoint
from authentik.policies.engine import PolicyEngine
Expand All @@ -28,7 +29,7 @@ def user_endpoint_cache_key(user_pk: str) -> str:
return f"goauthentik.io/providers/rac/endpoint_access/{user_pk}"


class EndpointSerializer(ModelSerializer):
class EndpointSerializer(EnterpriseRequiredMixin, ModelSerializer):
"""Endpoint Serializer"""

provider_obj = RACProviderSerializer(source="provider", read_only=True)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
from authentik.core.api.propertymappings import PropertyMappingSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import JSONDictField
from authentik.enterprise.api import EnterpriseRequiredMixin
from authentik.enterprise.providers.rac.models import RACPropertyMapping


class RACPropertyMappingSerializer(PropertyMappingSerializer):
class RACPropertyMappingSerializer(EnterpriseRequiredMixin, PropertyMappingSerializer):
"""RACPropertyMapping Serializer"""

static_settings = JSONDictField()
Expand Down
3 changes: 2 additions & 1 deletion authentik/enterprise/providers/rac/api/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.enterprise.api import EnterpriseRequiredMixin
from authentik.enterprise.providers.rac.models import RACProvider


class RACProviderSerializer(ProviderSerializer):
class RACProviderSerializer(EnterpriseRequiredMixin, ProviderSerializer):
"""RACProvider Serializer"""

outpost_set = ListField(child=CharField(), read_only=True, source="outpost_set.all")
Expand Down
4 changes: 2 additions & 2 deletions authentik/enterprise/providers/rac/apps.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""RAC app config"""
from authentik.blueprints.apps import ManagedAppConfig
from authentik.enterprise.apps import EnterpriseConfig


class AuthentikEnterpriseProviderRAC(ManagedAppConfig):
class AuthentikEnterpriseProviderRAC(EnterpriseConfig):
"""authentik enterprise rac app config"""

name = "authentik.enterprise.providers.rac"
Expand Down
2 changes: 1 addition & 1 deletion authentik/enterprise/providers/rac/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class AuthenticationMode(models.TextChoices):


class RACProvider(Provider):
"""Remotely access computers/servers"""
"""Remotely access computers/servers via RDP/SSH/VNC."""

settings = models.JSONField(default=dict)
auth_mode = models.TextField(
Expand Down
4 changes: 4 additions & 0 deletions schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19158,6 +19158,7 @@ paths:
- tr
- tt
- udm
- ug
- uk
- ur
- uz
Expand Down Expand Up @@ -42957,6 +42958,9 @@ components:
type: string
model_name:
type: string
requires_enterprise:
type: boolean
default: false
required:
- component
- description
Expand Down
32 changes: 25 additions & 7 deletions web/src/admin/property-mappings/PropertyMappingWizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,24 @@ import { WizardPage } from "@goauthentik/elements/wizard/WizardPage";

import { msg, str } from "@lit/localize";
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
import { CSSResult, TemplateResult, html } from "lit";
import { property } from "lit/decorators.js";
import { CSSResult, TemplateResult, html, nothing } from "lit";
import { property, state } from "lit/decorators.js";

import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFRadio from "@patternfly/patternfly/components/Radio/radio.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";

import { PropertymappingsApi, TypeCreate } from "@goauthentik/api";
import { EnterpriseApi, LicenseSummary, PropertymappingsApi, TypeCreate } from "@goauthentik/api";

@customElement("ak-property-mapping-wizard-initial")
export class InitialPropertyMappingWizardPage extends WizardPage {
@property({ attribute: false })
mappingTypes: TypeCreate[] = [];

@property({ attribute: false })
enterprise?: LicenseSummary;

static get styles(): CSSResult[] {
return [PFBase, PFForm, PFButton, PFRadio];
}
Expand Down Expand Up @@ -60,11 +63,20 @@ export class InitialPropertyMappingWizardPage extends WizardPage {
];
this.host.isValid = true;
}}
?disabled=${type.requiresEnterprise ? !this.enterprise?.hasLicense : false}
/>
<label class="pf-c-radio__label" for=${`${type.component}-${type.modelName}`}
>${type.name}</label
>
<span class="pf-c-radio__description">${type.description}</span>
${type.requiresEnterprise && !this.enterprise?.hasLicense
? html`
<ak-alert class="pf-c-radio__description" ?inline=${true}>
${msg("Provider require enterprise.")}
<a href="#/enterprise/licenses">${msg("Learn more")}</a>
</ak-alert>
`
: nothing}
</div>`;
})}
</form>`;
Expand All @@ -80,10 +92,16 @@ export class PropertyMappingWizard extends AKElement {
@property({ attribute: false })
mappingTypes: TypeCreate[] = [];

firstUpdated(): void {
new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsAllTypesList().then((types) => {
this.mappingTypes = types;
});
@state()
enterprise?: LicenseSummary;

async firstUpdated(): Promise<void> {
this.mappingTypes = await new PropertymappingsApi(
DEFAULT_CONFIG,
).propertymappingsAllTypesList();
this.enterprise = await new EnterpriseApi(
DEFAULT_CONFIG,
).enterpriseLicenseSummaryRetrieve();
}

render(): TemplateResult {
Expand Down
37 changes: 29 additions & 8 deletions web/src/admin/providers/ProviderWizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import "@goauthentik/admin/providers/proxy/ProxyProviderForm";
import "@goauthentik/admin/providers/saml/SAMLProviderForm";
import "@goauthentik/admin/providers/saml/SAMLProviderImportForm";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/Alert";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/forms/ProxyForm";
import { paramURL } from "@goauthentik/elements/router/RouterOutlet";
Expand All @@ -13,22 +14,25 @@ import { WizardPage } from "@goauthentik/elements/wizard/WizardPage";

import { msg, str } from "@lit/localize";
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
import { CSSResult, TemplateResult, html } from "lit";
import { property } from "lit/decorators.js";
import { CSSResult, TemplateResult, html, nothing } from "lit";
import { property, state } from "lit/decorators.js";

import PFButton from "@patternfly/patternfly/components/Button/button.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFHint from "@patternfly/patternfly/components/Hint/hint.css";
import PFRadio from "@patternfly/patternfly/components/Radio/radio.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";

import { ProvidersApi, TypeCreate } from "@goauthentik/api";
import { EnterpriseApi, LicenseSummary, ProvidersApi, TypeCreate } from "@goauthentik/api";

@customElement("ak-provider-wizard-initial")
export class InitialProviderWizardPage extends WizardPage {
@property({ attribute: false })
providerTypes: TypeCreate[] = [];

@property({ attribute: false })
enterprise?: LicenseSummary;

static get styles(): CSSResult[] {
return [PFBase, PFForm, PFHint, PFButton, PFRadio];
}
Expand Down Expand Up @@ -79,9 +83,18 @@ export class InitialProviderWizardPage extends WizardPage {
this.host.steps = ["initial", `type-${type.component}`];
this.host.isValid = true;
}}
?disabled=${type.requiresEnterprise ? !this.enterprise?.hasLicense : false}
/>
<label class="pf-c-radio__label" for=${type.component}>${type.name}</label>
<span class="pf-c-radio__description">${type.description}</span>
${type.requiresEnterprise && !this.enterprise?.hasLicense
? html`
<ak-alert class="pf-c-radio__description" ?inline=${true}>
${msg("Provider require enterprise.")}
<a href="#/enterprise/licenses">${msg("Learn more")}</a>
</ak-alert>
`
: nothing}
</div>`;
})}
</form>`;
Expand All @@ -100,15 +113,19 @@ export class ProviderWizard extends AKElement {
@property({ attribute: false })
providerTypes: TypeCreate[] = [];

@state()
enterprise?: LicenseSummary;

@property({ attribute: false })
finalHandler: () => Promise<void> = () => {
return Promise.resolve();
};

firstUpdated(): void {
new ProvidersApi(DEFAULT_CONFIG).providersAllTypesList().then((types) => {
this.providerTypes = types;
});
async firstUpdated(): Promise<void> {
this.providerTypes = await new ProvidersApi(DEFAULT_CONFIG).providersAllTypesList();
this.enterprise = await new EnterpriseApi(
DEFAULT_CONFIG,
).enterpriseLicenseSummaryRetrieve();
}

render(): TemplateResult {
Expand All @@ -121,7 +138,11 @@ export class ProviderWizard extends AKElement {
return this.finalHandler();
}}
>
<ak-provider-wizard-initial slot="initial" .providerTypes=${this.providerTypes}>
<ak-provider-wizard-initial
slot="initial"
.providerTypes=${this.providerTypes}
.enterprise=${this.enterprise}
>
</ak-provider-wizard-initial>
${this.providerTypes.map((type) => {
return html`
Expand Down
6 changes: 2 additions & 4 deletions web/src/elements/enterprise/EnterpriseStatusBanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ export class EnterpriseStatusBanner extends AKElement {
return [PFBanner];
}

firstUpdated(): void {
new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseSummaryRetrieve().then((b) => {
this.summary = b;
});
async firstUpdated(): Promise<void> {
this.summary = await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseSummaryRetrieve();
}

renderBanner(): TemplateResult {
Expand Down
6 changes: 6 additions & 0 deletions web/xliff/de.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -6237,6 +6237,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s6dd297c217729828">
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
</trans-unit>
<trans-unit id="scc7f34824150bfb8">
<source>Provider require enterprise.</source>
</trans-unit>
<trans-unit id="s31f1afc1bfe1cb3a">
<source>Learn more</source>
</trans-unit>
</body>
</file>
Expand Down
6 changes: 6 additions & 0 deletions web/xliff/en.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -6513,6 +6513,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s6dd297c217729828">
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
</trans-unit>
<trans-unit id="scc7f34824150bfb8">
<source>Provider require enterprise.</source>
</trans-unit>
<trans-unit id="s31f1afc1bfe1cb3a">
<source>Learn more</source>
</trans-unit>
</body>
</file>
Expand Down
6 changes: 6 additions & 0 deletions web/xliff/es.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -6153,6 +6153,12 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s6dd297c217729828">
<source>Determines how long a session lasts before being disconnected and requiring re-authorization.</source>
</trans-unit>
<trans-unit id="scc7f34824150bfb8">
<source>Provider require enterprise.</source>
</trans-unit>
<trans-unit id="s31f1afc1bfe1cb3a">
<source>Learn more</source>
</trans-unit>
</body>
</file>
Expand Down
Loading
Loading