diff --git a/Jenkinsfile b/Jenkinsfile index 42329fc5..03f054d6 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -188,6 +188,11 @@ pipeline { sh 'docker logs $(docker-compose ps --all -q pdns) > debug/postgres/docker_pdns.log 2>&1' sh 'docker logs $(docker-compose ps --all -q pdns-db) > debug/postgres/docker_pdns-db.log 2>&1' sh 'docker logs $(docker-compose ps --all -q dnsrouter) > debug/postgres/docker_dnsrouter.log 2>&1' + sh 'docker logs $(docker-compose ps --all -q db-postgres) > debug/postgres/docker_db.log 2>&1' + sh 'docker logs $(docker-compose ps --all -q authentik) > debug/postgres/docker_authentik.log 2>&1' + sh 'docker logs $(docker-compose ps --all -q authentik-redis) > debug/postgres/docker_authentik-redis.log 2>&1' + sh 'docker logs $(docker-compose ps --all -q authentik-ldap) > debug/postgres/docker_authentik-ldap.log 2>&1' + junit 'test/results/junit/*' sh 'docker-compose down --remove-orphans --volumes -t 30 || true' } @@ -248,16 +253,16 @@ pipeline { printResult() } failure { + archiveArtifacts(artifacts: 'debug/**/*', allowEmptyArchive: true) dir(path: 'test') { archiveArtifacts allowEmptyArchive: true, artifacts: 'results/**/*', excludes: '**/*.xml' } - archiveArtifacts(artifacts: 'debug/*', allowEmptyArchive: true) } unstable { + archiveArtifacts(artifacts: 'debug/**/*', allowEmptyArchive: true) dir(path: 'test') { archiveArtifacts allowEmptyArchive: true, artifacts: 'results/**/*', excludes: '**/*.xml' } - archiveArtifacts(artifacts: 'debug/*', allowEmptyArchive: true) } } } diff --git a/backend/.testcoverage.yml b/backend/.testcoverage.yml index ca20e288..17444139 100644 --- a/backend/.testcoverage.yml +++ b/backend/.testcoverage.yml @@ -18,4 +18,4 @@ threshold: # package: 30 # (optional; default 0) # The minimum total coverage project should have - total: 34 + total: 33 diff --git a/backend/embed/api_docs/components/UserAuthObject.json b/backend/embed/api_docs/components/UserAuthObject.json index d24e8398..4c1979d5 100644 --- a/backend/embed/api_docs/components/UserAuthObject.json +++ b/backend/embed/api_docs/components/UserAuthObject.json @@ -24,7 +24,7 @@ }, "type": { "type": "string", - "pattern": "^password$" + "pattern": "^(local|ldap|oidc)$" } } } diff --git a/backend/embed/api_docs/components/UserObject.json b/backend/embed/api_docs/components/UserObject.json index d7f3328e..f5776ebc 100644 --- a/backend/embed/api_docs/components/UserObject.json +++ b/backend/embed/api_docs/components/UserObject.json @@ -53,7 +53,7 @@ }, "type": { "type": "string", - "pattern": "^password$" + "pattern": "^(local|ldap|oidc)$" } } }, diff --git a/backend/embed/migrations/mysql/20201013035839_initial_data.sql b/backend/embed/migrations/mysql/20201013035839_initial_data.sql index 6814fe0e..5be06a48 100644 --- a/backend/embed/migrations/mysql/20201013035839_initial_data.sql +++ b/backend/embed/migrations/mysql/20201013035839_initial_data.sql @@ -125,14 +125,12 @@ INSERT INTO `user` ( `created_at`, `updated_at`, `name`, - `nickname`, `email`, `is_system` ) VALUES ( ROUND(UNIX_TIMESTAMP(CURTIME(4)) * 1000), ROUND(UNIX_TIMESTAMP(CURTIME(4)) * 1000), "System", - "System", "system@localhost", TRUE ); diff --git a/backend/embed/migrations/postgres/20201013035839_initial_data.sql b/backend/embed/migrations/postgres/20201013035839_initial_data.sql index 97126f83..fb3483fd 100644 --- a/backend/embed/migrations/postgres/20201013035839_initial_data.sql +++ b/backend/embed/migrations/postgres/20201013035839_initial_data.sql @@ -125,14 +125,12 @@ INSERT INTO "user" ( "created_at", "updated_at", "name", - "nickname", "email", "is_system" ) VALUES ( EXTRACT(EPOCH FROM TIMESTAMP '2011-05-17 10:40:28.876944') * 1000, EXTRACT(EPOCH FROM TIMESTAMP '2011-05-17 10:40:28.876944') * 1000, 'System', - 'System', 'system@localhost', TRUE ); diff --git a/backend/embed/migrations/sqlite/20201013035839_initial_data.sql b/backend/embed/migrations/sqlite/20201013035839_initial_data.sql index 48bd18dc..90beda89 100644 --- a/backend/embed/migrations/sqlite/20201013035839_initial_data.sql +++ b/backend/embed/migrations/sqlite/20201013035839_initial_data.sql @@ -124,14 +124,12 @@ INSERT INTO `user` ( created_at, updated_at, name, - nickname, email, is_system ) VALUES ( unixepoch() * 1000, unixepoch() * 1000, "System", - "System", "system@localhost", 1 ); diff --git a/backend/internal/entity/auth/entity_test.go b/backend/internal/entity/auth/entity_test.go index 6c54b62f..29a4f39e 100644 --- a/backend/internal/entity/auth/entity_test.go +++ b/backend/internal/entity/auth/entity_test.go @@ -97,13 +97,14 @@ func (s *testsuite) TestSave() { defer goleak.VerifyNone(s.T(), goleak.IgnoreAnyFunction("database/sql.(*DB).connectionOpener")) s.mock.ExpectBegin() - s.mock.ExpectQuery(regexp.QuoteMeta(`INSERT INTO "auth" ("created_at","updated_at","is_deleted","user_id","type","secret") VALUES ($1,$2,$3,$4,$5,$6) RETURNING "id"`)). + s.mock.ExpectQuery(regexp.QuoteMeta(`INSERT INTO "auth" ("created_at","updated_at","is_deleted","user_id","type","identity","secret") VALUES ($1,$2,$3,$4,$5,$6,$7) RETURNING "id"`)). WithArgs( sqlmock.AnyArg(), sqlmock.AnyArg(), 0, 100, TypeLocal, + "", "abc123", ). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("11")) diff --git a/backend/internal/entity/auth/model.go b/backend/internal/entity/auth/model.go index 729f7c58..5348763f 100644 --- a/backend/internal/entity/auth/model.go +++ b/backend/internal/entity/auth/model.go @@ -39,7 +39,6 @@ func (m *Model) LoadByID(id int) error { // Save will save this model to the DB func (m *Model) Save() error { db := database.GetDB() - // todo: touch? not sure that save does this or not? result := db.Save(m) return result.Error } diff --git a/backend/internal/entity/user/entity_test.go b/backend/internal/entity/user/entity_test.go index e1f296e5..b78f5e07 100644 --- a/backend/internal/entity/user/entity_test.go +++ b/backend/internal/entity/user/entity_test.go @@ -84,7 +84,6 @@ func (s *testsuite) SetupTest() { ).AddRow( 11, "Jane Doe", - "Jane", "jane@example.com", true, false, @@ -183,7 +182,6 @@ func (s *testsuite) TestSave() { sqlmock.AnyArg(), 0, "John Doe", - "Jonny", "sarah@example.com", false, false, diff --git a/docker/ci.env b/docker/ci.env new file mode 100644 index 00000000..7128295d --- /dev/null +++ b/docker/ci.env @@ -0,0 +1,8 @@ +AUTHENTIK_SECRET_KEY=gl8woZe8L6IIX8SC0c5Ocsj0xPkX5uJo5DVZCFl+L/QGbzuplfutYuua2ODNLEiDD3aFd9H2ylJmrke0 +AUTHENTIK_REDIS__HOST=authentik-redis +AUTHENTIK_POSTGRESQL__HOST=db-postgres +AUTHENTIK_POSTGRESQL__USER=authentik +AUTHENTIK_POSTGRESQL__NAME=authentik +AUTHENTIK_POSTGRESQL__PASSWORD=07EKS5NLI6Tpv68tbdvrxfvj +AUTHENTIK_BOOTSTRAP_PASSWORD=admin +AUTHENTIK_BOOTSTRAP_EMAIL=admin@example.com diff --git a/docker/ci/postgres/authentik.sql.gz b/docker/ci/postgres/authentik.sql.gz new file mode 100644 index 00000000..3eed585b Binary files /dev/null and b/docker/ci/postgres/authentik.sql.gz differ diff --git a/docker/docker-compose.ci.postgres.yml b/docker/docker-compose.ci.postgres.yml index e70d3bd7..b0e9aac3 100644 --- a/docker/docker-compose.ci.postgres.yml +++ b/docker/docker-compose.ci.postgres.yml @@ -12,15 +12,63 @@ services: NPM_DB_SSLMODE: 'disable' depends_on: - db-postgres + - authentik + - authentik-worker + - authentik-ldap db-postgres: - image: postgres:15 + image: postgres:latest environment: POSTGRES_USER: 'npm' POSTGRES_PASSWORD: 'npmpass' POSTGRES_DB: 'npm' volumes: - psql_vol:/var/lib/postgresql/data + - ./ci/postgres:/docker-entrypoint-initdb.d + + authentik-redis: + image: 'redis:alpine' + command: --save 60 1 --loglevel warning + restart: unless-stopped + healthcheck: + test: ['CMD-SHELL', 'redis-cli ping | grep PONG'] + start_period: 20s + interval: 30s + retries: 5 + timeout: 3s + volumes: + - redis_vol:/data + + authentik: + image: ghcr.io/goauthentik/server:2024.8.3 + restart: unless-stopped + command: server + env_file: + - ci.env + depends_on: + - authentik-redis + - db-postgres + + authentik-worker: + image: ghcr.io/goauthentik/server:2024.8.3 + restart: unless-stopped + command: worker + env_file: + - ci.env + depends_on: + - authentik-redis + - db-postgres + + authentik-ldap: + image: ghcr.io/goauthentik/ldap + environment: + AUTHENTIK_HOST: 'http://authentik:9000' + AUTHENTIK_INSECURE: 'true' + AUTHENTIK_TOKEN: '1N7z2r5PZrNBauuyDZSnlhU4gPSih7bkooIgqbvhzBbrA1MGYyDGZmBasJqU' + restart: unless-stopped + depends_on: + - authentik volumes: psql_vol: + redis_vol: diff --git a/docs/src/development/index.md b/docs/src/development/index.md index f1f89d26..4d1b3432 100644 --- a/docs/src/development/index.md +++ b/docs/src/development/index.md @@ -25,7 +25,6 @@ curl --request POST \ --header 'Content-Type: application/json' \ --data '{ "name": "Bobby Tables", - "nickname": "Bobby", "email": "you@example.com", "roles": ["admin"], "is_disabled": false, diff --git a/frontend/src/api/npm/createUser.ts b/frontend/src/api/npm/createUser.ts index 8c893240..59f74035 100644 --- a/frontend/src/api/npm/createUser.ts +++ b/frontend/src/api/npm/createUser.ts @@ -8,7 +8,6 @@ export interface AuthOptions { export interface NewUser { name: string; - nickname: string; email: string; isDisabled: boolean; auth: AuthOptions; diff --git a/frontend/src/api/npm/models.ts b/frontend/src/api/npm/models.ts index d960ba27..75e86089 100644 --- a/frontend/src/api/npm/models.ts +++ b/frontend/src/api/npm/models.ts @@ -14,7 +14,6 @@ export interface UserAuth { export interface User { id: number; name: string; - nickname: string; email: string; createdOn: number; updatedOn: number; diff --git a/frontend/src/locale/src/de.json b/frontend/src/locale/src/de.json index 8f7dfcbe..319c93ac 100644 --- a/frontend/src/locale/src/de.json +++ b/frontend/src/locale/src/de.json @@ -185,6 +185,9 @@ "error.email-already-exists": { "defaultMessage": "Es existiert bereits ein Benutzer mit dieser E-Mail-Adresse" }, + "error.invalid-auth-type": { + "defaultMessage": "Invalid authentication type" + }, "error.invalid-login-credentials": { "defaultMessage": "Ungültige Login-Details" }, @@ -440,9 +443,6 @@ "user.name": { "defaultMessage": "Name" }, - "user.nickname": { - "defaultMessage": "Benutzername" - }, "user.password": { "defaultMessage": "Passwort" }, diff --git a/frontend/src/locale/src/en.json b/frontend/src/locale/src/en.json index 53cf596d..00573e08 100644 --- a/frontend/src/locale/src/en.json +++ b/frontend/src/locale/src/en.json @@ -461,6 +461,9 @@ "error.email-already-exists": { "defaultMessage": "A user already exists with this email address" }, + "error.invalid-auth-type": { + "defaultMessage": "Invalid authentication type" + }, "error.invalid-login-credentials": { "defaultMessage": "Invalid login credentials" }, @@ -722,9 +725,6 @@ "user.name": { "defaultMessage": "Name" }, - "user.nickname": { - "defaultMessage": "Nickname" - }, "user.password": { "defaultMessage": "Password" }, diff --git a/frontend/src/locale/src/fa.json b/frontend/src/locale/src/fa.json index 3e4438de..8f546c10 100644 --- a/frontend/src/locale/src/fa.json +++ b/frontend/src/locale/src/fa.json @@ -185,6 +185,9 @@ "error.email-already-exists": { "defaultMessage": "کاربری از قبل با این آدرس ایمیل وجود دارد" }, + "error.invalid-auth-type": { + "defaultMessage": "Invalid authentication type" + }, "error.invalid-login-credentials": { "defaultMessage": "اعتبار ورود نامعتبر است" }, @@ -443,9 +446,6 @@ "user.name": { "defaultMessage": "نام" }, - "user.nickname": { - "defaultMessage": "کنیه" - }, "user.password": { "defaultMessage": "کلمه عبور" }, diff --git a/frontend/src/modals/ProfileModal.tsx b/frontend/src/modals/ProfileModal.tsx index 11ae78cf..4b164b8a 100644 --- a/frontend/src/modals/ProfileModal.tsx +++ b/frontend/src/modals/ProfileModal.tsx @@ -5,19 +5,19 @@ import { FormLabel, Input, Modal, - ModalOverlay, - ModalContent, - ModalHeader, - ModalCloseButton, ModalBody, + ModalCloseButton, + ModalContent, ModalFooter, + ModalHeader, + ModalOverlay, Stack, useToast, } from "@chakra-ui/react"; -import { Formik, Form, Field } from "formik"; +import { Field, Form, Formik } from "formik"; import { PrettyButton } from "src/components"; -import { useUser, useSetUser } from "src/hooks"; +import { useSetUser, useUser } from "src/hooks"; import { intl } from "src/locale"; import { validateEmail, validateString } from "src/modules/Validations"; @@ -59,7 +59,6 @@ function ProfileModal({ isOpen, onClose }: ProfileModalProps) { @@ -71,7 +70,7 @@ function ProfileModal({ isOpen, onClose }: ProfileModalProps) { - + {({ field, form }: any) => ( )} - - {({ field, form }: any) => ( - - - {intl.formatMessage({ id: "user.nickname" })} - - - - {form.errors.nickname} - - - )} - {({ field, form }: any) => ( - + {({ field, form }: any) => ( )} - - {({ field, form }: any) => ( - - - {intl.formatMessage({ id: "user.nickname" })} - - - - {form.errors.nickname} - - - )} - {({ field, form }: any) => ( - + {({ field, form }: any) => ( )} - - {({ field, form }: any) => ( - - - {intl.formatMessage({ id: "user.nickname" })} - - - - {form.errors.nickname} - - - )} - {({ field, form }: any) => ( (
- + {({ field, form }: any) => ( )} - - {({ field, form }: any) => ( - - - {intl.formatMessage({ id: "user.nickname" })} - - - - {form.errors.nickname} - - - )} - {({ field, form }: any) => ( "${LOCAL_RESOLVE}" docker-compose up -d --remove-orphans stepca docker-compose pull db-mysql || true # ok to fail docker-compose pull db-postgres || true # ok to fail +docker-compose pull authentik authentik-redis authentik-ldap || true # ok to fail docker-compose up -d --remove-orphans --pull=never fullstack # wait for main container to be healthy diff --git a/test/cypress/e2e/api/Certificates.cy.js b/test/cypress/e2e/api/Certificates.cy.js index b60ef9d7..e79b5ec7 100644 --- a/test/cypress/e2e/api/Certificates.cy.js +++ b/test/cypress/e2e/api/Certificates.cy.js @@ -1,4 +1,4 @@ -/// +/// describe('Certificates endpoints', () => { let token; diff --git a/test/cypress/e2e/api/FullCertProvision.cy.js b/test/cypress/e2e/api/FullCertProvision.cy.js index fcbb998a..c5cec7c4 100644 --- a/test/cypress/e2e/api/FullCertProvision.cy.js +++ b/test/cypress/e2e/api/FullCertProvision.cy.js @@ -1,4 +1,4 @@ -/// +/// describe('Full Certificate Provisions', () => { let token; diff --git a/test/cypress/e2e/api/Health.cy.js b/test/cypress/e2e/api/Health.cy.js index 873b16cc..a7053af8 100644 --- a/test/cypress/e2e/api/Health.cy.js +++ b/test/cypress/e2e/api/Health.cy.js @@ -1,4 +1,4 @@ -/// +/// describe('Basic API checks', () => { it('Should return a valid health payload', function() { diff --git a/test/cypress/e2e/api/Settings.cy.js b/test/cypress/e2e/api/Settings.cy.js index 62148dda..3b1bafd2 100644 --- a/test/cypress/e2e/api/Settings.cy.js +++ b/test/cypress/e2e/api/Settings.cy.js @@ -1,4 +1,4 @@ -/// +/// // Settings are stored lowercase in the backend diff --git a/test/cypress/e2e/api/SetupPhase.cy.js b/test/cypress/e2e/api/SetupPhase.cy.js index 9c9f3dca..010c6555 100644 --- a/test/cypress/e2e/api/SetupPhase.cy.js +++ b/test/cypress/e2e/api/SetupPhase.cy.js @@ -1,4 +1,4 @@ -/// +/// describe('Setup Phase', () => { @@ -8,15 +8,15 @@ describe('Setup Phase', () => { it('Should NOT be able to get a token', function() { cy.task('backendApiPost', { - path: '/api/tokens', + path: '/api/auth', data: { - type: 'password', + type: 'local', identity: 'cypress@example.com', secret: 'changeme' }, returnOnError: true }).then((data) => { - cy.validateSwaggerSchema('post', 403, '/tokens', data); + cy.validateSwaggerSchema('post', 403, '/auth', data); expect(data.error).to.have.property('code', 403); expect(data.error).to.have.property('message', 'Not available during setup phase'); }); diff --git a/test/cypress/e2e/api/SwaggerSchema.cy.js b/test/cypress/e2e/api/SwaggerSchema.cy.js index e38684d4..0a684760 100644 --- a/test/cypress/e2e/api/SwaggerSchema.cy.js +++ b/test/cypress/e2e/api/SwaggerSchema.cy.js @@ -1,4 +1,4 @@ -/// +/// describe('Swagger Schema Checks', () => { it('Should be valid with swagger-validator', function() { diff --git a/test/cypress/e2e/api/Upstreams.cy.js b/test/cypress/e2e/api/Upstreams.cy.js index 7bad4f55..b86cd6d8 100644 --- a/test/cypress/e2e/api/Upstreams.cy.js +++ b/test/cypress/e2e/api/Upstreams.cy.js @@ -1,4 +1,4 @@ -/// +/// describe('Upstream endpoints', () => { let token; diff --git a/test/cypress/e2e/api/Users.cy.js b/test/cypress/e2e/api/Users.cy.js index 65450948..6a12fed1 100644 --- a/test/cypress/e2e/api/Users.cy.js +++ b/test/cypress/e2e/api/Users.cy.js @@ -1,4 +1,4 @@ -/// +/// describe('Users endpoints', () => { let token; @@ -82,11 +82,10 @@ describe('Users endpoints', () => { path: '/api/users', data: { name: 'Example user 1', - nickname: 'User1', email: uniqueEmail, is_disabled: false, auth: { - type: 'password', + type: 'local', secret: 'changeme' }, capabilities: [ @@ -108,11 +107,10 @@ describe('Users endpoints', () => { returnOnError: true, data: { name: 'Example user 2', - nickname: 'User2', email: uniqueEmail, is_disabled: false, auth: { - type: 'password', + type: 'local', secret: 'changeme' }, capabilities: [ diff --git a/test/cypress/e2e/ui/SetupLogin.cy.js b/test/cypress/e2e/ui/SetupLogin.cy.js index 9545220f..a60fa85a 100644 --- a/test/cypress/e2e/ui/SetupLogin.cy.js +++ b/test/cypress/e2e/ui/SetupLogin.cy.js @@ -1,4 +1,4 @@ -/// +/// describe('UI Setup and Login', () => { @@ -10,7 +10,6 @@ describe('UI Setup and Login', () => { it('Should be able to setup a new user', function() { cy.visit('/'); cy.get('input[name="name"]').type('Cypress McGee'); - cy.get('input[name="nickname"]').type('Cypress'); cy.get('input[name="email"]').type('cypress@example.com'); cy.get('input[name="password"]').type('changeme'); cy.get('form button:last').click(); diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 276df817..00ea5ecd 100644 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -66,7 +66,7 @@ Cypress.Commands.add('getToken', (defaultUser, defaultAuth) => { }); } else { let auth = { - type: 'password', + type: 'local', identity: 'cypress@example.com', secret: 'changeme', }; @@ -78,7 +78,7 @@ Cypress.Commands.add('getToken', (defaultUser, defaultAuth) => { cy.log('Setup = true'); // login with existing user cy.task('backendApiPost', { - path: '/api/tokens', + path: '/api/auth', data: auth, }).then((res) => { cy.wrap(res.result.token); @@ -90,11 +90,10 @@ Cypress.Commands.add('getToken', (defaultUser, defaultAuth) => { Cypress.Commands.add('createInitialUser', (defaultUser) => { let user = { name: 'Cypress McGee', - nickname: 'Cypress', email: 'cypress@example.com', is_disabled: false, auth: { - type: 'password', + type: 'local', secret: 'changeme' }, capabilities: ['full-admin']