Skip to content

Commit

Permalink
Store expected GitHub userid in database during enroll (#2566)
Browse files Browse the repository at this point in the history
* Update Keycloak config to store and expose gh_id and gh_login

* Switch from kcadm.sh commands to keycloak-config-cli for most setup

* Store expected GitHub userid in database during enroll

* Fix lint errors

* Further cleanup keycloak config (optional)

* Fix remaining lint errors

* Add tests for handlers_oauth.go

* Add a metric to track what fraction of tokens are tied to a GitHub userid

* Apply changes from Ria's review

* Fix non-compiling code

* Address Ozz's feedback

* Update migration number

* Fix test failures brought on by merge

* re-run `make gen`

Signed-off-by: Juan Antonio Osorio <[email protected]>

* Fix test

Signed-off-by: Juan Antonio Osorio <[email protected]>

* Enable variable substitution in keycloak CLI container

Signed-off-by: Juan Antonio Osorio <[email protected]>

---------

Signed-off-by: Juan Antonio Osorio <[email protected]>
Co-authored-by: Juan Antonio Osorio <[email protected]>
  • Loading branch information
evankanderson and JAORMX authored Mar 13, 2024
1 parent e174e74 commit 8866984
Show file tree
Hide file tree
Showing 35 changed files with 695 additions and 268 deletions.
6 changes: 4 additions & 2 deletions .mk/identity.mk
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ ifndef KC_GITHUB_CLIENT_SECRET
$(error KC_GITHUB_CLIENT_SECRET is not set)
endif
@echo "Setting up GitHub login..."
@$(CONTAINER) exec -it keycloak_container /opt/keycloak/bin/kcadm.sh config credentials --server http://localhost:8080 --realm master --user admin --password admin
# Delete the existing GitHub identity provider, if it exists. Otherwise, ignore the error.
@$(CONTAINER) exec -it keycloak_container /opt/keycloak/bin/kcadm.sh delete identity-provider/instances/github -r stacklok || true
@$(CONTAINER) exec -it keycloak_container /opt/keycloak/bin/kcadm.sh create identity-provider/instances -r stacklok -s alias=github -s providerId=github -s enabled=true -s 'config.useJwksUrl="true"' -s config.clientId=$$KC_GITHUB_CLIENT_ID -s config.clientSecret=$$KC_GITHUB_CLIENT_SECRET
@$(CONTAINER) exec -it keycloak_container /opt/keycloak/bin/kcadm.sh create identity-provider/instances/github/mappers -r stacklok -s name=gh_id -s identityProviderAlias=github -s identityProviderMapper=github-user-attribute-mapper -s config='{"syncMode":"FORCE", "jsonField":"id", "userAttribute":"gh_id"}'
@$(CONTAINER) exec -it keycloak_container /opt/keycloak/bin/kcadm.sh create identity-provider/instances/github/mappers -r stacklok -s name=gh_login -s identityProviderAlias=github -s identityProviderMapper=github-user-attribute-mapper -s config='{"syncMode":"FORCE", "jsonField":"login", "userAttribute":"gh_login"}'

password-login:
@echo "Setting up password login..."
@$(CONTAINER) exec -it keycloak_container /opt/keycloak/bin/kcadm.sh config credentials --server http://localhost:8080 --realm master --user admin --password admin
@$(CONTAINER) exec -it keycloak_container /opt/keycloak/bin/kcadm.sh create users -r stacklok -s username=testuser -s enabled=true
@$(CONTAINER) exec -it keycloak_container /opt/keycloak/bin/kcadm.sh set-password -r stacklok --username testuser --new-password tester
3 changes: 2 additions & 1 deletion cmd/dev/app/container/cmd_verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package container

import (
"context"
"database/sql"
"encoding/json"
"fmt"
"os"
Expand Down Expand Up @@ -121,7 +122,7 @@ func buildGitHubClient(token string) (provifv1.GitHub, error) {
"github": {}
}`),
},
db.ProviderAccessToken{},
sql.NullString{},
token,
)

Expand Down
3 changes: 2 additions & 1 deletion cmd/dev/app/rule_type/rttst.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package rule_type
import (
"bytes"
"context"
"database/sql"
"encoding/json"
"fmt"
"os"
Expand Down Expand Up @@ -132,7 +133,7 @@ func testCmdRun(cmd *cobra.Command, _ []string) error {
"github": {}
}`),
},
db.ProviderAccessToken{},
sql.NullString{},
token,
))
inf := &entities.EntityInfoWrapper{
Expand Down
15 changes: 15 additions & 0 deletions database/migrations/000028_session_userid.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- Copyright 2024 Stacklok, Inc
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

ALTER TABLE session_store DROP COLUMN remote_user;
15 changes: 15 additions & 0 deletions database/migrations/000028_session_userid.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- Copyright 2024 Stacklok, Inc
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

ALTER TABLE session_store ADD COLUMN remote_user TEXT;
30 changes: 0 additions & 30 deletions database/mock/store.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 3 additions & 9 deletions database/query/session_store.sql
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
-- name: CreateSessionState :one
INSERT INTO session_store (provider, project_id, session_state, owner_filter, redirect_url) VALUES ($1, $2, $3, $4, $5) RETURNING *;

-- name: GetSessionState :one
SELECT * FROM session_store WHERE id = $1;

-- name: GetSessionStateByProjectID :one
SELECT * FROM session_store WHERE project_id = $1;
INSERT INTO session_store (provider, project_id, remote_user, session_state, owner_filter, redirect_url) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *;

-- name: GetProjectIDBySessionState :one
SELECT provider, project_id, owner_filter, redirect_url FROM session_store WHERE session_state = $1;
SELECT provider, project_id, remote_user, owner_filter, redirect_url FROM session_store WHERE session_state = $1;

-- name: DeleteSessionState :exec
DELETE FROM session_store WHERE id = $1;

-- name: DeleteSessionStateByProjectID :exec
DELETE FROM session_store WHERE provider=$1 AND project_id = $2;
DELETE FROM session_store WHERE provider = $1 AND project_id = $2;

-- name: DeleteExpiredSessionStates :exec
DELETE FROM session_store WHERE created_at < NOW() - INTERVAL '1 day';
29 changes: 25 additions & 4 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ services:
condition: service_healthy
openfga:
condition: service_healthy
migrate:
condition: service_completed_successfully
keycloak-config:
condition: service_completed_successfully
migrate:
container_name: minder_migrate_up
build:
Expand Down Expand Up @@ -126,21 +130,38 @@ services:
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
KC_MINDER_SERVER_SECRET: secret
KC_HEALTH_ENABLED: "true"
healthcheck:
test: ["CMD", "/opt/keycloak/bin/kcadm.sh", "get", "realms/stacklok", "--fields", "enabled"]
test: ["CMD", "/opt/keycloak/bin/kcadm.sh", "config", "credentials", "--server", "http://localhost:8080", "--realm", "master", "--user", "admin", "--password", "admin"]
interval: 10s
timeout: 5s
retries: 10
ports:
- "8081:8080"
volumes:
- ./identity/themes:/opt/keycloak/themes:z
- ./identity/scripts:/opt/keycloak/scripts:z
networks:
- app_net
entrypoint: ["/opt/keycloak/scripts/kc-setup.sh"]

keycloak-config:
container_name: keycloak_config
image: bitnami/keycloak-config-cli:5.10.0
entrypoint: ["java", "-jar", "/opt/bitnami/keycloak-config-cli/keycloak-config-cli.jar"]
environment:
KEYCLOAK_URL: http://keycloak:8080
KEYCLOAK_USER: admin
KEYCLOAK_PASSWORD: admin
KC_MINDER_SERVER_SECRET: secret
IMPORT_VARSUBSTITUTION_ENABLED: "true"
IMPORT_FILES_LOCATIONS: /config/*.yaml
volumes:
- ./identity/config:/config:z
networks:
- app_net

depends_on:
keycloak:
condition: service_healthy

openfga:
container_name: openfga
Expand Down
137 changes: 137 additions & 0 deletions identity/config/stacklok.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Copyright 2024 Stacklok, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# From:
# create realms -s realm=stacklok -s loginTheme=keycloak -s eventsEnabled=true -s 'enabledEventTypes=["DELETE_ACCOUNT"]' -s eventsExpiration=604800 -s enabled=true
realm: stacklok
enabled: true
loginTheme: keycloak
eventsEnabled: true
enabledEventTypes:
- DELETE_ACCOUNT
eventsExpiration: 604800

# From:
# Add account deletion capability to stacklok realm (see https://www.keycloak.org/docs/latest/server_admin/#authentication-operations)
# update "/authentication/required-actions/delete_account" -r stacklok -b '{ "alias" : "delete_account", "name" : "Delete Account", "providerId" : "delete_account", "enabled" : true, "defaultAction" : false, "priority" : 60, "config" : { }}'
requiredActions:
- alias: delete_account
name: Delete Account
providerId: delete_account
enabled: true
defaultAction: false

# From:
# Give all users permission to delete their own account
# add-roles -r stacklok --rname default-roles-stacklok --rolename delete-account --cclientid account
roles:
realm:
- name: default-roles-stacklok
composites:
client:
account:
- delete-account
- view-profile
- manage-account

# Collect gh_login and gh_id from GitHub and expose them in tokens
clientScopes:
- name: gh-data
description: "Add GitHub information to tokens"
protocol: openid-connect
attributes:
"include.in.token.scope": "true"
"display.on.consent.screen": "false"
protocolMappers:
- name: gh_id
protocol: openid-connect
protocolMapper: oidc-usermodel-attribute-mapper
consentRequired: false
config:
userinfo.token.claim: "true"
id.token.claim: "true"
access.token.claim: "true"
claim.name: "gh_id"
jsonType.label: "String"
user.attribute: "gh_id"
- name: gh_login
protocol: openid-connect
protocolMapper: oidc-usermodel-attribute-mapper
consentRequired: false
config:
userinfo.token.claim: "true"
id.token.claim: "true"
access.token.claim: "true"
claim.name: "gh_login"
jsonType.label: "String"
user.attribute: "gh_login"


clients:
# From:
# create clients -r stacklok -s clientId=minder-cli -s 'redirectUris=["http://localhost/*"]' -s publicClient=true -s enabled=true -s defaultClientScopes='["acr","email","profile","roles","web-origins","gh-data"]' -s optionalClientScopes='["microprofile-jwt","offline_access"]'
- clientId: minder-cli
enabled: true
redirectUris:
- "http://localhost/*"
publicClient: true
# If you set one of these, you seem to need to set both (per CLI experimentation)
defaultClientScopes:
- acr
- email
- profile
- roles
- web-origins
- gh-data
optionalClientScopes:
- microprofile-jwt
- offline_access
# From:
# create clients -r stacklok -s clientId=minder-ui -s 'redirectUris=["http://localhost/*"]' -s publicClient=true -s enabled=true -s defaultClientScopes='["acr","email","profile","roles","web-origins","gh-data"]' -s optionalClientScopes='["microprofile-jwt","offline_access"]'
- clientId: minder-ui
enabled: true
redirectUris:
- "http://localhost/*"
publicClient: true
# If you set one of these, you seem to need to set both (per CLI experimentation)
defaultClientScopes:
- acr
- email
- profile
- roles
- web-origins
- gh-data
optionalClientScopes:
- microprofile-jwt
- offline_access
# From:
# create clients -r stacklok -s clientId=minder-server -s serviceAccountsEnabled=true -s clientAuthenticatorType=client-secret -s secret="$KC_MINDER_SERVER_SECRET" -s enabled=true -s defaultClientScopes='["acr","email","profile","roles","web-origins","gh-data"]' -s optionalClientScopes='["microprofile-jwt","offline_access"]'
- clientId: minder-server
enabled: true
serviceAccountsEnabled: true
clientAuthenticatorType: client-secret
secret: "$(env:KC_MINDER_SERVER_SECRET)"

users:
- username: service-account-minder-server
clientRoles:
realm-management:
# From:
# Give minder-server the capability to view events
# add-roles -r stacklok --uusername service-account-minder-server --cclientid realm-management --rolename view-events
- view-events
# From:
# Give minder-server the capability to delete users
# add-roles -r stacklok --uusername service-account-minder-server --cclientid realm-management --rolename manage-users
- manage-users
56 changes: 0 additions & 56 deletions identity/scripts/initialize.sh

This file was deleted.

Loading

0 comments on commit 8866984

Please sign in to comment.