mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-11-21 05:53:11 -05:00
Remove name prefix in apikeys (#1167)
This commit is contained in:
parent
a115c83cba
commit
18b2ae2c5f
@ -4,10 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
|
||||||
"maps"
|
"maps"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
@ -45,7 +44,7 @@ func MapDbKey(key *dbc.Apikey) ApiKeyWToken {
|
|||||||
CreatedAt: key.CreatedAt,
|
CreatedAt: key.CreatedAt,
|
||||||
LastUsed: key.LastUsed,
|
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
|
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")
|
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) {
|
func (h *Handler) createApiJwt(apikey string) (string, error) {
|
||||||
info := strings.SplitN(apikey, "-", 2)
|
var key *ApiKeyWToken
|
||||||
if len(info) != 2 {
|
for _, k := range h.config.EnvApiKeys {
|
||||||
return "", echo.NewHTTPError(http.StatusForbidden, "Invalid api key format")
|
if k.Token == apikey {
|
||||||
|
key = &k
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
key, fromEnv := h.config.EnvApiKeys[info[0]]
|
if key == nil {
|
||||||
if !fromEnv {
|
dbKey, err := h.db.GetApiKey(context.Background(), apikey)
|
||||||
dbKey, err := h.db.GetApiKey(context.Background(), dbc.GetApiKeyParams{
|
|
||||||
Name: info[0],
|
|
||||||
Token: info[1],
|
|
||||||
})
|
|
||||||
if err == pgx.ErrNoRows {
|
if err == pgx.ErrNoRows {
|
||||||
return "", echo.NewHTTPError(http.StatusForbidden, "Invalid api key")
|
return "", echo.NewHTTPError(http.StatusForbidden, "Invalid api key")
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
@ -195,7 +195,8 @@ func (h *Handler) createApiJwt(apikey string) (string, error) {
|
|||||||
h.db.TouchApiKey(context.Background(), dbKey.Pk)
|
h.db.TouchApiKey(context.Background(), dbKey.Pk)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
key = MapDbKey(&dbKey)
|
found := MapDbKey(&dbKey)
|
||||||
|
key = &found
|
||||||
}
|
}
|
||||||
|
|
||||||
claims := maps.Clone(key.Claims)
|
claims := maps.Clone(key.Claims)
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ type Configuration struct {
|
|||||||
GuestClaims jwt.MapClaims
|
GuestClaims jwt.MapClaims
|
||||||
ProtectedClaims []string
|
ProtectedClaims []string
|
||||||
ExpirationDelay time.Duration
|
ExpirationDelay time.Duration
|
||||||
EnvApiKeys map[string]ApiKeyWToken
|
EnvApiKeys []ApiKeyWToken
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultConfig = Configuration{
|
var DefaultConfig = Configuration{
|
||||||
@ -39,7 +40,7 @@ var DefaultConfig = Configuration{
|
|||||||
FirstUserClaims: make(jwt.MapClaims),
|
FirstUserClaims: make(jwt.MapClaims),
|
||||||
ProtectedClaims: []string{"permissions"},
|
ProtectedClaims: []string{"permissions"},
|
||||||
ExpirationDelay: 30 * 24 * time.Hour,
|
ExpirationDelay: 30 * 24 * time.Hour,
|
||||||
EnvApiKeys: make(map[string]ApiKeyWToken),
|
EnvApiKeys: make([]ApiKeyWToken, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfiguration(db *dbc.Queries) (*Configuration, error) {
|
func LoadConfiguration(db *dbc.Queries) (*Configuration, error) {
|
||||||
@ -137,14 +138,14 @@ func LoadConfiguration(db *dbc.Queries) (*Configuration, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
name = strings.ToLower(name)
|
name = strings.ToLower(name)
|
||||||
ret.EnvApiKeys[name] = ApiKeyWToken{
|
ret.EnvApiKeys = append(ret.EnvApiKeys, ApiKeyWToken{
|
||||||
ApiKey: ApiKey{
|
ApiKey: ApiKey{
|
||||||
Id: uuid.New(),
|
Id: uuid.New(),
|
||||||
Name: name,
|
Name: name,
|
||||||
Claims: claims,
|
Claims: claims,
|
||||||
},
|
},
|
||||||
Token: v[1],
|
Token: v[1],
|
||||||
}
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
apikeys, err := db.ListApiKeys(context.Background())
|
apikeys, err := db.ListApiKeys(context.Background())
|
||||||
@ -152,7 +153,10 @@ func LoadConfiguration(db *dbc.Queries) (*Configuration, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, key := range apikeys {
|
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(
|
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",
|
"an api key with the name %s is already defined in database. Can't specify a new one via env var",
|
||||||
key.Name,
|
key.Name,
|
||||||
|
|||||||
@ -76,17 +76,11 @@ select
|
|||||||
from
|
from
|
||||||
keibi.apikeys
|
keibi.apikeys
|
||||||
where
|
where
|
||||||
name = $1
|
token = $1
|
||||||
and token = $2
|
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetApiKeyParams struct {
|
func (q *Queries) GetApiKey(ctx context.Context, token string) (Apikey, error) {
|
||||||
Name string `json:"name"`
|
row := q.db.QueryRow(ctx, getApiKey, token)
|
||||||
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)
|
|
||||||
var i Apikey
|
var i Apikey
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.Pk,
|
&i.Pk,
|
||||||
|
|||||||
@ -4,8 +4,7 @@ select
|
|||||||
from
|
from
|
||||||
keibi.apikeys
|
keibi.apikeys
|
||||||
where
|
where
|
||||||
name = $1
|
token = $1;
|
||||||
and token = $2;
|
|
||||||
|
|
||||||
-- name: TouchApiKey :exec
|
-- name: TouchApiKey :exec
|
||||||
update
|
update
|
||||||
|
|||||||
@ -11,7 +11,7 @@ HTTP 401
|
|||||||
|
|
||||||
POST {{host}}/keys
|
POST {{host}}/keys
|
||||||
# this is created from the gh workflow file's env var
|
# this is created from the gh workflow file's env var
|
||||||
X-API-KEY: hurl-1234apikey
|
X-API-KEY: 1234apikey
|
||||||
{
|
{
|
||||||
"name": "dryflower",
|
"name": "dryflower",
|
||||||
"claims": {
|
"claims": {
|
||||||
@ -32,7 +32,7 @@ jwt: jsonpath "$.token"
|
|||||||
|
|
||||||
# Duplicates email
|
# Duplicates email
|
||||||
POST {{host}}/keys
|
POST {{host}}/keys
|
||||||
X-API-KEY: hurl-1234apikey
|
X-API-KEY: 1234apikey
|
||||||
{
|
{
|
||||||
"name": "dryflower",
|
"name": "dryflower",
|
||||||
"claims": {
|
"claims": {
|
||||||
@ -57,5 +57,5 @@ Authorization: Bearer {{jwt}}
|
|||||||
HTTP 403
|
HTTP 403
|
||||||
|
|
||||||
DELETE {{host}}/keys/{{id}}
|
DELETE {{host}}/keys/{{id}}
|
||||||
X-API-KEY: hurl-1234apikey
|
X-API-KEY: 1234apikey
|
||||||
HTTP 200
|
HTTP 200
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
POST {{host}}/keys
|
POST {{host}}/keys
|
||||||
# this is created from the gh workflow file's env var
|
# this is created from the gh workflow file's env var
|
||||||
X-API-KEY: hurl-1234apikey
|
X-API-KEY: 1234apikey
|
||||||
{
|
{
|
||||||
"name": "dryflower",
|
"name": "dryflower",
|
||||||
"claims": {
|
"claims": {
|
||||||
@ -32,5 +32,5 @@ jsonpath "$.items[0].claims.permissions" contains "apikeys.read"
|
|||||||
# Clean api key
|
# Clean api key
|
||||||
|
|
||||||
DELETE {{host}}/keys/{{id}}
|
DELETE {{host}}/keys/{{id}}
|
||||||
X-API-KEY: hurl-1234apikey
|
X-API-KEY: 1234apikey
|
||||||
HTTP 200
|
HTTP 200
|
||||||
|
|||||||
@ -62,13 +62,11 @@ spec:
|
|||||||
value: "http://{{ include "kyoo.auth.fullname" . }}:4568/.well-known/jwks.json"
|
value: "http://{{ include "kyoo.auth.fullname" . }}:4568/.well-known/jwks.json"
|
||||||
- name: JWT_ISSUER
|
- name: JWT_ISSUER
|
||||||
value: {{ .Values.kyoo.address | quote }}
|
value: {{ .Values.kyoo.address | quote }}
|
||||||
- name: HELPERVAR_APIKEY
|
- name: KYOO_APIKEY
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
key: {{ .Values.kyoo.auth.apikeys.scanner.apikeyKey }}
|
key: {{ .Values.kyoo.auth.apikeys.scanner.apikeyKey }}
|
||||||
name: {{ .Values.kyoo.auth.apikeys.scanner.existingSecret }}
|
name: {{ .Values.kyoo.auth.apikeys.scanner.existingSecret }}
|
||||||
- name: KYOO_APIKEY
|
|
||||||
value: "scanner-$(HELPERVAR_APIKEY)"
|
|
||||||
- name: THEMOVIEDB_API_ACCESS_TOKEN
|
- name: THEMOVIEDB_API_ACCESS_TOKEN
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
|
|||||||
@ -115,7 +115,6 @@ kyoo:
|
|||||||
extra: []
|
extra: []
|
||||||
# - name: example
|
# - name: example
|
||||||
# existingSecret: bigsecret
|
# existingSecret: bigsecret
|
||||||
## value of the apieky should be $name-$apikey
|
|
||||||
# apikeyKey: example_apikey
|
# apikeyKey: example_apikey
|
||||||
# claims: '{"permissions": ["core.read"]}'
|
# claims: '{"permissions": ["core.read"]}'
|
||||||
|
|
||||||
|
|||||||
@ -121,7 +121,7 @@ services:
|
|||||||
# Use this env var once we use mTLS for auth
|
# Use this env var once we use mTLS for auth
|
||||||
# - KYOO_URL=${KYOO_URL:-http://api:3567/api}
|
# - KYOO_URL=${KYOO_URL:-http://api:3567/api}
|
||||||
- KYOO_URL=${KYOO_URL:-http://traefik:8901/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
|
- JWKS_URL=http://auth:4568/.well-known/jwks.json
|
||||||
- JWT_ISSUER=${PUBLIC_URL}
|
- JWT_ISSUER=${PUBLIC_URL}
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@ -78,7 +78,7 @@ services:
|
|||||||
# Use this env var once we use mTLS for auth
|
# Use this env var once we use mTLS for auth
|
||||||
# - KYOO_URL=${KYOO_URL:-http://api:3567/api}
|
# - KYOO_URL=${KYOO_URL:-http://api:3567/api}
|
||||||
- KYOO_URL=${KYOO_URL:-http://traefik:8901/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
|
- JWKS_URL=http://auth:4568/.well-known/jwks.json
|
||||||
- JWT_ISSUER=${PUBLIC_URL}
|
- JWT_ISSUER=${PUBLIC_URL}
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@ -11,7 +11,7 @@ LIBRARY_IGNORE_PATTERN=".*/[dD]ownloads?/.*"
|
|||||||
THEMOVIEDB_API_ACCESS_TOKEN=""
|
THEMOVIEDB_API_ACCESS_TOKEN=""
|
||||||
|
|
||||||
KYOO_URL="http://api:3567/api"
|
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"
|
JWKS_URL="http://auth:4568/.well-known/jwks.json"
|
||||||
JWT_ISSUER=$PUBLIC_URL
|
JWT_ISSUER=$PUBLIC_URL
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user