Compare commits

...

10 Commits

Author SHA1 Message Date
2fb097061d release: 2024.8.0 2024-09-02 14:14:03 +02:00
8962d17e03 web: fix dual-select with dynamic selection (cherry-pick #11133) (#11134)
web: fix dual-select with dynamic selection (#11133)

* web: fix dual-select with dynamic selection

For dynamic selection, the property name is `.selector` to message that it's a function the
API layer uses to select the elements.

A few bits of lint picked.

* web: added comment to clarify what the fallback selector does

Co-authored-by: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com>
2024-08-30 19:07:36 +02:00
8326e1490c ci: fix failing release attestation (cherry-pick #11107) (#11120)
ci: fix failing release attestation (#11107)

* ci: fix failing release attestation



* fix



---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-08-29 13:29:47 +02:00
091e4d3e4c enterprise: fix incorrect comparison for latest validity date (cherry-pick #11109) (#11110)
enterprise: fix incorrect comparison for latest validity date (#11109)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-08-29 01:58:56 +02:00
6ee77edcbb website/docs: 2024.8 release notes: reword group sync disable and fix typo (cherry-pick #11103) (#11108)
website/docs: 2024.8 release notes: reword group sync disable and fix… (#11103)

Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-08-29 01:34:33 +02:00
763e2288bf release: 2024.8.0-rc2 2024-08-28 20:22:52 +02:00
9cdb177ca7 website/docs: a couple of minor rewrite things (#11099)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
# Conflicts:
#	website/docs/releases/2024/v2024.8.md
2024-08-28 20:22:21 +02:00
6070508058 providers/oauth2: audit_ignore last_login change for generated service account (cherry-pick #11085) (#11086)
providers/oauth2: audit_ignore last_login change for generated service account (#11085)

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-08-27 14:32:17 +02:00
ec13a5d84d release: 2024.8.0-rc1 2024-08-26 16:34:53 +02:00
057de82b01 schemas: fix XML Schema loading...for some reason?
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-08-26 16:34:47 +02:00
27 changed files with 4099 additions and 69 deletions

View File

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 2024.6.4 current_version = 2024.8.0
tag = True tag = True
commit = True commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))? parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?

View File

@ -29,9 +29,9 @@ outputs:
imageTags: imageTags:
description: "Docker image tags" description: "Docker image tags"
value: ${{ steps.ev.outputs.imageTags }} value: ${{ steps.ev.outputs.imageTags }}
imageNames: attestImageNames:
description: "Docker image names" description: "Docker image names used for attestation"
value: ${{ steps.ev.outputs.imageNames }} value: ${{ steps.ev.outputs.attestImageNames }}
imageMainTag: imageMainTag:
description: "Docker image main tag" description: "Docker image main tag"
value: ${{ steps.ev.outputs.imageMainTag }} value: ${{ steps.ev.outputs.imageMainTag }}

View File

@ -51,15 +51,24 @@ else:
] ]
image_main_tag = image_tags[0].split(":")[-1] image_main_tag = image_tags[0].split(":")[-1]
image_tags_rendered = ",".join(image_tags)
image_names_rendered = ",".join(set(name.split(":")[0] for name in image_tags))
def get_attest_image_names(image_with_tags: list[str]):
"""Attestation only for GHCR"""
image_tags = []
for image_name in set(name.split(":")[0] for name in image_with_tags):
if not image_name.startswith("ghcr.io"):
continue
image_tags.append(image_name)
return ",".join(set(image_tags))
with open(os.environ["GITHUB_OUTPUT"], "a+", encoding="utf-8") as _output: with open(os.environ["GITHUB_OUTPUT"], "a+", encoding="utf-8") as _output:
print(f"shouldBuild={should_build}", file=_output) print(f"shouldBuild={should_build}", file=_output)
print(f"sha={sha}", file=_output) print(f"sha={sha}", file=_output)
print(f"version={version}", file=_output) print(f"version={version}", file=_output)
print(f"prerelease={prerelease}", file=_output) print(f"prerelease={prerelease}", file=_output)
print(f"imageTags={image_tags_rendered}", file=_output) print(f"imageTags={','.join(image_tags)}", file=_output)
print(f"imageNames={image_names_rendered}", file=_output) print(f"attestImageNames={get_attest_image_names(image_tags)}", file=_output)
print(f"imageMainTag={image_main_tag}", file=_output) print(f"imageMainTag={image_main_tag}", file=_output)
print(f"imageMainName={image_tags[0]}", file=_output) print(f"imageMainName={image_tags[0]}", file=_output)

View File

@ -261,7 +261,7 @@ jobs:
id: attest id: attest
if: ${{ steps.ev.outputs.shouldBuild == 'true' }} if: ${{ steps.ev.outputs.shouldBuild == 'true' }}
with: with:
subject-name: ${{ steps.ev.outputs.imageNames }} subject-name: ${{ steps.ev.outputs.attestImageNames }}
subject-digest: ${{ steps.push.outputs.digest }} subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true push-to-registry: true
pr-comment: pr-comment:

View File

@ -115,7 +115,7 @@ jobs:
id: attest id: attest
if: ${{ steps.ev.outputs.shouldBuild == 'true' }} if: ${{ steps.ev.outputs.shouldBuild == 'true' }}
with: with:
subject-name: ${{ steps.ev.outputs.imageNames }} subject-name: ${{ steps.ev.outputs.attestImageNames }}
subject-digest: ${{ steps.push.outputs.digest }} subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true push-to-registry: true
build-binary: build-binary:

View File

@ -58,7 +58,7 @@ jobs:
- uses: actions/attest-build-provenance@v1 - uses: actions/attest-build-provenance@v1
id: attest id: attest
with: with:
subject-name: ${{ steps.ev.outputs.imageNames }} subject-name: ${{ steps.ev.outputs.attestImageNames }}
subject-digest: ${{ steps.push.outputs.digest }} subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true push-to-registry: true
build-outpost: build-outpost:
@ -122,7 +122,7 @@ jobs:
- uses: actions/attest-build-provenance@v1 - uses: actions/attest-build-provenance@v1
id: attest id: attest
with: with:
subject-name: ${{ steps.ev.outputs.imageNames }} subject-name: ${{ steps.ev.outputs.attestImageNames }}
subject-digest: ${{ steps.push.outputs.digest }} subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true push-to-registry: true
build-outpost-binary: build-outpost-binary:

View File

@ -2,7 +2,7 @@
from os import environ from os import environ
__version__ = "2024.6.4" __version__ = "2024.8.0"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -25,4 +25,4 @@ class AuthentikEnterpriseConfig(EnterpriseConfig):
"""Actual enterprise check, cached""" """Actual enterprise check, cached"""
from authentik.enterprise.license import LicenseKey from authentik.enterprise.license import LicenseKey
return LicenseKey.cached_summary().status return LicenseKey.cached_summary().status.is_valid

View File

@ -117,7 +117,7 @@ class LicenseKey:
our_cert.public_key(), our_cert.public_key(),
algorithms=["ES512"], algorithms=["ES512"],
audience=get_license_aud(), audience=get_license_aud(),
options={"verify_exp": check_expiry}, options={"verify_exp": check_expiry, "verify_signature": check_expiry},
), ),
) )
except PyJWTError: except PyJWTError:
@ -134,7 +134,7 @@ class LicenseKey:
exp_ts = int(mktime(lic.expiry.timetuple())) exp_ts = int(mktime(lic.expiry.timetuple()))
if total.exp == 0: if total.exp == 0:
total.exp = exp_ts total.exp = exp_ts
total.exp = min(total.exp, exp_ts) total.exp = max(total.exp, exp_ts)
total.license_flags.extend(lic.status.license_flags) total.license_flags.extend(lic.status.license_flags)
return total return total

View File

@ -433,20 +433,21 @@ class TokenParams:
app = Application.objects.filter(provider=self.provider).first() app = Application.objects.filter(provider=self.provider).first()
if not app or not app.provider: if not app or not app.provider:
raise TokenError("invalid_grant") raise TokenError("invalid_grant")
self.user, _ = User.objects.update_or_create( with audit_ignore():
# trim username to ensure the entire username is max 150 chars self.user, _ = User.objects.update_or_create(
# (22 chars being the length of the "template") # trim username to ensure the entire username is max 150 chars
username=f"ak-{self.provider.name[:150-22]}-client_credentials", # (22 chars being the length of the "template")
defaults={ username=f"ak-{self.provider.name[:150-22]}-client_credentials",
"attributes": { defaults={
USER_ATTRIBUTE_GENERATED: True, "attributes": {
USER_ATTRIBUTE_GENERATED: True,
},
"last_login": timezone.now(),
"name": f"Autogenerated user from application {app.name} (client credentials)",
"path": f"{USER_PATH_SYSTEM_PREFIX}/apps/{app.slug}",
"type": UserTypes.SERVICE_ACCOUNT,
}, },
"last_login": timezone.now(), )
"name": f"Autogenerated user from application {app.name} (client credentials)",
"path": f"{USER_PATH_SYSTEM_PREFIX}/apps/{app.slug}",
"type": UserTypes.SERVICE_ACCOUNT,
},
)
self.__check_policy_access(app, request) self.__check_policy_access(app, request)
Event.new( Event.new(

View File

@ -164,7 +164,7 @@ class SAMLProvider(Provider):
) )
sign_assertion = models.BooleanField(default=True) sign_assertion = models.BooleanField(default=True)
sign_response = models.BooleanField(default=True) sign_response = models.BooleanField(default=False)
@property @property
def launch_url(self) -> str | None: def launch_url(self) -> str | None:

View File

@ -54,7 +54,11 @@ class TestServiceProviderMetadataParser(TestCase):
request = self.factory.get("/") request = self.factory.get("/")
metadata = lxml_from_string(MetadataProcessor(provider, request).build_entity_descriptor()) metadata = lxml_from_string(MetadataProcessor(provider, request).build_entity_descriptor())
schema = etree.XMLSchema(etree.parse("schemas/saml-schema-metadata-2.0.xsd")) # nosec schema = etree.XMLSchema(
etree.parse(
source="schemas/saml-schema-metadata-2.0.xsd", parser=etree.XMLParser()
) # nosec
)
self.assertTrue(schema.validate(metadata)) self.assertTrue(schema.validate(metadata))
def test_schema_want_authn_requests_signed(self): def test_schema_want_authn_requests_signed(self):

View File

@ -47,7 +47,9 @@ class TestSchema(TestCase):
metadata = lxml_from_string(request) metadata = lxml_from_string(request)
schema = etree.XMLSchema(etree.parse("schemas/saml-schema-protocol-2.0.xsd")) # nosec schema = etree.XMLSchema(
etree.parse("schemas/saml-schema-protocol-2.0.xsd", parser=etree.XMLParser()) # nosec
)
self.assertTrue(schema.validate(metadata)) self.assertTrue(schema.validate(metadata))
def test_response_schema(self): def test_response_schema(self):
@ -68,5 +70,7 @@ class TestSchema(TestCase):
metadata = lxml_from_string(response) metadata = lxml_from_string(response)
schema = etree.XMLSchema(etree.parse("schemas/saml-schema-protocol-2.0.xsd")) # nosec schema = etree.XMLSchema(
etree.parse("schemas/saml-schema-protocol-2.0.xsd", parser=etree.XMLParser()) # nosec
)
self.assertTrue(schema.validate(metadata)) self.assertTrue(schema.validate(metadata))

View File

@ -30,7 +30,9 @@ class TestMetadataProcessor(TestCase):
xml = MetadataProcessor(self.source, request).build_entity_descriptor() xml = MetadataProcessor(self.source, request).build_entity_descriptor()
metadata = lxml_from_string(xml) metadata = lxml_from_string(xml)
schema = etree.XMLSchema(etree.parse("schemas/saml-schema-metadata-2.0.xsd")) # nosec schema = etree.XMLSchema(
etree.parse("schemas/saml-schema-metadata-2.0.xsd", parser=etree.XMLParser()) # nosec
)
self.assertTrue(schema.validate(metadata)) self.assertTrue(schema.validate(metadata))
def test_metadata_consistent(self): def test_metadata_consistent(self):

View File

@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema", "$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://goauthentik.io/blueprints/schema.json", "$id": "https://goauthentik.io/blueprints/schema.json",
"type": "object", "type": "object",
"title": "authentik 2024.6.4 Blueprint schema", "title": "authentik 2024.8.0 Blueprint schema",
"required": [ "required": [
"version", "version",
"entries" "entries"

View File

@ -31,7 +31,7 @@ services:
volumes: volumes:
- redis:/data - redis:/data
server: server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.6.4} image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.8.0}
restart: unless-stopped restart: unless-stopped
command: server command: server
environment: environment:
@ -52,7 +52,7 @@ services:
- postgresql - postgresql
- redis - redis
worker: worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.6.4} image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.8.0}
restart: unless-stopped restart: unless-stopped
command: worker command: worker
environment: environment:

View File

@ -29,4 +29,4 @@ func UserAgent() string {
return fmt.Sprintf("authentik@%s", FullVersion()) return fmt.Sprintf("authentik@%s", FullVersion())
} }
const VERSION = "2024.6.4" const VERSION = "2024.8.0"

View File

@ -1,5 +1,5 @@
{ {
"name": "@goauthentik/authentik", "name": "@goauthentik/authentik",
"version": "2024.6.4", "version": "2024.8.0",
"private": true "private": true
} }

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "authentik" name = "authentik"
version = "2024.6.4" version = "2024.8.0"
description = "" description = ""
authors = ["authentik Team <hello@goauthentik.io>"] authors = ["authentik Team <hello@goauthentik.io>"]

View File

@ -1,7 +1,7 @@
openapi: 3.0.3 openapi: 3.0.3
info: info:
title: authentik title: authentik
version: 2024.6.4 version: 2024.8.0
description: Making authentication simple. description: Making authentication simple.
contact: contact:
email: hello@goauthentik.io email: hello@goauthentik.io

View File

@ -46,7 +46,8 @@ async function makeSourcesSelector(instanceSources: string[] | undefined) {
return localSources return localSources
? ([pk, _]: DualSelectPair) => localSources.has(pk) ? ([pk, _]: DualSelectPair) => localSources.has(pk)
: ([_0, _1, _2, source]: DualSelectPair<Source>) => : // Creating a new instance, auto-select built-in source only when no other sources exist
([_0, _1, _2, source]: DualSelectPair<Source>) =>
source !== undefined && source.component === ""; source !== undefined && source.component === "";
} }
@ -75,11 +76,11 @@ export class IdentificationStageForm extends BaseStageForm<IdentificationStage>
stageUuid: this.instance.pk || "", stageUuid: this.instance.pk || "",
identificationStageRequest: data, identificationStageRequest: data,
}); });
} else {
return new StagesApi(DEFAULT_CONFIG).stagesIdentificationCreate({
identificationStageRequest: data,
});
} }
return new StagesApi(DEFAULT_CONFIG).stagesIdentificationCreate({
identificationStageRequest: data,
});
} }
isUserFieldSelected(field: UserFieldsEnum): boolean { isUserFieldSelected(field: UserFieldsEnum): boolean {
@ -232,12 +233,12 @@ export class IdentificationStageForm extends BaseStageForm<IdentificationStage>
?required=${true} ?required=${true}
name="sources" name="sources"
> >
<ak-dual-select-provider-dynamic-selected <ak-dual-select-dynamic-selected
.provider=${sourcesProvider} .provider=${sourcesProvider}
.selected=${makeSourcesSelector(this.instance?.sources)} .selector=${makeSourcesSelector(this.instance?.sources)}
available-label="${msg("Available Stages")}" available-label="${msg("Available Stages")}"
selected-label="${msg("Selected Stages")}" selected-label="${msg("Selected Stages")}"
></ak-dual-select-provider-dynamic-selected> ></ak-dual-select-dynamic-selected>
<p class="pf-c-form__helper-text"> <p class="pf-c-form__helper-text">
${msg( ${msg(
"Select sources should be shown for users to authenticate with. This only affects web-based sources, not LDAP.", "Select sources should be shown for users to authenticate with. This only affects web-based sources, not LDAP.",

View File

@ -3,7 +3,7 @@ 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 VERSION = "2024.6.4"; export const VERSION = "2024.8.0";
export const TITLE_DEFAULT = "authentik"; export const TITLE_DEFAULT = "authentik";
export const ROUTE_SEPARATOR = ";"; export const ROUTE_SEPARATOR = ";";

View File

@ -50,3 +50,9 @@ export class AkDualSelectDynamic extends AkDualSelectProvider {
></ak-dual-select>`; ></ak-dual-select>`;
} }
} }
declare global {
interface HTMLElementTagNameMap {
"ak-dual-select-dynamic-selected": AkDualSelectDynamic;
}
}

View File

@ -36,11 +36,11 @@ To disable existing blueprints, an empty file can be mounted over the existing b
File-based blueprints are automatically removed once they become unavailable, however none of the objects created by those blueprints afre affected by this. File-based blueprints are automatically removed once they become unavailable, however none of the objects created by those blueprints afre affected by this.
:::info :::info
Please note that, by default, blueprint discovery and evaluation is not guaranteed to follow any specific order. Please note that, by default, blueprint discovery and evaluation is not guaranteed to follow any specific order.
If you have dependencies between blueprints, you should use [meta models](/developer-docs/blueprints/v1/meta#authentik_blueprintsmetaapplyblueprint) to make sure that objects are created in the correct order. If you have dependencies between blueprints, you should use [meta models](./v1/meta#authentik_blueprintsmetaapplyblueprint) to make sure that objects are created in the correct order.
::: :::
## Storage - OCI ## Storage - OCI

View File

@ -105,11 +105,7 @@ The following events occur when a license expeires and is not renewed within two
### About users and licenses ### About users and licenses
License usage is calculated based on total user counts and log-in data data that authentik regularly captures. This data is checked against all valid licenses, and the sum total of all users. License usage is calculated based on total user counts that authentik regularly captures. This data is checked against all valid licenses, and the sum total of all users. Internal and external users are counted based on the number of active users of the respective type saved in authentik. Service account users are not counted towards the license.
- The **_internal user_** count is calculated based on actual users assigned to the organization.
- The **_external user_** count is calculated based on how many external users were active (i.e. logged in) since the start of the current month.
:::info :::info
An **internal** user is typically a team member, such as company employees, who has access to the full Enterprise feature set. An **external** user might be an external consultant, a volunteer in a charitable site, or a B2C customer who logged onto your website to shop. These users don't get access to Enterprise features. An **internal** user is typically a team member, such as company employees, who has access to the full Enterprise feature set. An **external** user might be an external consultant, a volunteer in a charitable site, or a B2C customer who logged onto your website to shop. These users don't get access to Enterprise features.

View File

@ -18,7 +18,8 @@ Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials& grant_type=client_credentials&
client_id=application_client_id& client_id=application_client_id&
username=my-service-account& username=my-service-account&
password=my-token password=my-token&
scope=profile
``` ```
This will return a JSON response with an `access_token`, which is a signed JWT token. This token can be sent along requests to other hosts, which can then validate the JWT based on the signing key configured in authentik. This will return a JSON response with an `access_token`, which is a signed JWT token. This token can be sent along requests to other hosts, which can then validate the JWT based on the signing key configured in authentik.

File diff suppressed because it is too large Load Diff