mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-04-29 00:48:11 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 37f927a570 | |||
| 0a6737891c | |||
| 63cc081853 | |||
| f6e994d834 | |||
| 906d810baf |
@@ -5,21 +5,18 @@ on:
|
|||||||
- main
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
ci:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
|
|
||||||
- name: Setup bun
|
- name: Setup bun
|
||||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
uses: oven-sh/setup-bun@v2
|
||||||
|
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: "^1.26.0"
|
go-version: "^1.26.0"
|
||||||
|
|
||||||
@@ -53,6 +50,6 @@ jobs:
|
|||||||
run: go test -coverprofile=coverage.txt -v ./...
|
run: go test -coverprofile=coverage.txt -v ./...
|
||||||
|
|
||||||
- name: Upload coverage reports to Codecov
|
- name: Upload coverage reports to Codecov
|
||||||
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6
|
uses: codecov/codecov-action@v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|||||||
@@ -4,16 +4,12 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 * * *"
|
- cron: "0 0 * * *"
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
create-release:
|
create-release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
|
|
||||||
- name: Delete old release
|
- name: Delete old release
|
||||||
run: gh release delete --cleanup-tag --yes nightly || echo release not found
|
run: gh release delete --cleanup-tag --yes nightly || echo release not found
|
||||||
@@ -23,7 +19,7 @@ jobs:
|
|||||||
REPO: ${{ github.event.repository.name }}
|
REPO: ${{ github.event.repository.name }}
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3
|
uses: softprops/action-gh-release@v3
|
||||||
with:
|
with:
|
||||||
prerelease: true
|
prerelease: true
|
||||||
tag_name: nightly
|
tag_name: nightly
|
||||||
@@ -37,7 +33,7 @@ jobs:
|
|||||||
BUILD_TIMESTAMP: ${{ steps.metadata.outputs.BUILD_TIMESTAMP }}
|
BUILD_TIMESTAMP: ${{ steps.metadata.outputs.BUILD_TIMESTAMP }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
with:
|
with:
|
||||||
ref: nightly
|
ref: nightly
|
||||||
|
|
||||||
@@ -55,15 +51,15 @@ jobs:
|
|||||||
- generate-metadata
|
- generate-metadata
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
with:
|
with:
|
||||||
ref: nightly
|
ref: nightly
|
||||||
|
|
||||||
- name: Install bun
|
- name: Install bun
|
||||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
uses: oven-sh/setup-bun@v2
|
||||||
|
|
||||||
- name: Install go
|
- name: Install go
|
||||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: "^1.26.0"
|
go-version: "^1.26.0"
|
||||||
|
|
||||||
@@ -89,7 +85,7 @@ jobs:
|
|||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@v7.0.1
|
||||||
with:
|
with:
|
||||||
name: tinyauth-amd64
|
name: tinyauth-amd64
|
||||||
path: tinyauth-amd64
|
path: tinyauth-amd64
|
||||||
@@ -101,15 +97,15 @@ jobs:
|
|||||||
- generate-metadata
|
- generate-metadata
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
with:
|
with:
|
||||||
ref: nightly
|
ref: nightly
|
||||||
|
|
||||||
- name: Install bun
|
- name: Install bun
|
||||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
uses: oven-sh/setup-bun@v2
|
||||||
|
|
||||||
- name: Install go
|
- name: Install go
|
||||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: "^1.26.0"
|
go-version: "^1.26.0"
|
||||||
|
|
||||||
@@ -135,7 +131,7 @@ jobs:
|
|||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@v7.0.1
|
||||||
with:
|
with:
|
||||||
name: tinyauth-arm64
|
name: tinyauth-arm64
|
||||||
path: tinyauth-arm64
|
path: tinyauth-arm64
|
||||||
@@ -147,28 +143,28 @@ jobs:
|
|||||||
- generate-metadata
|
- generate-metadata
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
with:
|
with:
|
||||||
ref: nightly
|
ref: nightly
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
uses: docker/build-push-action@v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
@@ -190,7 +186,7 @@ jobs:
|
|||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
- name: Upload digest
|
- name: Upload digest
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@v7.0.1
|
||||||
with:
|
with:
|
||||||
name: digests-linux-amd64
|
name: digests-linux-amd64
|
||||||
path: ${{ runner.temp }}/digests/*
|
path: ${{ runner.temp }}/digests/*
|
||||||
@@ -205,28 +201,28 @@ jobs:
|
|||||||
- image-build
|
- image-build
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
with:
|
with:
|
||||||
ref: nightly
|
ref: nightly
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
uses: docker/build-push-action@v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
@@ -249,7 +245,7 @@ jobs:
|
|||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
- name: Upload digest
|
- name: Upload digest
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@v7.0.1
|
||||||
with:
|
with:
|
||||||
name: digests-distroless-linux-amd64
|
name: digests-distroless-linux-amd64
|
||||||
path: ${{ runner.temp }}/digests/*
|
path: ${{ runner.temp }}/digests/*
|
||||||
@@ -263,28 +259,28 @@ jobs:
|
|||||||
- generate-metadata
|
- generate-metadata
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
with:
|
with:
|
||||||
ref: nightly
|
ref: nightly
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
uses: docker/build-push-action@v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/arm64
|
platforms: linux/arm64
|
||||||
@@ -306,7 +302,7 @@ jobs:
|
|||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
- name: Upload digest
|
- name: Upload digest
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@v7.0.1
|
||||||
with:
|
with:
|
||||||
name: digests-linux-arm64
|
name: digests-linux-arm64
|
||||||
path: ${{ runner.temp }}/digests/*
|
path: ${{ runner.temp }}/digests/*
|
||||||
@@ -321,28 +317,28 @@ jobs:
|
|||||||
- image-build-arm
|
- image-build-arm
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
with:
|
with:
|
||||||
ref: nightly
|
ref: nightly
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
uses: docker/build-push-action@v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/arm64
|
platforms: linux/arm64
|
||||||
@@ -365,7 +361,7 @@ jobs:
|
|||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
- name: Upload digest
|
- name: Upload digest
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@v7.0.1
|
||||||
with:
|
with:
|
||||||
name: digests-distroless-linux-arm64
|
name: digests-distroless-linux-arm64
|
||||||
path: ${{ runner.temp }}/digests/*
|
path: ${{ runner.temp }}/digests/*
|
||||||
@@ -379,25 +375,25 @@ jobs:
|
|||||||
- image-build-arm
|
- image-build-arm
|
||||||
steps:
|
steps:
|
||||||
- name: Download digests
|
- name: Download digests
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
path: ${{ runner.temp }}/digests
|
path: ${{ runner.temp }}/digests
|
||||||
pattern: digests-*
|
pattern: digests-*
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
||||||
flavor: |
|
flavor: |
|
||||||
@@ -418,25 +414,25 @@ jobs:
|
|||||||
- image-build-arm-distroless
|
- image-build-arm-distroless
|
||||||
steps:
|
steps:
|
||||||
- name: Download digests
|
- name: Download digests
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
path: ${{ runner.temp }}/digests
|
path: ${{ runner.temp }}/digests
|
||||||
pattern: digests-distroless-*
|
pattern: digests-distroless-*
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
||||||
flavor: |
|
flavor: |
|
||||||
@@ -456,14 +452,14 @@ jobs:
|
|||||||
- binary-build
|
- binary-build
|
||||||
- binary-build-arm
|
- binary-build-arm
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
- uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
pattern: tinyauth-*
|
pattern: tinyauth-*
|
||||||
path: binaries
|
path: binaries
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3
|
uses: softprops/action-gh-release@v3
|
||||||
with:
|
with:
|
||||||
files: binaries/*
|
files: binaries/*
|
||||||
tag_name: nightly
|
tag_name: nightly
|
||||||
|
|||||||
@@ -5,10 +5,6 @@ on:
|
|||||||
tags:
|
tags:
|
||||||
- "v*"
|
- "v*"
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
generate-metadata:
|
generate-metadata:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -18,7 +14,7 @@ jobs:
|
|||||||
BUILD_TIMESTAMP: ${{ steps.metadata.outputs.BUILD_TIMESTAMP }}
|
BUILD_TIMESTAMP: ${{ steps.metadata.outputs.BUILD_TIMESTAMP }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
|
|
||||||
- name: Generate metadata
|
- name: Generate metadata
|
||||||
id: metadata
|
id: metadata
|
||||||
@@ -33,13 +29,13 @@ jobs:
|
|||||||
- generate-metadata
|
- generate-metadata
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
|
|
||||||
- name: Install bun
|
- name: Install bun
|
||||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
uses: oven-sh/setup-bun@v2
|
||||||
|
|
||||||
- name: Install go
|
- name: Install go
|
||||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: "^1.26.0"
|
go-version: "^1.26.0"
|
||||||
|
|
||||||
@@ -65,7 +61,7 @@ jobs:
|
|||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@v7.0.1
|
||||||
with:
|
with:
|
||||||
name: tinyauth-amd64
|
name: tinyauth-amd64
|
||||||
path: tinyauth-amd64
|
path: tinyauth-amd64
|
||||||
@@ -76,13 +72,13 @@ jobs:
|
|||||||
- generate-metadata
|
- generate-metadata
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
|
|
||||||
- name: Install bun
|
- name: Install bun
|
||||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
uses: oven-sh/setup-bun@v2
|
||||||
|
|
||||||
- name: Install go
|
- name: Install go
|
||||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: "^1.26.0"
|
go-version: "^1.26.0"
|
||||||
|
|
||||||
@@ -108,7 +104,7 @@ jobs:
|
|||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@v7.0.1
|
||||||
with:
|
with:
|
||||||
name: tinyauth-arm64
|
name: tinyauth-arm64
|
||||||
path: tinyauth-arm64
|
path: tinyauth-arm64
|
||||||
@@ -119,26 +115,26 @@ jobs:
|
|||||||
- generate-metadata
|
- generate-metadata
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
uses: docker/build-push-action@v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
@@ -160,7 +156,7 @@ jobs:
|
|||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
- name: Upload digest
|
- name: Upload digest
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@v7.0.1
|
||||||
with:
|
with:
|
||||||
name: digests-linux-amd64
|
name: digests-linux-amd64
|
||||||
path: ${{ runner.temp }}/digests/*
|
path: ${{ runner.temp }}/digests/*
|
||||||
@@ -174,26 +170,26 @@ jobs:
|
|||||||
- image-build
|
- image-build
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
uses: docker/build-push-action@v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
@@ -216,7 +212,7 @@ jobs:
|
|||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
- name: Upload digest
|
- name: Upload digest
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@v7.0.1
|
||||||
with:
|
with:
|
||||||
name: digests-distroless-linux-amd64
|
name: digests-distroless-linux-amd64
|
||||||
path: ${{ runner.temp }}/digests/*
|
path: ${{ runner.temp }}/digests/*
|
||||||
@@ -229,26 +225,26 @@ jobs:
|
|||||||
- generate-metadata
|
- generate-metadata
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
uses: docker/build-push-action@v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/arm64
|
platforms: linux/arm64
|
||||||
@@ -270,7 +266,7 @@ jobs:
|
|||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
- name: Upload digest
|
- name: Upload digest
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@v7.0.1
|
||||||
with:
|
with:
|
||||||
name: digests-linux-arm64
|
name: digests-linux-arm64
|
||||||
path: ${{ runner.temp }}/digests/*
|
path: ${{ runner.temp }}/digests/*
|
||||||
@@ -284,26 +280,26 @@ jobs:
|
|||||||
- image-build-arm
|
- image-build-arm
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
uses: docker/build-push-action@v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/arm64
|
platforms: linux/arm64
|
||||||
@@ -326,7 +322,7 @@ jobs:
|
|||||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||||
|
|
||||||
- name: Upload digest
|
- name: Upload digest
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@v7.0.1
|
||||||
with:
|
with:
|
||||||
name: digests-distroless-linux-arm64
|
name: digests-distroless-linux-arm64
|
||||||
path: ${{ runner.temp }}/digests/*
|
path: ${{ runner.temp }}/digests/*
|
||||||
@@ -340,25 +336,25 @@ jobs:
|
|||||||
- image-build-arm
|
- image-build-arm
|
||||||
steps:
|
steps:
|
||||||
- name: Download digests
|
- name: Download digests
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
path: ${{ runner.temp }}/digests
|
path: ${{ runner.temp }}/digests
|
||||||
pattern: digests-*
|
pattern: digests-*
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
||||||
flavor: |
|
flavor: |
|
||||||
@@ -381,25 +377,25 @@ jobs:
|
|||||||
- image-build-arm-distroless
|
- image-build-arm-distroless
|
||||||
steps:
|
steps:
|
||||||
- name: Download digests
|
- name: Download digests
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
path: ${{ runner.temp }}/digests
|
path: ${{ runner.temp }}/digests
|
||||||
pattern: digests-distroless-*
|
pattern: digests-distroless-*
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
||||||
flavor: |
|
flavor: |
|
||||||
@@ -423,13 +419,13 @@ jobs:
|
|||||||
- binary-build
|
- binary-build
|
||||||
- binary-build-arm
|
- binary-build-arm
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
- uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
pattern: tinyauth-*
|
pattern: tinyauth-*
|
||||||
path: binaries
|
path: binaries
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3
|
uses: softprops/action-gh-release@v3
|
||||||
with:
|
with:
|
||||||
files: binaries/*
|
files: binaries/*
|
||||||
|
|||||||
@@ -38,6 +38,6 @@ jobs:
|
|||||||
retention-days: 5
|
retention-days: 5
|
||||||
|
|
||||||
- name: Upload to code-scanning
|
- name: Upload to code-scanning
|
||||||
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
|
uses: github/codeql-action/upload-sarif@v4
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
|||||||
@@ -2,19 +2,15 @@ name: Generate Sponsors List
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
generate-sponsors:
|
generate-sponsors:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@v6.0.2
|
||||||
|
|
||||||
- name: Generate Sponsors
|
- name: Generate Sponsors
|
||||||
uses: JamesIves/github-sponsors-readme-action@2fd9142e765f755780202122261dc85e78459405 # v1
|
uses: JamesIves/github-sponsors-readme-action@v1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.SPONSORS_GENERATOR_PAT }}
|
token: ${{ secrets.SPONSORS_GENERATOR_PAT }}
|
||||||
active-only: false
|
active-only: false
|
||||||
@@ -22,7 +18,7 @@ jobs:
|
|||||||
template: '<a href="https://github.com/{{{ login }}}"><img src="{{{ avatarUrl }}}" width="64px" alt="User avatar: {{{ login }}}" /></a> '
|
template: '<a href="https://github.com/{{{ login }}}"><img src="{{{ avatarUrl }}}" width="64px" alt="User avatar: {{{ login }}}" /></a> '
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8
|
uses: peter-evans/create-pull-request@v8
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
commit-message: |
|
commit-message: |
|
||||||
|
|||||||
@@ -3,15 +3,11 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: 0 10 * * *
|
- cron: 0 10 * * *
|
||||||
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10
|
- uses: actions/stale@v10
|
||||||
with:
|
with:
|
||||||
days-before-stale: 30
|
days-before-stale: 30
|
||||||
stale-pr-message: This PR has been inactive for 30 days and will be marked as stale.
|
stale-pr-message: This PR has been inactive for 30 days and will be marked as stale.
|
||||||
|
|||||||
@@ -80,9 +80,5 @@
|
|||||||
"profileScopeDescription": "Allows the app to access your profile information.",
|
"profileScopeDescription": "Allows the app to access your profile information.",
|
||||||
"groupsScopeName": "Groups",
|
"groupsScopeName": "Groups",
|
||||||
"groupsScopeDescription": "Allows the app to access your group information.",
|
"groupsScopeDescription": "Allows the app to access your group information.",
|
||||||
"backToLoginButton": "Back to login",
|
"backToLoginButton": "Back to login"
|
||||||
"phoneScopeName": "Phone",
|
|
||||||
"phoneScopeDescription": "Allows the app to access your phone number.",
|
|
||||||
"addressScopeName": "Address",
|
|
||||||
"addressScopeDescription": "Allows the app to access your address."
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { toast } from "sonner";
|
|||||||
import { useOIDCParams } from "@/lib/hooks/oidc";
|
import { useOIDCParams } from "@/lib/hooks/oidc";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { TFunction } from "i18next";
|
import { TFunction } from "i18next";
|
||||||
import { Mail, MapPin, Phone, Shield, User, Users } from "lucide-react";
|
import { Mail, Shield, User, Users } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
@@ -61,18 +61,6 @@ const createScopeMap = (t: TFunction<"translation", undefined>): Scope[] => {
|
|||||||
description: t("groupsScopeDescription"),
|
description: t("groupsScopeDescription"),
|
||||||
icon: <Users {...scopeMapIconProps} />,
|
icon: <Users {...scopeMapIconProps} />,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: "phone",
|
|
||||||
name: t("phoneScopeName"),
|
|
||||||
description: t("phoneScopeDescription"),
|
|
||||||
icon: <Phone {...scopeMapIconProps} />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "address",
|
|
||||||
name: t("addressScopeName"),
|
|
||||||
description: t("addressScopeDescription"),
|
|
||||||
icon: <MapPin {...scopeMapIconProps} />,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
ALTER TABLE "oidc_userinfo" DROP COLUMN "given_name";
|
|
||||||
ALTER TABLE "oidc_userinfo" DROP COLUMN "family_name";
|
|
||||||
ALTER TABLE "oidc_userinfo" DROP COLUMN "middle_name";
|
|
||||||
ALTER TABLE "oidc_userinfo" DROP COLUMN "nickname";
|
|
||||||
ALTER TABLE "oidc_userinfo" DROP COLUMN "profile";
|
|
||||||
ALTER TABLE "oidc_userinfo" DROP COLUMN "picture";
|
|
||||||
ALTER TABLE "oidc_userinfo" DROP COLUMN "website";
|
|
||||||
ALTER TABLE "oidc_userinfo" DROP COLUMN "gender";
|
|
||||||
ALTER TABLE "oidc_userinfo" DROP COLUMN "birthdate";
|
|
||||||
ALTER TABLE "oidc_userinfo" DROP COLUMN "zoneinfo";
|
|
||||||
ALTER TABLE "oidc_userinfo" DROP COLUMN "locale";
|
|
||||||
ALTER TABLE "oidc_userinfo" DROP COLUMN "phone_number";
|
|
||||||
ALTER TABLE "oidc_userinfo" DROP COLUMN "address";
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
ALTER TABLE "oidc_userinfo" ADD COLUMN "given_name" TEXT NOT NULL DEFAULT "";
|
|
||||||
ALTER TABLE "oidc_userinfo" ADD COLUMN "family_name" TEXT NOT NULL DEFAULT "";
|
|
||||||
ALTER TABLE "oidc_userinfo" ADD COLUMN "middle_name" TEXT NOT NULL DEFAULT "";
|
|
||||||
ALTER TABLE "oidc_userinfo" ADD COLUMN "nickname" TEXT NOT NULL DEFAULT "";
|
|
||||||
ALTER TABLE "oidc_userinfo" ADD COLUMN "profile" TEXT NOT NULL DEFAULT "";
|
|
||||||
ALTER TABLE "oidc_userinfo" ADD COLUMN "picture" TEXT NOT NULL DEFAULT "";
|
|
||||||
ALTER TABLE "oidc_userinfo" ADD COLUMN "website" TEXT NOT NULL DEFAULT "";
|
|
||||||
ALTER TABLE "oidc_userinfo" ADD COLUMN "gender" TEXT NOT NULL DEFAULT "";
|
|
||||||
ALTER TABLE "oidc_userinfo" ADD COLUMN "birthdate" TEXT NOT NULL DEFAULT "";
|
|
||||||
ALTER TABLE "oidc_userinfo" ADD COLUMN "zoneinfo" TEXT NOT NULL DEFAULT "";
|
|
||||||
ALTER TABLE "oidc_userinfo" ADD COLUMN "locale" TEXT NOT NULL DEFAULT "";
|
|
||||||
ALTER TABLE "oidc_userinfo" ADD COLUMN "phone_number" TEXT NOT NULL DEFAULT "";
|
|
||||||
ALTER TABLE "oidc_userinfo" ADD COLUMN "address" TEXT NOT NULL DEFAULT "{}";
|
|
||||||
@@ -63,7 +63,7 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse users
|
// Parse users
|
||||||
users, err := utils.GetUsers(app.config.Auth.Users, app.config.Auth.UsersFile, app.config.Auth.UserAttributes)
|
users, err := utils.GetUsers(app.config.Auth.Users, app.config.Auth.UsersFile)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -113,43 +113,15 @@ type ServerConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AuthConfig struct {
|
type AuthConfig struct {
|
||||||
IP IPConfig `description:"IP whitelisting config options." yaml:"ip"`
|
IP IPConfig `description:"IP whitelisting config options." yaml:"ip"`
|
||||||
Users []string `description:"Comma-separated list of users (username:hashed_password)." yaml:"users"`
|
Users []string `description:"Comma-separated list of users (username:hashed_password)." yaml:"users"`
|
||||||
UserAttributes map[string]UserAttributes `description:"Map of per-user OIDC attributes (username -> attributes)." yaml:"userAttributes"`
|
UsersFile string `description:"Path to the users file." yaml:"usersFile"`
|
||||||
UsersFile string `description:"Path to the users file." yaml:"usersFile"`
|
SecureCookie bool `description:"Enable secure cookies." yaml:"secureCookie"`
|
||||||
SecureCookie bool `description:"Enable secure cookies." yaml:"secureCookie"`
|
SessionExpiry int `description:"Session expiry time in seconds." yaml:"sessionExpiry"`
|
||||||
SessionExpiry int `description:"Session expiry time in seconds." yaml:"sessionExpiry"`
|
SessionMaxLifetime int `description:"Maximum session lifetime in seconds." yaml:"sessionMaxLifetime"`
|
||||||
SessionMaxLifetime int `description:"Maximum session lifetime in seconds." yaml:"sessionMaxLifetime"`
|
LoginTimeout int `description:"Login timeout in seconds." yaml:"loginTimeout"`
|
||||||
LoginTimeout int `description:"Login timeout in seconds." yaml:"loginTimeout"`
|
LoginMaxRetries int `description:"Maximum login retries." yaml:"loginMaxRetries"`
|
||||||
LoginMaxRetries int `description:"Maximum login retries." yaml:"loginMaxRetries"`
|
TrustedProxies []string `description:"Comma-separated list of trusted proxy addresses." yaml:"trustedProxies"`
|
||||||
TrustedProxies []string `description:"Comma-separated list of trusted proxy addresses." yaml:"trustedProxies"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserAttributes struct {
|
|
||||||
Name string `description:"Full name of the user." yaml:"name"`
|
|
||||||
GivenName string `description:"Given (first) name of the user." yaml:"givenName"`
|
|
||||||
FamilyName string `description:"Family (last) name of the user." yaml:"familyName"`
|
|
||||||
MiddleName string `description:"Middle name of the user." yaml:"middleName"`
|
|
||||||
Nickname string `description:"Nickname of the user." yaml:"nickname"`
|
|
||||||
Profile string `description:"URL of the user's profile page." yaml:"profile"`
|
|
||||||
Picture string `description:"URL of the user's profile picture." yaml:"picture"`
|
|
||||||
Website string `description:"URL of the user's website." yaml:"website"`
|
|
||||||
Email string `description:"Email address of the user." yaml:"email"`
|
|
||||||
Gender string `description:"Gender of the user." yaml:"gender"`
|
|
||||||
Birthdate string `description:"Birthdate of the user (YYYY-MM-DD)." yaml:"birthdate"`
|
|
||||||
Zoneinfo string `description:"Time zone of the user (e.g. Europe/Athens)." yaml:"zoneinfo"`
|
|
||||||
Locale string `description:"Locale of the user (e.g. en-US)." yaml:"locale"`
|
|
||||||
PhoneNumber string `description:"Phone number of the user." yaml:"phoneNumber"`
|
|
||||||
Address AddressClaim `description:"Address of the user." yaml:"address"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AddressClaim struct {
|
|
||||||
Formatted string `description:"Full mailing address, formatted for display." yaml:"formatted" json:"formatted,omitempty"`
|
|
||||||
StreetAddress string `description:"Street address." yaml:"streetAddress" json:"street_address,omitempty"`
|
|
||||||
Locality string `description:"City or locality." yaml:"locality" json:"locality,omitempty"`
|
|
||||||
Region string `description:"State, province, or region." yaml:"region" json:"region,omitempty"`
|
|
||||||
PostalCode string `description:"Zip or postal code." yaml:"postalCode" json:"postal_code,omitempty"`
|
|
||||||
Country string `description:"Country." yaml:"country" json:"country,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type IPConfig struct {
|
type IPConfig struct {
|
||||||
@@ -256,7 +228,6 @@ type User struct {
|
|||||||
Username string
|
Username string
|
||||||
Password string
|
Password string
|
||||||
TotpSecret string
|
TotpSecret string
|
||||||
Attributes UserAttributes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type LdapUser struct {
|
type LdapUser struct {
|
||||||
@@ -283,7 +254,6 @@ type UserContext struct {
|
|||||||
OAuthName string
|
OAuthName string
|
||||||
OAuthSub string
|
OAuthSub string
|
||||||
LdapGroups string
|
LdapGroups string
|
||||||
Attributes UserAttributes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// API responses and queries
|
// API responses and queries
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/config"
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/repository"
|
"github.com/tinyauthapp/tinyauth/internal/repository"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils"
|
"github.com/tinyauthapp/tinyauth/internal/utils"
|
||||||
@@ -106,32 +105,16 @@ func (controller *UserController) loginHandler(c *gin.Context) {
|
|||||||
|
|
||||||
controller.auth.RecordLoginAttempt(req.Username, true)
|
controller.auth.RecordLoginAttempt(req.Username, true)
|
||||||
|
|
||||||
var localUser *config.User
|
|
||||||
if userSearch.Type == "local" {
|
if userSearch.Type == "local" {
|
||||||
user := controller.auth.GetLocalUser(userSearch.Username)
|
user := controller.auth.GetLocalUser(userSearch.Username)
|
||||||
localUser = &user
|
|
||||||
}
|
|
||||||
|
|
||||||
if userSearch.Type == "local" && localUser != nil {
|
|
||||||
user := *localUser
|
|
||||||
|
|
||||||
if user.TotpSecret != "" {
|
if user.TotpSecret != "" {
|
||||||
tlog.App.Debug().Str("username", req.Username).Msg("User has TOTP enabled, requiring TOTP verification")
|
tlog.App.Debug().Str("username", req.Username).Msg("User has TOTP enabled, requiring TOTP verification")
|
||||||
|
|
||||||
name := user.Attributes.Name
|
|
||||||
if name == "" {
|
|
||||||
name = utils.Capitalize(user.Username)
|
|
||||||
}
|
|
||||||
|
|
||||||
email := user.Attributes.Email
|
|
||||||
if email == "" {
|
|
||||||
email = utils.CompileUserEmail(user.Username, controller.config.CookieDomain)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := controller.auth.CreateSessionCookie(c, &repository.Session{
|
err := controller.auth.CreateSessionCookie(c, &repository.Session{
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
Name: name,
|
Name: utils.Capitalize(user.Username),
|
||||||
Email: email,
|
Email: utils.CompileUserEmail(user.Username, controller.config.CookieDomain),
|
||||||
Provider: "local",
|
Provider: "local",
|
||||||
TotpPending: true,
|
TotpPending: true,
|
||||||
})
|
})
|
||||||
@@ -161,15 +144,6 @@ func (controller *UserController) loginHandler(c *gin.Context) {
|
|||||||
Provider: "local",
|
Provider: "local",
|
||||||
}
|
}
|
||||||
|
|
||||||
if userSearch.Type == "local" && localUser != nil {
|
|
||||||
if localUser.Attributes.Name != "" {
|
|
||||||
sessionCookie.Name = localUser.Attributes.Name
|
|
||||||
}
|
|
||||||
if localUser.Attributes.Email != "" {
|
|
||||||
sessionCookie.Email = localUser.Attributes.Email
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if userSearch.Type == "ldap" {
|
if userSearch.Type == "ldap" {
|
||||||
sessionCookie.Provider = "ldap"
|
sessionCookie.Provider = "ldap"
|
||||||
}
|
}
|
||||||
@@ -284,13 +258,6 @@ func (controller *UserController) totpHandler(c *gin.Context) {
|
|||||||
Provider: "local",
|
Provider: "local",
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.Attributes.Name != "" {
|
|
||||||
sessionCookie.Name = user.Attributes.Name
|
|
||||||
}
|
|
||||||
if user.Attributes.Email != "" {
|
|
||||||
sessionCookie.Email = user.Attributes.Email
|
|
||||||
}
|
|
||||||
|
|
||||||
tlog.App.Trace().Interface("session_cookie", sessionCookie).Msg("Creating session cookie")
|
tlog.App.Trace().Interface("session_cookie", sessionCookie).Msg("Creating session cookie")
|
||||||
|
|
||||||
err = controller.auth.CreateSessionCookie(c, &sessionCookie)
|
err = controller.auth.CreateSessionCookie(c, &sessionCookie)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"path"
|
"path"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -35,23 +36,6 @@ func TestUserController(t *testing.T) {
|
|||||||
Password: "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa", // password
|
Password: "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa", // password
|
||||||
TotpSecret: "JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK",
|
TotpSecret: "JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Username: "attruser",
|
|
||||||
Password: "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa", // password
|
|
||||||
Attributes: config.UserAttributes{
|
|
||||||
Name: "Alice Smith",
|
|
||||||
Email: "alice@example.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Username: "attrtotpuser",
|
|
||||||
Password: "$2a$10$ZwVYQH07JX2zq7Fjkt3gU.BjwvvwPeli4OqOno04RQIv0P7usBrXa", // password
|
|
||||||
TotpSecret: "JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK",
|
|
||||||
Attributes: config.UserAttributes{
|
|
||||||
Name: "Bob Jones",
|
|
||||||
Email: "bob@example.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
SessionExpiry: 10, // 10 seconds, useful for testing
|
SessionExpiry: 10, // 10 seconds, useful for testing
|
||||||
CookieDomain: "example.com",
|
CookieDomain: "example.com",
|
||||||
@@ -289,64 +273,6 @@ func TestUserController(t *testing.T) {
|
|||||||
assert.Contains(t, recorder.Body.String(), "Too many failed TOTP attempts.")
|
assert.Contains(t, recorder.Body.String(), "Too many failed TOTP attempts.")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: "Login uses name and email from user attributes",
|
|
||||||
middlewares: []gin.HandlerFunc{},
|
|
||||||
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
|
||||||
loginReq := controller.LoginRequest{Username: "attruser", Password: "password"}
|
|
||||||
body, err := json.Marshal(loginReq)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
req := httptest.NewRequest("POST", "/api/user/login", strings.NewReader(string(body)))
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
router.ServeHTTP(recorder, req)
|
|
||||||
|
|
||||||
require.Equal(t, 200, recorder.Code)
|
|
||||||
cookies := recorder.Result().Cookies()
|
|
||||||
require.Len(t, cookies, 1)
|
|
||||||
assert.Equal(t, "tinyauth-session", cookies[0].Name)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Login with TOTP uses name and email from user attributes in pending session",
|
|
||||||
middlewares: []gin.HandlerFunc{},
|
|
||||||
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
|
||||||
loginReq := controller.LoginRequest{Username: "attrtotpuser", Password: "password"}
|
|
||||||
body, err := json.Marshal(loginReq)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
req := httptest.NewRequest("POST", "/api/user/login", strings.NewReader(string(body)))
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
router.ServeHTTP(recorder, req)
|
|
||||||
|
|
||||||
require.Equal(t, 200, recorder.Code)
|
|
||||||
var res map[string]any
|
|
||||||
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &res))
|
|
||||||
assert.Equal(t, true, res["totpPending"])
|
|
||||||
require.Len(t, recorder.Result().Cookies(), 1)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "TOTP completion uses name and email from user attributes",
|
|
||||||
middlewares: []gin.HandlerFunc{},
|
|
||||||
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
|
||||||
code, err := totp.GenerateCode("JPIEBDKJH6UGWJMX66RR3S55UFP2SGKK", time.Now())
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
totpReq := controller.TotpRequest{Code: code}
|
|
||||||
body, err := json.Marshal(totpReq)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
req := httptest.NewRequest("POST", "/api/user/totp", strings.NewReader(string(body)))
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
router.ServeHTTP(recorder, req)
|
|
||||||
|
|
||||||
require.Equal(t, 200, recorder.Code)
|
|
||||||
cookies := recorder.Result().Cookies()
|
|
||||||
require.Len(t, cookies, 1)
|
|
||||||
assert.Equal(t, "tinyauth-session", cookies[0].Name)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
oauthBrokerCfgs := make(map[string]config.OAuthServiceConfig)
|
oauthBrokerCfgs := make(map[string]config.OAuthServiceConfig)
|
||||||
@@ -379,31 +305,9 @@ func TestUserController(t *testing.T) {
|
|||||||
authService.ClearRateLimitsTestingOnly()
|
authService.ClearRateLimitsTestingOnly()
|
||||||
}
|
}
|
||||||
|
|
||||||
setTotpMiddlewareOverrides := map[string]config.UserContext{
|
setTotpMiddlewareOverrides := []string{
|
||||||
"Should be able to login with totp": {
|
"Should be able to login with totp",
|
||||||
Username: "totpuser",
|
"Totp should rate limit on multiple invalid attempts",
|
||||||
Name: "Totpuser",
|
|
||||||
Email: "totpuser@example.com",
|
|
||||||
Provider: "local",
|
|
||||||
TotpPending: true,
|
|
||||||
TotpEnabled: true,
|
|
||||||
},
|
|
||||||
"Totp should rate limit on multiple invalid attempts": {
|
|
||||||
Username: "totpuser",
|
|
||||||
Name: "Totpuser",
|
|
||||||
Email: "totpuser@example.com",
|
|
||||||
Provider: "local",
|
|
||||||
TotpPending: true,
|
|
||||||
TotpEnabled: true,
|
|
||||||
},
|
|
||||||
"TOTP completion uses name and email from user attributes": {
|
|
||||||
Username: "attrtotpuser",
|
|
||||||
Name: "Bob Jones",
|
|
||||||
Email: "bob@example.com",
|
|
||||||
Provider: "local",
|
|
||||||
TotpPending: true,
|
|
||||||
TotpEnabled: true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@@ -417,10 +321,18 @@ func TestUserController(t *testing.T) {
|
|||||||
|
|
||||||
// Gin is stupid and doesn't allow setting a middleware after the groups
|
// Gin is stupid and doesn't allow setting a middleware after the groups
|
||||||
// so we need to do some stupid overrides here
|
// so we need to do some stupid overrides here
|
||||||
if ctx, ok := setTotpMiddlewareOverrides[test.description]; ok {
|
if slices.Contains(setTotpMiddlewareOverrides, test.description) {
|
||||||
ctx := ctx
|
// Assuming the cookie is set, it should be picked up by the
|
||||||
|
// context middleware
|
||||||
router.Use(func(c *gin.Context) {
|
router.Use(func(c *gin.Context) {
|
||||||
c.Set("context", &ctx)
|
c.Set("context", &config.UserContext{
|
||||||
|
Username: "totpuser",
|
||||||
|
Name: "Totpuser",
|
||||||
|
Email: "totpuser@example.com",
|
||||||
|
Provider: "local",
|
||||||
|
TotpPending: true,
|
||||||
|
TotpEnabled: true,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ func (controller *WellKnownController) OpenIDConnectConfiguration(c *gin.Context
|
|||||||
SubjectTypesSupported: []string{"pairwise"},
|
SubjectTypesSupported: []string{"pairwise"},
|
||||||
IDTokenSigningAlgValuesSupported: []string{"RS256"},
|
IDTokenSigningAlgValuesSupported: []string{"RS256"},
|
||||||
TokenEndpointAuthMethodsSupported: []string{"client_secret_basic", "client_secret_post"},
|
TokenEndpointAuthMethodsSupported: []string{"client_secret_basic", "client_secret_post"},
|
||||||
ClaimsSupported: []string{"sub", "updated_at", "name", "preferred_username", "email", "email_verified", "groups", "phone_number", "phone_number_verified", "address", "given_name", "family_name", "middle_name", "nickname", "profile", "picture", "website", "gender", "birthdate", "zoneinfo", "locale"},
|
ClaimsSupported: []string{"sub", "updated_at", "name", "preferred_username", "email", "email_verified", "groups"},
|
||||||
ServiceDocumentation: "https://tinyauth.app/docs/guides/oidc",
|
ServiceDocumentation: "https://tinyauth.app/docs/guides/oidc",
|
||||||
RequestParameterSupported: true,
|
RequestParameterSupported: true,
|
||||||
RequestObjectSigningAlgValuesSupported: []string{"none"},
|
RequestObjectSigningAlgValuesSupported: []string{"none"},
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ func TestWellKnownController(t *testing.T) {
|
|||||||
SubjectTypesSupported: []string{"pairwise"},
|
SubjectTypesSupported: []string{"pairwise"},
|
||||||
IDTokenSigningAlgValuesSupported: []string{"RS256"},
|
IDTokenSigningAlgValuesSupported: []string{"RS256"},
|
||||||
TokenEndpointAuthMethodsSupported: []string{"client_secret_basic", "client_secret_post"},
|
TokenEndpointAuthMethodsSupported: []string{"client_secret_basic", "client_secret_post"},
|
||||||
ClaimsSupported: []string{"sub", "updated_at", "name", "preferred_username", "email", "email_verified", "groups", "phone_number", "phone_number_verified", "address", "given_name", "family_name", "middle_name", "nickname", "profile", "picture", "website", "gender", "birthdate", "zoneinfo", "locale"},
|
ClaimsSupported: []string{"sub", "updated_at", "name", "preferred_username", "email", "email_verified", "groups"},
|
||||||
ServiceDocumentation: "https://tinyauth.app/docs/guides/oidc",
|
ServiceDocumentation: "https://tinyauth.app/docs/guides/oidc",
|
||||||
RequestParameterSupported: true,
|
RequestParameterSupported: true,
|
||||||
RequestObjectSigningAlgValuesSupported: []string{"none"},
|
RequestObjectSigningAlgValuesSupported: []string{"none"},
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ func (m *ContextMiddleware) Middleware() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ldapGroups []string
|
var ldapGroups []string
|
||||||
var localAttributes config.UserAttributes
|
|
||||||
|
|
||||||
if cookie.Provider == "ldap" {
|
if cookie.Provider == "ldap" {
|
||||||
ldapUser, err := m.auth.GetLdapUser(userSearch.Username)
|
ldapUser, err := m.auth.GetLdapUser(userSearch.Username)
|
||||||
@@ -113,11 +112,6 @@ func (m *ContextMiddleware) Middleware() gin.HandlerFunc {
|
|||||||
ldapGroups = ldapUser.Groups
|
ldapGroups = ldapUser.Groups
|
||||||
}
|
}
|
||||||
|
|
||||||
if cookie.Provider == "local" {
|
|
||||||
localUser := m.auth.GetLocalUser(cookie.Username)
|
|
||||||
localAttributes = localUser.Attributes
|
|
||||||
}
|
|
||||||
|
|
||||||
m.auth.RefreshSessionCookie(c)
|
m.auth.RefreshSessionCookie(c)
|
||||||
c.Set("context", &config.UserContext{
|
c.Set("context", &config.UserContext{
|
||||||
Username: cookie.Username,
|
Username: cookie.Username,
|
||||||
@@ -126,7 +120,6 @@ func (m *ContextMiddleware) Middleware() gin.HandlerFunc {
|
|||||||
Provider: cookie.Provider,
|
Provider: cookie.Provider,
|
||||||
IsLoggedIn: true,
|
IsLoggedIn: true,
|
||||||
LdapGroups: strings.Join(ldapGroups, ","),
|
LdapGroups: strings.Join(ldapGroups, ","),
|
||||||
Attributes: localAttributes,
|
|
||||||
})
|
})
|
||||||
c.Next()
|
c.Next()
|
||||||
return
|
return
|
||||||
@@ -209,23 +202,13 @@ func (m *ContextMiddleware) Middleware() gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
name := utils.Capitalize(user.Username)
|
|
||||||
if user.Attributes.Name != "" {
|
|
||||||
name = user.Attributes.Name
|
|
||||||
}
|
|
||||||
email := utils.CompileUserEmail(user.Username, m.config.CookieDomain)
|
|
||||||
if user.Attributes.Email != "" {
|
|
||||||
email = user.Attributes.Email
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Set("context", &config.UserContext{
|
c.Set("context", &config.UserContext{
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
Name: name,
|
Name: utils.Capitalize(user.Username),
|
||||||
Email: email,
|
Email: utils.CompileUserEmail(user.Username, m.config.CookieDomain),
|
||||||
Provider: "local",
|
Provider: "local",
|
||||||
IsLoggedIn: true,
|
IsLoggedIn: true,
|
||||||
IsBasicAuth: true,
|
IsBasicAuth: true,
|
||||||
Attributes: user.Attributes,
|
|
||||||
})
|
})
|
||||||
c.Next()
|
c.Next()
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -34,19 +34,6 @@ type OidcUserinfo struct {
|
|||||||
Email string
|
Email string
|
||||||
Groups string
|
Groups string
|
||||||
UpdatedAt int64
|
UpdatedAt int64
|
||||||
GivenName string
|
|
||||||
FamilyName string
|
|
||||||
MiddleName string
|
|
||||||
Nickname string
|
|
||||||
Profile string
|
|
||||||
Picture string
|
|
||||||
Website string
|
|
||||||
Gender string
|
|
||||||
Birthdate string
|
|
||||||
Zoneinfo string
|
|
||||||
Locale string
|
|
||||||
PhoneNumber string
|
|
||||||
Address string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
|
|||||||
@@ -124,24 +124,11 @@ INSERT INTO "oidc_userinfo" (
|
|||||||
"preferred_username",
|
"preferred_username",
|
||||||
"email",
|
"email",
|
||||||
"groups",
|
"groups",
|
||||||
"updated_at",
|
"updated_at"
|
||||||
"given_name",
|
|
||||||
"family_name",
|
|
||||||
"middle_name",
|
|
||||||
"nickname",
|
|
||||||
"profile",
|
|
||||||
"picture",
|
|
||||||
"website",
|
|
||||||
"gender",
|
|
||||||
"birthdate",
|
|
||||||
"zoneinfo",
|
|
||||||
"locale",
|
|
||||||
"phone_number",
|
|
||||||
"address"
|
|
||||||
) VALUES (
|
) VALUES (
|
||||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
?, ?, ?, ?, ?, ?
|
||||||
)
|
)
|
||||||
RETURNING sub, name, preferred_username, email, "groups", updated_at, given_name, family_name, middle_name, nickname, profile, picture, website, gender, birthdate, zoneinfo, locale, phone_number, address
|
RETURNING sub, name, preferred_username, email, "groups", updated_at
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateOidcUserInfoParams struct {
|
type CreateOidcUserInfoParams struct {
|
||||||
@@ -151,19 +138,6 @@ type CreateOidcUserInfoParams struct {
|
|||||||
Email string
|
Email string
|
||||||
Groups string
|
Groups string
|
||||||
UpdatedAt int64
|
UpdatedAt int64
|
||||||
GivenName string
|
|
||||||
FamilyName string
|
|
||||||
MiddleName string
|
|
||||||
Nickname string
|
|
||||||
Profile string
|
|
||||||
Picture string
|
|
||||||
Website string
|
|
||||||
Gender string
|
|
||||||
Birthdate string
|
|
||||||
Zoneinfo string
|
|
||||||
Locale string
|
|
||||||
PhoneNumber string
|
|
||||||
Address string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) CreateOidcUserInfo(ctx context.Context, arg CreateOidcUserInfoParams) (OidcUserinfo, error) {
|
func (q *Queries) CreateOidcUserInfo(ctx context.Context, arg CreateOidcUserInfoParams) (OidcUserinfo, error) {
|
||||||
@@ -174,19 +148,6 @@ func (q *Queries) CreateOidcUserInfo(ctx context.Context, arg CreateOidcUserInfo
|
|||||||
arg.Email,
|
arg.Email,
|
||||||
arg.Groups,
|
arg.Groups,
|
||||||
arg.UpdatedAt,
|
arg.UpdatedAt,
|
||||||
arg.GivenName,
|
|
||||||
arg.FamilyName,
|
|
||||||
arg.MiddleName,
|
|
||||||
arg.Nickname,
|
|
||||||
arg.Profile,
|
|
||||||
arg.Picture,
|
|
||||||
arg.Website,
|
|
||||||
arg.Gender,
|
|
||||||
arg.Birthdate,
|
|
||||||
arg.Zoneinfo,
|
|
||||||
arg.Locale,
|
|
||||||
arg.PhoneNumber,
|
|
||||||
arg.Address,
|
|
||||||
)
|
)
|
||||||
var i OidcUserinfo
|
var i OidcUserinfo
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
@@ -196,19 +157,6 @@ func (q *Queries) CreateOidcUserInfo(ctx context.Context, arg CreateOidcUserInfo
|
|||||||
&i.Email,
|
&i.Email,
|
||||||
&i.Groups,
|
&i.Groups,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.GivenName,
|
|
||||||
&i.FamilyName,
|
|
||||||
&i.MiddleName,
|
|
||||||
&i.Nickname,
|
|
||||||
&i.Profile,
|
|
||||||
&i.Picture,
|
|
||||||
&i.Website,
|
|
||||||
&i.Gender,
|
|
||||||
&i.Birthdate,
|
|
||||||
&i.Zoneinfo,
|
|
||||||
&i.Locale,
|
|
||||||
&i.PhoneNumber,
|
|
||||||
&i.Address,
|
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
@@ -508,7 +456,7 @@ func (q *Queries) GetOidcTokenBySub(ctx context.Context, sub string) (OidcToken,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getOidcUserInfo = `-- name: GetOidcUserInfo :one
|
const getOidcUserInfo = `-- name: GetOidcUserInfo :one
|
||||||
SELECT sub, name, preferred_username, email, "groups", updated_at, given_name, family_name, middle_name, nickname, profile, picture, website, gender, birthdate, zoneinfo, locale, phone_number, address FROM "oidc_userinfo"
|
SELECT sub, name, preferred_username, email, "groups", updated_at FROM "oidc_userinfo"
|
||||||
WHERE "sub" = ?
|
WHERE "sub" = ?
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -522,19 +470,6 @@ func (q *Queries) GetOidcUserInfo(ctx context.Context, sub string) (OidcUserinfo
|
|||||||
&i.Email,
|
&i.Email,
|
||||||
&i.Groups,
|
&i.Groups,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
&i.GivenName,
|
|
||||||
&i.FamilyName,
|
|
||||||
&i.MiddleName,
|
|
||||||
&i.Nickname,
|
|
||||||
&i.Profile,
|
|
||||||
&i.Picture,
|
|
||||||
&i.Website,
|
|
||||||
&i.Gender,
|
|
||||||
&i.Birthdate,
|
|
||||||
&i.Zoneinfo,
|
|
||||||
&i.Locale,
|
|
||||||
&i.PhoneNumber,
|
|
||||||
&i.Address,
|
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
SupportedScopes = []string{"openid", "profile", "email", "phone", "address", "groups"}
|
SupportedScopes = []string{"openid", "profile", "email", "groups"}
|
||||||
SupportedResponseTypes = []string{"code"}
|
SupportedResponseTypes = []string{"code"}
|
||||||
SupportedGrantTypes = []string{"authorization_code", "refresh_token"}
|
SupportedGrantTypes = []string{"authorization_code", "refresh_token"}
|
||||||
)
|
)
|
||||||
@@ -48,17 +48,6 @@ type ClaimSet struct {
|
|||||||
Iat int64 `json:"iat"`
|
Iat int64 `json:"iat"`
|
||||||
Exp int64 `json:"exp"`
|
Exp int64 `json:"exp"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
GivenName string `json:"given_name,omitempty"`
|
|
||||||
FamilyName string `json:"family_name,omitempty"`
|
|
||||||
MiddleName string `json:"middle_name,omitempty"`
|
|
||||||
Nickname string `json:"nickname,omitempty"`
|
|
||||||
Profile string `json:"profile,omitempty"`
|
|
||||||
Picture string `json:"picture,omitempty"`
|
|
||||||
Website string `json:"website,omitempty"`
|
|
||||||
Gender string `json:"gender,omitempty"`
|
|
||||||
Birthdate string `json:"birthdate,omitempty"`
|
|
||||||
Zoneinfo string `json:"zoneinfo,omitempty"`
|
|
||||||
Locale string `json:"locale,omitempty"`
|
|
||||||
Email string `json:"email,omitempty"`
|
Email string `json:"email,omitempty"`
|
||||||
EmailVerified bool `json:"email_verified,omitempty"`
|
EmailVerified bool `json:"email_verified,omitempty"`
|
||||||
PreferredUsername string `json:"preferred_username,omitempty"`
|
PreferredUsername string `json:"preferred_username,omitempty"`
|
||||||
@@ -67,27 +56,13 @@ type ClaimSet struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserinfoResponse struct {
|
type UserinfoResponse struct {
|
||||||
Sub string `json:"sub"`
|
Sub string `json:"sub"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
GivenName string `json:"given_name,omitempty"`
|
Email string `json:"email,omitempty"`
|
||||||
FamilyName string `json:"family_name,omitempty"`
|
PreferredUsername string `json:"preferred_username,omitempty"`
|
||||||
MiddleName string `json:"middle_name,omitempty"`
|
Groups []string `json:"groups,omitempty"`
|
||||||
Nickname string `json:"nickname,omitempty"`
|
EmailVerified bool `json:"email_verified,omitempty"`
|
||||||
Profile string `json:"profile,omitempty"`
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
Picture string `json:"picture,omitempty"`
|
|
||||||
Website string `json:"website,omitempty"`
|
|
||||||
Gender string `json:"gender,omitempty"`
|
|
||||||
Birthdate string `json:"birthdate,omitempty"`
|
|
||||||
Zoneinfo string `json:"zoneinfo,omitempty"`
|
|
||||||
Locale string `json:"locale,omitempty"`
|
|
||||||
Email string `json:"email,omitempty"`
|
|
||||||
PreferredUsername string `json:"preferred_username,omitempty"`
|
|
||||||
Groups []string `json:"groups,omitempty"`
|
|
||||||
EmailVerified bool `json:"email_verified,omitempty"`
|
|
||||||
PhoneNumber string `json:"phone_number,omitempty"`
|
|
||||||
PhoneNumberVerified *bool `json:"phone_number_verified,omitempty"`
|
|
||||||
Address *config.AddressClaim `json:"address,omitempty"`
|
|
||||||
UpdatedAt int64 `json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TokenResponse struct {
|
type TokenResponse struct {
|
||||||
@@ -367,30 +342,12 @@ func (service *OIDCService) StoreCode(c *gin.Context, sub string, code string, r
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (service *OIDCService) StoreUserinfo(c *gin.Context, sub string, userContext config.UserContext, req AuthorizeRequest) error {
|
func (service *OIDCService) StoreUserinfo(c *gin.Context, sub string, userContext config.UserContext, req AuthorizeRequest) error {
|
||||||
addressJSON, err := json.Marshal(userContext.Attributes.Address)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
userInfoParams := repository.CreateOidcUserInfoParams{
|
userInfoParams := repository.CreateOidcUserInfoParams{
|
||||||
Sub: sub,
|
Sub: sub,
|
||||||
Name: userContext.Name,
|
Name: userContext.Name,
|
||||||
Email: userContext.Email,
|
Email: userContext.Email,
|
||||||
PreferredUsername: userContext.Username,
|
PreferredUsername: userContext.Username,
|
||||||
UpdatedAt: time.Now().Unix(),
|
UpdatedAt: time.Now().Unix(),
|
||||||
GivenName: userContext.Attributes.GivenName,
|
|
||||||
FamilyName: userContext.Attributes.FamilyName,
|
|
||||||
MiddleName: userContext.Attributes.MiddleName,
|
|
||||||
Nickname: userContext.Attributes.Nickname,
|
|
||||||
Profile: userContext.Attributes.Profile,
|
|
||||||
Picture: userContext.Attributes.Picture,
|
|
||||||
Website: userContext.Attributes.Website,
|
|
||||||
Gender: userContext.Attributes.Gender,
|
|
||||||
Birthdate: userContext.Attributes.Birthdate,
|
|
||||||
Zoneinfo: userContext.Attributes.Zoneinfo,
|
|
||||||
Locale: userContext.Attributes.Locale,
|
|
||||||
PhoneNumber: userContext.Attributes.PhoneNumber,
|
|
||||||
Address: string(addressJSON),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tinyauth will pass through the groups it got from an LDAP or an OIDC server
|
// Tinyauth will pass through the groups it got from an LDAP or an OIDC server
|
||||||
@@ -402,7 +359,7 @@ func (service *OIDCService) StoreUserinfo(c *gin.Context, sub string, userContex
|
|||||||
userInfoParams.Groups = userContext.OAuthGroups
|
userInfoParams.Groups = userContext.OAuthGroups
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = service.queries.CreateOidcUserInfo(c, userInfoParams)
|
_, err := service.queries.CreateOidcUserInfo(c, userInfoParams)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -680,22 +637,12 @@ func (service *OIDCService) CompileUserinfo(user repository.OidcUserinfo, scope
|
|||||||
if slices.Contains(scopes, "profile") {
|
if slices.Contains(scopes, "profile") {
|
||||||
userInfo.Name = user.Name
|
userInfo.Name = user.Name
|
||||||
userInfo.PreferredUsername = user.PreferredUsername
|
userInfo.PreferredUsername = user.PreferredUsername
|
||||||
userInfo.GivenName = user.GivenName
|
|
||||||
userInfo.FamilyName = user.FamilyName
|
|
||||||
userInfo.MiddleName = user.MiddleName
|
|
||||||
userInfo.Nickname = user.Nickname
|
|
||||||
userInfo.Profile = user.Profile
|
|
||||||
userInfo.Picture = user.Picture
|
|
||||||
userInfo.Website = user.Website
|
|
||||||
userInfo.Gender = user.Gender
|
|
||||||
userInfo.Birthdate = user.Birthdate
|
|
||||||
userInfo.Zoneinfo = user.Zoneinfo
|
|
||||||
userInfo.Locale = user.Locale
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if slices.Contains(scopes, "email") {
|
if slices.Contains(scopes, "email") {
|
||||||
userInfo.Email = user.Email
|
userInfo.Email = user.Email
|
||||||
userInfo.EmailVerified = user.Email != ""
|
// We can set this as a configuration option in the future but for now it's a good idea to assume it's true
|
||||||
|
userInfo.EmailVerified = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if slices.Contains(scopes, "groups") {
|
if slices.Contains(scopes, "groups") {
|
||||||
@@ -706,19 +653,6 @@ func (service *OIDCService) CompileUserinfo(user repository.OidcUserinfo, scope
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if slices.Contains(scopes, "phone") {
|
|
||||||
userInfo.PhoneNumber = user.PhoneNumber
|
|
||||||
verified := user.PhoneNumber != ""
|
|
||||||
userInfo.PhoneNumberVerified = &verified
|
|
||||||
}
|
|
||||||
|
|
||||||
if slices.Contains(scopes, "address") {
|
|
||||||
var addr config.AddressClaim
|
|
||||||
if err := json.Unmarshal([]byte(user.Address), &addr); err == nil {
|
|
||||||
userInfo.Address = &addr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return userInfo
|
return userInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,198 +0,0 @@
|
|||||||
package service_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/config"
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/repository"
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newTestUser() repository.OidcUserinfo {
|
|
||||||
addr := config.AddressClaim{
|
|
||||||
Formatted: "123 Main St",
|
|
||||||
StreetAddress: "123 Main St",
|
|
||||||
Locality: "Springfield",
|
|
||||||
Region: "IL",
|
|
||||||
PostalCode: "62701",
|
|
||||||
Country: "US",
|
|
||||||
}
|
|
||||||
addrJSON, _ := json.Marshal(addr)
|
|
||||||
|
|
||||||
return repository.OidcUserinfo{
|
|
||||||
Sub: "test-sub",
|
|
||||||
Name: "Test User",
|
|
||||||
PreferredUsername: "testuser",
|
|
||||||
Email: "test@example.com",
|
|
||||||
Groups: "admins,users",
|
|
||||||
UpdatedAt: 1234567890,
|
|
||||||
GivenName: "Test",
|
|
||||||
FamilyName: "User",
|
|
||||||
MiddleName: "M",
|
|
||||||
Nickname: "testy",
|
|
||||||
Profile: "https://example.com/testuser",
|
|
||||||
Picture: "https://example.com/testuser.jpg",
|
|
||||||
Website: "https://testuser.example.com",
|
|
||||||
Gender: "male",
|
|
||||||
Birthdate: "1990-01-01",
|
|
||||||
Zoneinfo: "America/Chicago",
|
|
||||||
Locale: "en-US",
|
|
||||||
PhoneNumber: "+15555550100",
|
|
||||||
Address: string(addrJSON),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCompileUserinfo(t *testing.T) {
|
|
||||||
dir := t.TempDir()
|
|
||||||
svc := service.NewOIDCService(service.OIDCServiceConfig{
|
|
||||||
PrivateKeyPath: dir + "/key.pem",
|
|
||||||
PublicKeyPath: dir + "/key.pub",
|
|
||||||
Issuer: "https://tinyauth.example.com",
|
|
||||||
SessionExpiry: 3600,
|
|
||||||
}, nil)
|
|
||||||
require.NoError(t, svc.Init())
|
|
||||||
|
|
||||||
type testCase struct {
|
|
||||||
description string
|
|
||||||
mutate func(u *repository.OidcUserinfo)
|
|
||||||
scope string
|
|
||||||
run func(t *testing.T, info service.UserinfoResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []testCase{
|
|
||||||
{
|
|
||||||
description: "openid scope only returns sub and updated_at",
|
|
||||||
scope: "openid",
|
|
||||||
run: func(t *testing.T, info service.UserinfoResponse) {
|
|
||||||
assert.Equal(t, "test-sub", info.Sub)
|
|
||||||
assert.Equal(t, int64(1234567890), info.UpdatedAt)
|
|
||||||
assert.Empty(t, info.Name)
|
|
||||||
assert.Empty(t, info.Email)
|
|
||||||
assert.Nil(t, info.Groups)
|
|
||||||
assert.Nil(t, info.PhoneNumberVerified)
|
|
||||||
assert.Nil(t, info.Address)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "profile scope returns all profile fields",
|
|
||||||
scope: "openid,profile",
|
|
||||||
run: func(t *testing.T, info service.UserinfoResponse) {
|
|
||||||
assert.Equal(t, "Test User", info.Name)
|
|
||||||
assert.Equal(t, "testuser", info.PreferredUsername)
|
|
||||||
assert.Equal(t, "Test", info.GivenName)
|
|
||||||
assert.Equal(t, "User", info.FamilyName)
|
|
||||||
assert.Equal(t, "M", info.MiddleName)
|
|
||||||
assert.Equal(t, "testy", info.Nickname)
|
|
||||||
assert.Equal(t, "https://example.com/testuser", info.Profile)
|
|
||||||
assert.Equal(t, "https://example.com/testuser.jpg", info.Picture)
|
|
||||||
assert.Equal(t, "https://testuser.example.com", info.Website)
|
|
||||||
assert.Equal(t, "male", info.Gender)
|
|
||||||
assert.Equal(t, "1990-01-01", info.Birthdate)
|
|
||||||
assert.Equal(t, "America/Chicago", info.Zoneinfo)
|
|
||||||
assert.Equal(t, "en-US", info.Locale)
|
|
||||||
assert.Empty(t, info.Email)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "email scope sets email and email_verified true when email present",
|
|
||||||
scope: "openid,email",
|
|
||||||
run: func(t *testing.T, info service.UserinfoResponse) {
|
|
||||||
assert.Equal(t, "test@example.com", info.Email)
|
|
||||||
assert.True(t, info.EmailVerified)
|
|
||||||
assert.Empty(t, info.Name)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "email scope sets email_verified false when email absent",
|
|
||||||
scope: "openid,email",
|
|
||||||
mutate: func(u *repository.OidcUserinfo) { u.Email = "" },
|
|
||||||
run: func(t *testing.T, info service.UserinfoResponse) {
|
|
||||||
assert.Empty(t, info.Email)
|
|
||||||
assert.False(t, info.EmailVerified)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "phone scope sets phone_number_verified true when phone present",
|
|
||||||
scope: "openid,phone",
|
|
||||||
run: func(t *testing.T, info service.UserinfoResponse) {
|
|
||||||
assert.Equal(t, "+15555550100", info.PhoneNumber)
|
|
||||||
require.NotNil(t, info.PhoneNumberVerified)
|
|
||||||
assert.True(t, *info.PhoneNumberVerified)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "phone scope sets phone_number_verified false when phone absent",
|
|
||||||
scope: "openid,phone",
|
|
||||||
mutate: func(u *repository.OidcUserinfo) { u.PhoneNumber = "" },
|
|
||||||
run: func(t *testing.T, info service.UserinfoResponse) {
|
|
||||||
require.NotNil(t, info.PhoneNumberVerified)
|
|
||||||
assert.False(t, *info.PhoneNumberVerified)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "address scope returns parsed address",
|
|
||||||
scope: "openid,address",
|
|
||||||
run: func(t *testing.T, info service.UserinfoResponse) {
|
|
||||||
require.NotNil(t, info.Address)
|
|
||||||
assert.Equal(t, "123 Main St", info.Address.Formatted)
|
|
||||||
assert.Equal(t, "123 Main St", info.Address.StreetAddress)
|
|
||||||
assert.Equal(t, "Springfield", info.Address.Locality)
|
|
||||||
assert.Equal(t, "IL", info.Address.Region)
|
|
||||||
assert.Equal(t, "62701", info.Address.PostalCode)
|
|
||||||
assert.Equal(t, "US", info.Address.Country)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "address scope with invalid JSON omits address",
|
|
||||||
scope: "openid,address",
|
|
||||||
mutate: func(u *repository.OidcUserinfo) { u.Address = "not-valid-json" },
|
|
||||||
run: func(t *testing.T, info service.UserinfoResponse) {
|
|
||||||
assert.Nil(t, info.Address)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "groups scope returns split groups",
|
|
||||||
scope: "openid,groups",
|
|
||||||
run: func(t *testing.T, info service.UserinfoResponse) {
|
|
||||||
assert.Equal(t, []string{"admins", "users"}, info.Groups)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "groups scope returns empty slice when no groups",
|
|
||||||
scope: "openid,groups",
|
|
||||||
mutate: func(u *repository.OidcUserinfo) { u.Groups = "" },
|
|
||||||
run: func(t *testing.T, info service.UserinfoResponse) {
|
|
||||||
assert.Equal(t, []string{}, info.Groups)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "all scopes return all fields",
|
|
||||||
scope: "openid,profile,email,phone,address,groups",
|
|
||||||
run: func(t *testing.T, info service.UserinfoResponse) {
|
|
||||||
assert.Equal(t, "Test User", info.Name)
|
|
||||||
assert.Equal(t, "test@example.com", info.Email)
|
|
||||||
assert.Equal(t, "+15555550100", info.PhoneNumber)
|
|
||||||
require.NotNil(t, info.PhoneNumberVerified)
|
|
||||||
assert.True(t, *info.PhoneNumberVerified)
|
|
||||||
require.NotNil(t, info.Address)
|
|
||||||
assert.Equal(t, "Springfield", info.Address.Locality)
|
|
||||||
assert.Equal(t, []string{"admins", "users"}, info.Groups)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.description, func(t *testing.T) {
|
|
||||||
user := newTestUser()
|
|
||||||
if test.mutate != nil {
|
|
||||||
test.mutate(&user)
|
|
||||||
}
|
|
||||||
info := svc.CompileUserinfo(user, test.scope)
|
|
||||||
test.run(t, info)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/tinyauthapp/tinyauth/internal/config"
|
"github.com/tinyauthapp/tinyauth/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseUsers(usersStr []string, userAttributes map[string]config.UserAttributes) ([]config.User, error) {
|
func ParseUsers(usersStr []string) ([]config.User, error) {
|
||||||
var users []config.User
|
var users []config.User
|
||||||
|
|
||||||
if len(usersStr) == 0 {
|
if len(usersStr) == 0 {
|
||||||
@@ -24,16 +24,13 @@ func ParseUsers(usersStr []string, userAttributes map[string]config.UserAttribut
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return []config.User{}, err
|
return []config.User{}, err
|
||||||
}
|
}
|
||||||
if attrs, ok := userAttributes[parsed.Username]; ok {
|
|
||||||
parsed.Attributes = attrs
|
|
||||||
}
|
|
||||||
users = append(users, parsed)
|
users = append(users, parsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUsers(usersCfg []string, usersPath string, userAttributes map[string]config.UserAttributes) ([]config.User, error) {
|
func GetUsers(usersCfg []string, usersPath string) ([]config.User, error) {
|
||||||
var usersStr []string
|
var usersStr []string
|
||||||
|
|
||||||
if len(usersCfg) == 0 && usersPath == "" {
|
if len(usersCfg) == 0 && usersPath == "" {
|
||||||
@@ -62,7 +59,7 @@ func GetUsers(usersCfg []string, usersPath string, userAttributes map[string]con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseUsers(usersStr, userAttributes)
|
return ParseUsers(usersStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseUser(userStr string) (config.User, error) {
|
func ParseUser(userStr string) (config.User, error) {
|
||||||
|
|||||||
@@ -4,117 +4,122 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/config"
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils"
|
"github.com/tinyauthapp/tinyauth/internal/utils"
|
||||||
|
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetUsers(t *testing.T) {
|
func TestGetUsers(t *testing.T) {
|
||||||
hash := "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G"
|
|
||||||
|
|
||||||
// Setup
|
// Setup
|
||||||
file, err := os.Create("/tmp/tinyauth_users_test.txt")
|
file, err := os.Create("/tmp/tinyauth_users_test.txt")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
_, err = file.WriteString(" user1:" + hash + " \n user2:" + hash + " ") // Spacing is on purpose
|
_, err = file.WriteString(" user1:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G \n user2:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G ") // Spacing is on purpose
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
err = file.Close()
|
err = file.Close()
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
defer os.Remove("/tmp/tinyauth_users_test.txt")
|
defer os.Remove("/tmp/tinyauth_users_test.txt")
|
||||||
|
|
||||||
noAttrs := map[string]config.UserAttributes{}
|
// Test file
|
||||||
|
users, err := utils.GetUsers([]string{}, "/tmp/tinyauth_users_test.txt")
|
||||||
// Test file only
|
|
||||||
users, err := utils.GetUsers([]string{}, "/tmp/tinyauth_users_test.txt", noAttrs)
|
|
||||||
|
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 2, len(users))
|
assert.Equal(t, 2, len(users))
|
||||||
|
|
||||||
assert.Equal(t, "user1", users[0].Username)
|
assert.Equal(t, "user1", users[0].Username)
|
||||||
assert.Equal(t, hash, users[0].Password)
|
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[0].Password)
|
||||||
assert.Equal(t, "user2", users[1].Username)
|
assert.Equal(t, "user2", users[1].Username)
|
||||||
assert.Equal(t, hash, users[1].Password)
|
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[1].Password)
|
||||||
|
|
||||||
// Test inline config only
|
// Test config
|
||||||
users, err = utils.GetUsers([]string{"user3:" + hash, "user4:" + hash}, "", noAttrs)
|
users, err = utils.GetUsers([]string{"user3:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", "user4:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G"}, "")
|
||||||
|
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 2, len(users))
|
assert.Equal(t, 2, len(users))
|
||||||
|
|
||||||
assert.Equal(t, "user3", users[0].Username)
|
assert.Equal(t, "user3", users[0].Username)
|
||||||
|
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[0].Password)
|
||||||
assert.Equal(t, "user4", users[1].Username)
|
assert.Equal(t, "user4", users[1].Username)
|
||||||
|
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[1].Password)
|
||||||
|
|
||||||
// Test both
|
// Test both
|
||||||
users, err = utils.GetUsers([]string{"user5:" + hash}, "/tmp/tinyauth_users_test.txt", noAttrs)
|
users, err = utils.GetUsers([]string{"user5:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G"}, "/tmp/tinyauth_users_test.txt")
|
||||||
|
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 3, len(users))
|
assert.Equal(t, 3, len(users))
|
||||||
|
|
||||||
usernames := map[string]bool{}
|
assert.Equal(t, "user5", users[0].Username)
|
||||||
for _, u := range users {
|
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[0].Password)
|
||||||
usernames[u.Username] = true
|
assert.Equal(t, "user1", users[1].Username)
|
||||||
}
|
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[1].Password)
|
||||||
assert.Assert(t, usernames["user1"])
|
assert.Equal(t, "user2", users[2].Username)
|
||||||
assert.Assert(t, usernames["user2"])
|
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[2].Password)
|
||||||
assert.Assert(t, usernames["user5"])
|
|
||||||
|
|
||||||
// Test attributes applied from userAttributes map
|
|
||||||
attrs := map[string]config.UserAttributes{
|
|
||||||
"user1": {Name: "User One", Email: "user1@example.com"},
|
|
||||||
}
|
|
||||||
users, err = utils.GetUsers([]string{}, "/tmp/tinyauth_users_test.txt", attrs)
|
|
||||||
|
|
||||||
assert.NilError(t, err)
|
|
||||||
assert.Equal(t, 2, len(users))
|
|
||||||
|
|
||||||
for _, u := range users {
|
|
||||||
if u.Username == "user1" {
|
|
||||||
assert.Equal(t, "User One", u.Attributes.Name)
|
|
||||||
assert.Equal(t, "user1@example.com", u.Attributes.Email)
|
|
||||||
}
|
|
||||||
if u.Username == "user2" {
|
|
||||||
assert.Equal(t, "", u.Attributes.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test empty
|
// Test empty
|
||||||
users, err = utils.GetUsers([]string{}, "", noAttrs)
|
users, err = utils.GetUsers([]string{}, "")
|
||||||
|
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, 0, len(users))
|
assert.Equal(t, 0, len(users))
|
||||||
|
|
||||||
// Test non-existent file
|
// Test non-existent file
|
||||||
users, err = utils.GetUsers([]string{}, "/tmp/non_existent_file.txt", noAttrs)
|
users, err = utils.GetUsers([]string{}, "/tmp/non_existent_file.txt")
|
||||||
|
|
||||||
assert.ErrorContains(t, err, "no such file or directory")
|
assert.ErrorContains(t, err, "no such file or directory")
|
||||||
|
|
||||||
assert.Equal(t, 0, len(users))
|
assert.Equal(t, 0, len(users))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseUser(t *testing.T) {
|
func TestParseUsers(t *testing.T) {
|
||||||
hash := "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G"
|
// Valid users
|
||||||
|
users, err := utils.ParseUsers([]string{"user1:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", "user2:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G:ABCDEF"}) // user2 has TOTP
|
||||||
|
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 2, len(users))
|
||||||
|
|
||||||
|
assert.Equal(t, "user1", users[0].Username)
|
||||||
|
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[0].Password)
|
||||||
|
assert.Equal(t, "", users[0].TotpSecret)
|
||||||
|
assert.Equal(t, "user2", users[1].Username)
|
||||||
|
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[1].Password)
|
||||||
|
assert.Equal(t, "ABCDEF", users[1].TotpSecret)
|
||||||
|
|
||||||
|
// Valid weirdly spaced users
|
||||||
|
users, err = utils.ParseUsers([]string{" user1:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G ", " user2:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G:ABCDEF "}) // Spacing is on purpose
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, 2, len(users))
|
||||||
|
|
||||||
|
assert.Equal(t, "user1", users[0].Username)
|
||||||
|
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[0].Password)
|
||||||
|
assert.Equal(t, "", users[0].TotpSecret)
|
||||||
|
assert.Equal(t, "user2", users[1].Username)
|
||||||
|
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", users[1].Password)
|
||||||
|
assert.Equal(t, "ABCDEF", users[1].TotpSecret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseUser(t *testing.T) {
|
||||||
// Valid user without TOTP
|
// Valid user without TOTP
|
||||||
user, err := utils.ParseUser("user1:" + hash)
|
user, err := utils.ParseUser("user1:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G")
|
||||||
|
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, "user1", user.Username)
|
assert.Equal(t, "user1", user.Username)
|
||||||
assert.Equal(t, hash, user.Password)
|
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", user.Password)
|
||||||
assert.Equal(t, "", user.TotpSecret)
|
assert.Equal(t, "", user.TotpSecret)
|
||||||
|
|
||||||
// Valid user with TOTP
|
// Valid user with TOTP
|
||||||
user, err = utils.ParseUser("user2:" + hash + ":ABCDEF")
|
user, err = utils.ParseUser("user2:$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G:ABCDEF")
|
||||||
|
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, "user2", user.Username)
|
assert.Equal(t, "user2", user.Username)
|
||||||
assert.Equal(t, hash, user.Password)
|
assert.Equal(t, "$2a$10$Mz5xhkfSJUtPWkzCd/TdaePh9CaXc5QcGII5wIMPLSR46eTwma30G", user.Password)
|
||||||
assert.Equal(t, "ABCDEF", user.TotpSecret)
|
assert.Equal(t, "ABCDEF", user.TotpSecret)
|
||||||
|
|
||||||
// Valid user with $$ in password
|
// Valid user with $$ in password
|
||||||
|
|||||||
+2
-15
@@ -95,22 +95,9 @@ INSERT INTO "oidc_userinfo" (
|
|||||||
"preferred_username",
|
"preferred_username",
|
||||||
"email",
|
"email",
|
||||||
"groups",
|
"groups",
|
||||||
"updated_at",
|
"updated_at"
|
||||||
"given_name",
|
|
||||||
"family_name",
|
|
||||||
"middle_name",
|
|
||||||
"nickname",
|
|
||||||
"profile",
|
|
||||||
"picture",
|
|
||||||
"website",
|
|
||||||
"gender",
|
|
||||||
"birthdate",
|
|
||||||
"zoneinfo",
|
|
||||||
"locale",
|
|
||||||
"phone_number",
|
|
||||||
"address"
|
|
||||||
) VALUES (
|
) VALUES (
|
||||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
?, ?, ?, ?, ?, ?
|
||||||
)
|
)
|
||||||
RETURNING *;
|
RETURNING *;
|
||||||
|
|
||||||
|
|||||||
+6
-19
@@ -22,23 +22,10 @@ CREATE TABLE IF NOT EXISTS "oidc_tokens" (
|
|||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "oidc_userinfo" (
|
CREATE TABLE IF NOT EXISTS "oidc_userinfo" (
|
||||||
"sub" TEXT NOT NULL UNIQUE PRIMARY KEY,
|
"sub" TEXT NOT NULL UNIQUE PRIMARY KEY,
|
||||||
"name" TEXT NOT NULL,
|
"name" TEXT NOT NULL,
|
||||||
"preferred_username" TEXT NOT NULL,
|
"preferred_username" TEXT NOT NULL,
|
||||||
"email" TEXT NOT NULL,
|
"email" TEXT NOT NULL,
|
||||||
"groups" TEXT NOT NULL,
|
"groups" TEXT NOT NULL,
|
||||||
"updated_at" INTEGER NOT NULL,
|
"updated_at" INTEGER NOT NULL
|
||||||
"given_name" TEXT NOT NULL,
|
|
||||||
"family_name" TEXT NOT NULL,
|
|
||||||
"middle_name" TEXT NOT NULL,
|
|
||||||
"nickname" TEXT NOT NULL,
|
|
||||||
"profile" TEXT NOT NULL,
|
|
||||||
"picture" TEXT NOT NULL,
|
|
||||||
"website" TEXT NOT NULL,
|
|
||||||
"gender" TEXT NOT NULL,
|
|
||||||
"birthdate" TEXT NOT NULL,
|
|
||||||
"zoneinfo" TEXT NOT NULL,
|
|
||||||
"locale" TEXT NOT NULL,
|
|
||||||
"phone_number" TEXT NOT NULL,
|
|
||||||
"address" TEXT NOT NULL
|
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user