feat: read username if email is not available for oauth provider

Also add recipe for OAuth with GitHub setup
This commit is contained in:
Evgenii Alekseev 2024-01-05 12:28:31 +02:00
parent 9e1cf3cde7
commit ed7ed5d5d9
7 changed files with 115 additions and 1 deletions

View File

@ -10,6 +10,7 @@ Collection of the examples of docker compose configuration files, which covers s
* [Distributed manual](distributed-manual): same as [distributed](distributed), but two nodes and update process must be run on worker node manually.
* [i686](i686): non-x86_64 architecture setup.
* [Multi repo](multirepo): run web service with two separated repositories.
* [OAuth](oauth): web service with OAuth (GitHub provider) authentication enabled.
* [Pull](pull): normal service, but in addition with pulling packages from another source (e.g. GitHub repository).
* [Sign](sign): create repository with database signing.
* [Web](web): simple web service with authentication enabled.

15
recipes/oauth/README.md Normal file
View File

@ -0,0 +1,15 @@
# OAuth
1. Create user from `AHRIMAN_OAUTH_USER` environment variable (same as GitHub user).
2. Configure OAuth to use GitHub provider with client ID and secret specified in variables `AHRIMAN_OAUTH_CLIENT_ID` and `AHRIMAN_OAUTH_CLIENT_SECRET` variables respectively.
3. Setup repository named `ahriman-demo` with architecture `x86_64`.
4. Start web server at port `8080`.
5. Repository is available at `http://localhost:8080/repo`.
Before you start, you need to create an application. It can be done by:
1. Go to `https://github.com/settings/applications/new`
2. Set application name and its homepage.
3. Set callback url to `http://localhost:8080/api/v1/login`
4. Copy Client ID.
5. Generate new client secret and copy it.

58
recipes/oauth/compose.yml Normal file
View File

@ -0,0 +1,58 @@
services:
backend:
image: arcan1s/ahriman:edge
privileged: true
environment:
AHRIMAN_DEBUG: yes
AHRIMAN_OAUTH_CLIENT_ID: ${AHRIMAN_OAUTH_CLIENT_ID}
AHRIMAN_OAUTH_CLIENT_SECRET: ${AHRIMAN_OAUTH_CLIENT_SECRET}
AHRIMAN_OUTPUT: console
AHRIMAN_PORT: 8080
AHRIMAN_PRESETUP_COMMAND: sudo -u ahriman ahriman user-add ${AHRIMAN_OAUTH_USER} -R full -p ""
AHRIMAN_REPOSITORY: ahriman-demo
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
configs:
- source: service
target: /etc/ahriman.ini.d/99-settings.ini
volumes:
- type: volume
source: repository
target: /var/lib/ahriman
volume:
nocopy: true
healthcheck:
test: curl --fail --silent --output /dev/null http://backend:8080/api/v1/info
interval: 10s
start_period: 30s
command: web
frontend:
image: nginx
ports:
- 8080:80
configs:
- source: nginx
target: /etc/nginx/conf.d/default.conf
volumes:
- type: volume
source: repository
target: /srv
read_only: true
volume:
nocopy: true
configs:
nginx:
file: nginx.conf
service:
file: service.ini
volumes:
repository:

18
recipes/oauth/nginx.conf Normal file
View File

@ -0,0 +1,18 @@
server {
listen 80;
location /repo {
rewrite ^/repo/(.*) /$1 break;
autoindex on;
root /srv/ahriman/repository;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarder-Proto $scheme;
proxy_pass http://backend:8080;
}
}

11
recipes/oauth/service.ini Normal file
View File

@ -0,0 +1,11 @@
[auth]
target = oauth
client_id = $AHRIMAN_OAUTH_CLIENT_ID
client_secret = $AHRIMAN_OAUTH_CLIENT_SECRET
oauth_icon = github
oauth_provider = GithubClient
oauth_scopes = read:user
[web]
address = http://localhost:8080

View File

@ -130,7 +130,7 @@ class OAuth(Mapping):
client.access_token = access_token
user, _ = await client.user_info()
username: str = user.email # type: ignore[attr-defined]
username: str = user.email or user.username # type: ignore[attr-defined]
return username
except Exception:
self.logger.exception("got exception while performing request")

View File

@ -75,6 +75,17 @@ async def test_get_oauth_username(oauth: OAuth, mocker: MockerFixture) -> None:
assert email == "email"
async def test_get_oauth_username_empty_email(oauth: OAuth, mocker: MockerFixture) -> None:
"""
must read username if email is not available
"""
mocker.patch("aioauth_client.GoogleClient.get_access_token", return_value=("token", ""))
mocker.patch("aioauth_client.GoogleClient.user_info", return_value=(aioauth_client.User(username="username"), ""))
username = await oauth.get_oauth_username("code")
assert username == "username"
async def test_get_oauth_username_exception_1(oauth: OAuth, mocker: MockerFixture) -> None:
"""
must return None in case of OAuth request error (get_access_token)