diff --git a/auth/apikey.go b/auth/apikey.go index a09b4019..851b6b90 100644 --- a/auth/apikey.go +++ b/auth/apikey.go @@ -4,10 +4,9 @@ import ( "context" "crypto/rand" "encoding/base64" - "fmt" "maps" "net/http" - "strings" + "slices" "time" "github.com/golang-jwt/jwt/v5" @@ -45,7 +44,7 @@ func MapDbKey(key *dbc.Apikey) ApiKeyWToken { CreatedAt: key.CreatedAt, LastUsed: key.LastUsed, }, - Token: fmt.Sprintf("%s-%s", key.Name, key.Token), + Token: key.Token, } } @@ -75,7 +74,10 @@ func (h *Handler) CreateApiKey(c echo.Context) error { return err } - if _, conflict := h.config.EnvApiKeys[req.Name]; conflict { + conflict := slices.ContainsFunc(h.config.EnvApiKeys, func(k ApiKeyWToken) bool { + return k.Name == req.Name + }) + if conflict { return echo.NewHTTPError(409, "An env apikey is already defined with the same name") } @@ -174,17 +176,15 @@ func (h *Handler) ListApiKey(c echo.Context) error { } func (h *Handler) createApiJwt(apikey string) (string, error) { - info := strings.SplitN(apikey, "-", 2) - if len(info) != 2 { - return "", echo.NewHTTPError(http.StatusForbidden, "Invalid api key format") + var key *ApiKeyWToken + for _, k := range h.config.EnvApiKeys { + if k.Token == apikey { + key = &k + break + } } - - key, fromEnv := h.config.EnvApiKeys[info[0]] - if !fromEnv { - dbKey, err := h.db.GetApiKey(context.Background(), dbc.GetApiKeyParams{ - Name: info[0], - Token: info[1], - }) + if key == nil { + dbKey, err := h.db.GetApiKey(context.Background(), apikey) if err == pgx.ErrNoRows { return "", echo.NewHTTPError(http.StatusForbidden, "Invalid api key") } else if err != nil { @@ -195,7 +195,8 @@ func (h *Handler) createApiJwt(apikey string) (string, error) { h.db.TouchApiKey(context.Background(), dbKey.Pk) }() - key = MapDbKey(&dbKey) + found := MapDbKey(&dbKey) + key = &found } claims := maps.Clone(key.Claims) diff --git a/auth/config.go b/auth/config.go index 37f9a65a..79aab64f 100644 --- a/auth/config.go +++ b/auth/config.go @@ -12,6 +12,7 @@ import ( "fmt" "maps" "os" + "slices" "strings" "time" @@ -31,7 +32,7 @@ type Configuration struct { GuestClaims jwt.MapClaims ProtectedClaims []string ExpirationDelay time.Duration - EnvApiKeys map[string]ApiKeyWToken + EnvApiKeys []ApiKeyWToken } var DefaultConfig = Configuration{ @@ -39,7 +40,7 @@ var DefaultConfig = Configuration{ FirstUserClaims: make(jwt.MapClaims), ProtectedClaims: []string{"permissions"}, ExpirationDelay: 30 * 24 * time.Hour, - EnvApiKeys: make(map[string]ApiKeyWToken), + EnvApiKeys: make([]ApiKeyWToken, 0), } func LoadConfiguration(db *dbc.Queries) (*Configuration, error) { @@ -137,14 +138,14 @@ func LoadConfiguration(db *dbc.Queries) (*Configuration, error) { } name = strings.ToLower(name) - ret.EnvApiKeys[name] = ApiKeyWToken{ + ret.EnvApiKeys = append(ret.EnvApiKeys, ApiKeyWToken{ ApiKey: ApiKey{ Id: uuid.New(), Name: name, Claims: claims, }, Token: v[1], - } + }) } apikeys, err := db.ListApiKeys(context.Background()) @@ -152,7 +153,10 @@ func LoadConfiguration(db *dbc.Queries) (*Configuration, error) { return nil, err } for _, key := range apikeys { - if _, defined := ret.EnvApiKeys[key.Name]; defined { + dup := slices.ContainsFunc(ret.EnvApiKeys, func(k ApiKeyWToken) bool { + return k.Name == key.Name + }) + if dup { return nil, fmt.Errorf( "an api key with the name %s is already defined in database. Can't specify a new one via env var", key.Name, diff --git a/auth/dbc/apikeys.sql.go b/auth/dbc/apikeys.sql.go index 698f8733..bf570ee3 100644 --- a/auth/dbc/apikeys.sql.go +++ b/auth/dbc/apikeys.sql.go @@ -76,17 +76,11 @@ select from keibi.apikeys where - name = $1 - and token = $2 + token = $1 ` -type GetApiKeyParams struct { - Name string `json:"name"` - Token string `json:"token"` -} - -func (q *Queries) GetApiKey(ctx context.Context, arg GetApiKeyParams) (Apikey, error) { - row := q.db.QueryRow(ctx, getApiKey, arg.Name, arg.Token) +func (q *Queries) GetApiKey(ctx context.Context, token string) (Apikey, error) { + row := q.db.QueryRow(ctx, getApiKey, token) var i Apikey err := row.Scan( &i.Pk, diff --git a/auth/sql/queries/apikeys.sql b/auth/sql/queries/apikeys.sql index f340d9fe..a7bb486c 100644 --- a/auth/sql/queries/apikeys.sql +++ b/auth/sql/queries/apikeys.sql @@ -4,8 +4,7 @@ select from keibi.apikeys where - name = $1 - and token = $2; + token = $1; -- name: TouchApiKey :exec update diff --git a/auth/tests/apikey.hurl b/auth/tests/apikey.hurl index 2daaf9dc..2c96a801 100644 --- a/auth/tests/apikey.hurl +++ b/auth/tests/apikey.hurl @@ -11,7 +11,7 @@ HTTP 401 POST {{host}}/keys # this is created from the gh workflow file's env var -X-API-KEY: hurl-1234apikey +X-API-KEY: 1234apikey { "name": "dryflower", "claims": { @@ -32,7 +32,7 @@ jwt: jsonpath "$.token" # Duplicates email POST {{host}}/keys -X-API-KEY: hurl-1234apikey +X-API-KEY: 1234apikey { "name": "dryflower", "claims": { @@ -57,5 +57,5 @@ Authorization: Bearer {{jwt}} HTTP 403 DELETE {{host}}/keys/{{id}} -X-API-KEY: hurl-1234apikey +X-API-KEY: 1234apikey HTTP 200 diff --git a/auth/tests/externalauth-apikey.hurl b/auth/tests/externalauth-apikey.hurl index fd80bb30..b1c2e46f 100644 --- a/auth/tests/externalauth-apikey.hurl +++ b/auth/tests/externalauth-apikey.hurl @@ -1,6 +1,6 @@ POST {{host}}/keys # this is created from the gh workflow file's env var -X-API-KEY: hurl-1234apikey +X-API-KEY: 1234apikey { "name": "dryflower", "claims": { @@ -32,5 +32,5 @@ jsonpath "$.items[0].claims.permissions" contains "apikeys.read" # Clean api key DELETE {{host}}/keys/{{id}} -X-API-KEY: hurl-1234apikey +X-API-KEY: 1234apikey HTTP 200 diff --git a/chart/templates/scanner/deployment.yaml b/chart/templates/scanner/deployment.yaml index 48a3ebb0..7862acc4 100644 --- a/chart/templates/scanner/deployment.yaml +++ b/chart/templates/scanner/deployment.yaml @@ -14,7 +14,7 @@ metadata: spec: replicas: {{ .Values.scanner.replicaCount }} {{- with .Values.scanner.updateStrategy }} - strategy: + strategy: {{- toYaml . | nindent 4 }} {{- end }} selector: @@ -62,13 +62,11 @@ spec: value: "http://{{ include "kyoo.auth.fullname" . }}:4568/.well-known/jwks.json" - name: JWT_ISSUER value: {{ .Values.kyoo.address | quote }} - - name: HELPERVAR_APIKEY + - name: KYOO_APIKEY valueFrom: secretKeyRef: key: {{ .Values.kyoo.auth.apikeys.scanner.apikeyKey }} name: {{ .Values.kyoo.auth.apikeys.scanner.existingSecret }} - - name: KYOO_APIKEY - value: "scanner-$(HELPERVAR_APIKEY)" - name: THEMOVIEDB_API_ACCESS_TOKEN valueFrom: secretKeyRef: @@ -136,4 +134,4 @@ spec: {{- end }} {{- with .Values.scanner.extraVolumes }} {{- toYaml . | nindent 8 }} - {{- end }} \ No newline at end of file + {{- end }} diff --git a/chart/values.yaml b/chart/values.yaml index ecc8b546..03c3c53f 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -115,7 +115,6 @@ kyoo: extra: [] # - name: example # existingSecret: bigsecret - ## value of the apieky should be $name-$apikey # apikeyKey: example_apikey # claims: '{"permissions": ["core.read"]}' @@ -201,7 +200,7 @@ api: volumeMounts: [] volumes: [] replicaCount: 1 - # default to recreate for better user experience with ReadWriteOnce volumes + # default to recreate for better user experience with ReadWriteOnce volumes updateStrategy: type: Recreate podLabels: {} @@ -472,7 +471,7 @@ postgres: initdb: scripts: kyoo_api.sql: | - CREATE DATABASE {{ .Values.global.postgres.kyoo_api.database }} WITH OWNER {{ .Values.global.postgres.infra.user }}; + CREATE DATABASE {{ .Values.global.postgres.kyoo_api.database }} WITH OWNER {{ .Values.global.postgres.infra.user }}; \connect {{ .Values.global.postgres.kyoo_api.database }}; REVOKE ALL ON SCHEMA public FROM PUBLIC; CREATE SCHEMA IF NOT EXISTS kyoo AUTHORIZATION {{ .Values.global.postgres.infra.user }}; @@ -481,7 +480,7 @@ postgres: SET pg_trgm.word_similarity_threshold = 0.4; ALTER DATABASE {{ .Values.global.postgres.kyoo_api.database }} SET pg_trgm.word_similarity_threshold = 0.4; kyoo_auth.sql: | - CREATE DATABASE {{ .Values.global.postgres.kyoo_auth.database }} WITH OWNER {{ .Values.global.postgres.infra.user }}; + CREATE DATABASE {{ .Values.global.postgres.kyoo_auth.database }} WITH OWNER {{ .Values.global.postgres.infra.user }}; \connect {{ .Values.global.postgres.kyoo_auth.database }}; REVOKE ALL ON SCHEMA public FROM PUBLIC; CREATE SCHEMA IF NOT EXISTS keibi AUTHORIZATION {{ .Values.global.postgres.infra.user }}; @@ -496,13 +495,13 @@ postgres: REVOKE ALL ON SCHEMA public FROM PUBLIC; CREATE SCHEMA IF NOT EXISTS gocoder AUTHORIZATION {{ .Values.global.postgres.infra.user }}; user.sql: | - ALTER ROLE {{ .Values.global.postgres.infra.user }} + ALTER ROLE {{ .Values.global.postgres.infra.user }} IN DATABASE {{ .Values.global.postgres.kyoo_api.database }} SET search_path TO "$user", kyoo; ALTER ROLE {{ .Values.global.postgres.infra.user }} IN DATABASE {{ .Values.global.postgres.kyoo_auth.database }} SET search_path TO "$user", keibi; - ALTER ROLE {{ .Values.global.postgres.infra.user }} + ALTER ROLE {{ .Values.global.postgres.infra.user }} IN DATABASE {{ .Values.global.postgres.kyoo_scanner.database }} SET search_path TO "$user", scanner; - ALTER ROLE {{ .Values.global.postgres.infra.user }} + ALTER ROLE {{ .Values.global.postgres.infra.user }} IN DATABASE {{ .Values.global.postgres.kyoo_transcoder.database }} SET search_path TO "$user", gocoder; persistence: enabled: true diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 9dc3876d..b4e04d34 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -121,7 +121,7 @@ services: # Use this env var once we use mTLS for auth # - KYOO_URL=${KYOO_URL:-http://api:3567/api} - KYOO_URL=${KYOO_URL:-http://traefik:8901/api} - - KYOO_APIKEY=scanner-$KEIBI_APIKEY_SCANNER + - KYOO_APIKEY=$KEIBI_APIKEY_SCANNER - JWKS_URL=http://auth:4568/.well-known/jwks.json - JWT_ISSUER=${PUBLIC_URL} volumes: diff --git a/docker-compose.yml b/docker-compose.yml index a9cced58..57ee406e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -78,7 +78,7 @@ services: # Use this env var once we use mTLS for auth # - KYOO_URL=${KYOO_URL:-http://api:3567/api} - KYOO_URL=${KYOO_URL:-http://traefik:8901/api} - - KYOO_APIKEY=scanner-$KEIBI_APIKEY_SCANNER + - KYOO_APIKEY=$KEIBI_APIKEY_SCANNER - JWKS_URL=http://auth:4568/.well-known/jwks.json - JWT_ISSUER=${PUBLIC_URL} volumes: diff --git a/scanner/.env.example b/scanner/.env.example index 79090c6a..84f9a439 100644 --- a/scanner/.env.example +++ b/scanner/.env.example @@ -11,7 +11,7 @@ LIBRARY_IGNORE_PATTERN=".*/[dD]ownloads?/.*" THEMOVIEDB_API_ACCESS_TOKEN="" KYOO_URL="http://api:3567/api" -KYOO_APIKEY=scanner-$KEIBI_APIKEY_SCANNER +KYOO_APIKEY=$KEIBI_APIKEY_SCANNER JWKS_URL="http://auth:4568/.well-known/jwks.json" JWT_ISSUER=$PUBLIC_URL