Skip to content

Commit

Permalink
Merge branch 'dev' into web/sidebar-with-live-content-3
Browse files Browse the repository at this point in the history
* dev: (21 commits)
  sources/ldap: clean-up certs written from db (#7617)
  web: bump the eslint group in /tests/wdio with 1 update (#7635)
  core: compile backend translations (#7637)
  core: bump psycopg from 3.1.12 to 3.1.13 (#7625)
  core: bump ruff from 0.1.5 to 0.1.6 (#7626)
  core: bump twilio from 8.10.1 to 8.10.2 (#7627)
  web: bump the eslint group in /web with 1 update (#7629)
  web: bump the esbuild group in /web with 2 updates (#7630)
  web: bump rollup from 4.4.1 to 4.5.0 in /web (#7631)
  web: bump core-js from 3.33.2 to 3.33.3 in /web (#7633)
  core: bump goauthentik.io/api/v3 from 3.2023103.3 to 3.2023103.4 (#7634)
  web: bump the wdio group in /tests/wdio with 4 updates (#7636)
  translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_TW (#7628)
  root: specify node and python versions in respective config files, deduplicate in CI (#7620)
  translate: Updates for file web/xliff/en.xlf in zh-Hans (#7619)
  translate: Updates for file web/xliff/en.xlf in zh_CN (#7618)
  tests: better per-test timeouts (#7612)
  web: bump API Client version (#7613)
  stages/identification: add option to pretend user exists (#7610)
  events: stop spam (#7611)
  ...
  • Loading branch information
kensternberg-authentik committed Nov 20, 2023
2 parents a9886b0 + 2ec8932 commit 6b92019
Show file tree
Hide file tree
Showing 55 changed files with 520 additions and 240 deletions.
16 changes: 10 additions & 6 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,27 @@ inputs:
runs:
using: "composite"
steps:
- name: Install poetry
- name: Install poetry & deps
shell: bash
run: |
pipx install poetry || true
sudo apt update
sudo apt install -y libpq-dev openssl libxmlsec1-dev pkg-config gettext
sudo apt-get update
sudo apt-get install --no-install-recommends -y libpq-dev openssl libxmlsec1-dev pkg-config gettext
- name: Setup python and restore poetry
uses: actions/setup-python@v3
uses: actions/setup-python@v4
with:
python-version: "3.11"
python-version-file: 'pyproject.toml'
cache: "poetry"
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: "20"
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Setup go
uses: actions/setup-go@v4
with:
go-version-file: "go.mod"
- name: Setup dependencies
shell: bash
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ jobs:
uses: helm/kind-action@v1.8.0
- name: run integration
run: |
poetry run coverage run manage.py test tests/integration
poetry run coverage run manage.py test --randomly-seed=2100196988 tests/integration
poetry run coverage xml
- if: ${{ always() }}
uses: codecov/codecov-action@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-outpost.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ jobs:
go-version-file: "go.mod"
- uses: actions/setup-node@v4
with:
node-version: "20"
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Generate API
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/ci-web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
node-version-file: ${{ matrix.project }}/package.json
cache: "npm"
cache-dependency-path: ${{ matrix.project }}/package-lock.json
- working-directory: ${{ matrix.project }}/
Expand All @@ -40,7 +40,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- working-directory: web/
Expand All @@ -62,7 +62,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
node-version-file: ${{ matrix.project }}/package.json
cache: "npm"
cache-dependency-path: ${{ matrix.project }}/package-lock.json
- working-directory: ${{ matrix.project }}/
Expand All @@ -78,7 +78,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- working-directory: web/
Expand Down Expand Up @@ -110,7 +110,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- working-directory: web/
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci-website.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
node-version-file: website/package.json
cache: "npm"
cache-dependency-path: website/package-lock.json
- working-directory: website/
Expand All @@ -32,7 +32,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
node-version-file: website/package.json
cache: "npm"
cache-dependency-path: website/package-lock.json
- working-directory: website/
Expand All @@ -53,7 +53,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
node-version-file: website/package.json
cache: "npm"
cache-dependency-path: website/package-lock.json
- working-directory: website/
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/gha-cache-cleanup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ on:
types:
- closed

permissions:
# Permission to delete cache
actions: write

jobs:
cleanup:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ jobs:
go-version-file: "go.mod"
- uses: actions/setup-node@v4
with:
node-version: "20"
node-version-file: web/package.json
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Build web
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/translation-advice.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ on:
- "!locale/en/**"
- "web/xliff/**"

permissions:
# Permission to write comment
pull-requests: write

jobs:
post-comment:
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/translation-rename.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ on:
pull_request:
types: [opened, reopened]

permissions:
# Permission to rename PR
pull-requests: write

jobs:
rename_pr:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/web-api-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
token: ${{ steps.generate_token.outputs.token }}
- uses: actions/setup-node@v4
with:
node-version: "20"
node-version-file: web/package.json
registry-url: "https://registry.npmjs.org"
- name: Generate API Client
run: make gen-client-ts
Expand Down
5 changes: 5 additions & 0 deletions authentik/core/api/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ def validate_type(self, user_type: str) -> str:
raise ValidationError("Setting a user to internal service account is not allowed.")
return user_type

def validate(self, attrs: dict) -> dict:
if self.instance and self.instance.type == UserTypes.INTERNAL_SERVICE_ACCOUNT:
raise ValidationError("Can't modify internal service account users")
return super().validate(attrs)

class Meta:
model = User
fields = [
Expand Down
3 changes: 3 additions & 0 deletions authentik/events/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from authentik.lib.utils.errors import exception_to_string
from authentik.outposts.models import OutpostServiceConnection
from authentik.policies.models import Policy, PolicyBindingModel
from authentik.policies.reputation.models import Reputation
from authentik.providers.oauth2.models import AccessToken, AuthorizationCode, RefreshToken
from authentik.providers.scim.models import SCIMGroup, SCIMUser
from authentik.stages.authenticator_static.models import StaticToken
Expand All @@ -52,11 +53,13 @@
RefreshToken,
SCIMUser,
SCIMGroup,
Reputation,
)


def should_log_model(model: Model) -> bool:
"""Return true if operation on `model` should be logged"""
# Check for silk by string so this comparison doesn't fail when silk isn't installed
if model.__module__.startswith("silk"):
return False
return model.__class__ not in IGNORED_MODELS
Expand Down
1 change: 1 addition & 0 deletions authentik/flows/tests/test_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ def test_invalid_restart(self):
ident_stage = IdentificationStage.objects.create(
name="ident",
user_fields=[UserFields.E_MAIL],
pretend_user_exists=False,
)
FlowStageBinding.objects.create(
target=flow,
Expand Down
10 changes: 9 additions & 1 deletion authentik/lib/avatars.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,15 @@ def generate_avatar_from_name(

def avatar_mode_generated(user: "User", mode: str) -> Optional[str]:
"""Wrapper that converts generated avatar to base64 svg"""
svg = generate_avatar_from_name(user.name if user.name.strip() != "" else "a k")
# By default generate based off of user's display name
name = user.name.strip()
if name == "":
# Fallback to username
name = user.username.strip()
# If we still don't have anything, fallback to `a k`
if name == "":
name = "a k"
svg = generate_avatar_from_name(name)
return f"data:image/svg+xml;base64,{b64encode(svg.encode('utf-8')).decode('utf-8')}"


Expand Down
20 changes: 15 additions & 5 deletions authentik/outposts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,12 +344,22 @@ def user(self) -> User:
user_created = False
if not user:
user: User = User.objects.create(username=self.user_identifier)
user.set_unusable_password()
user_created = True
user.type = UserTypes.INTERNAL_SERVICE_ACCOUNT
user.name = f"Outpost {self.name} Service-Account"
user.path = USER_PATH_OUTPOSTS
user.save()
attrs = {
"type": UserTypes.INTERNAL_SERVICE_ACCOUNT,
"name": f"Outpost {self.name} Service-Account",
"path": USER_PATH_OUTPOSTS,
}
dirty = False
for key, value in attrs.items():
if getattr(user, key) != value:
dirty = True
setattr(user, key, value)
if user.has_usable_password():
user.set_unusable_password()
dirty = True
if dirty:
user.save()
if user_created:
self.build_user_permissions(user)
return user
Expand Down
7 changes: 7 additions & 0 deletions authentik/sources/ldap/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""authentik LDAP Models"""
from os import chmod
from os.path import dirname, exists
from shutil import rmtree
from ssl import CERT_REQUIRED
from tempfile import NamedTemporaryFile, mkdtemp
from typing import Optional
Expand Down Expand Up @@ -189,6 +191,11 @@ def connection(
raise exc
server_kwargs["get_info"] = NONE
return self.connection(server, server_kwargs, connection_kwargs)
finally:
if connection.server.tls.certificate_file is not None and exists(
connection.server.tls.certificate_file
):
rmtree(dirname(connection.server.tls.certificate_file))
return RuntimeError("Failed to bind")

@property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,14 @@ class Migration(migrations.Migration):
name="totpdevice",
options={"verbose_name": "TOTP Device", "verbose_name_plural": "TOTP Devices"},
),
migrations.AlterField(
model_name="authenticatortotpstage",
name="digits",
field=models.IntegerField(
choices=[
("6", "6 digits, widely compatible"),
("8", "8 digits, not compatible with apps like Google Authenticator"),
]
),
),
]
1 change: 1 addition & 0 deletions authentik/stages/identification/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Meta:
"passwordless_flow",
"sources",
"show_source_labels",
"pretend_user_exists",
]


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.7 on 2023-11-17 16:32

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
(
"authentik_stages_identification",
"0002_auto_20200530_2204_squashed_0013_identificationstage_passwordless_flow",
),
]

operations = [
migrations.AddField(
model_name="identificationstage",
name="pretend_user_exists",
field=models.BooleanField(
default=True,
help_text="When enabled, the stage will succeed and continue even when incorrect user info is entered.",
),
),
]
7 changes: 7 additions & 0 deletions authentik/stages/identification/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ class IdentificationStage(Stage):
"entered will be shown"
),
)
pretend_user_exists = models.BooleanField(
default=True,
help_text=_(
"When enabled, the stage will succeed and continue even when incorrect user info "
"is entered."
),
)

enrollment_flow = models.ForeignKey(
Flow,
Expand Down
4 changes: 2 additions & 2 deletions authentik/stages/identification/stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
self.pre_user = self.stage.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
if not current_stage.show_matched_user:
self.stage.executor.plan.context[PLAN_CONTEXT_PENDING_USER_IDENTIFIER] = uid_field
if self.stage.executor.flow.designation == FlowDesignation.RECOVERY:
# When used in a recovery flow, always continue to not disclose if a user exists
# when `pretend` is enabled, continue regardless
if current_stage.pretend_user_exists:
return attrs
raise ValidationError("Failed to authenticate.")
self.pre_user = pre_user
Expand Down
21 changes: 21 additions & 0 deletions authentik/stages/identification/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def setUp(self):
self.stage = IdentificationStage.objects.create(
name="identification",
user_fields=[UserFields.E_MAIL],
pretend_user_exists=False,
)
self.stage.sources.set([source])
self.stage.save()
Expand Down Expand Up @@ -106,6 +107,26 @@ def test_invalid_with_username(self):
form_data,
)
self.assertEqual(response.status_code, 200)
self.assertStageResponse(
response,
self.flow,
component="ak-stage-identification",
response_errors={
"non_field_errors": [{"string": "Failed to authenticate.", "code": "invalid"}]
},
)

def test_invalid_with_username_pretend(self):
"""Test invalid with username (user exists but stage only allows email)"""
self.stage.pretend_user_exists = True
self.stage.save()
form_data = {"uid_field": self.user.username}
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
form_data,
)
self.assertEqual(response.status_code, 200)
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))

def test_invalid_no_fields(self):
"""Test invalid with username (no user fields are enabled)"""
Expand Down
Loading

0 comments on commit 6b92019

Please sign in to comment.