Add hurl tests for apikeys

This commit is contained in:
Zoe Roux 2025-04-23 19:38:05 +02:00
parent e8acb31834
commit dcbbb6352a
No known key found for this signature in database
8 changed files with 99 additions and 11 deletions

View File

@ -99,6 +99,6 @@ RABBITMQ_DEFAULT_PASS=aohohunuhouhuhhoahothonseuhaoensuthoaentsuhha
# v5 stuff, does absolutely nothing on master (aka: you can delete this)
EXTRA_CLAIMS='{"permissions": ["core.read"], "verified": false}'
FIRST_USER_CLAIMS='{"permissions": ["users.read", "users.write", "users.delete", "core.read"], "verified": true}'
FIRST_USER_CLAIMS='{"permissions": ["users.read", "users.write", "apikeys.read", "apikeys.write", "users.delete", "core.read", "core.write"], "verified": true}'
GUEST_CLAIMS='{"permissions": ["core.read"]}'
PROTECTED_CLAIMS="permissions,verified"

View File

@ -53,8 +53,12 @@ jobs:
env:
POSTGRES_SERVER: localhost
FIRST_USER_CLAIMS: '{"permissions": ["users.read"]}'
KEIBI_APIKEY_HURL: 1234apikey
KEIBI_APIKEY_HURL_CLAIMS: '{"permissions": ["apikeys.write", "apikeys.read"]}'
- name: Show logs
if: failure()
working-directory: ./auth
run: cat logs

View File

@ -32,7 +32,7 @@ type ApiKeyWToken struct {
}
type ApiKeyDto struct {
Name string `json:"name" example:"my-app" validate:"alpha"`
Name string `json:"name" example:"myapp" validate:"alpha"`
Claims jwt.MapClaims `json:"claims" example:"isAdmin: true"`
}
@ -61,8 +61,13 @@ func MapDbKey(key *dbc.Apikey) ApiKeyWToken {
// @Failure 422 {object} KError "Invalid create body"
// @Router /keys [post]
func (h *Handler) CreateApiKey(c echo.Context) error {
err := CheckPermissions(c, []string{"apikeys.write"})
if err != nil {
return err
}
var req ApiKeyDto
err := c.Bind(&req)
err = c.Bind(&req)
if err != nil {
return echo.NewHTTPError(http.StatusUnprocessableEntity, err.Error())
}
@ -104,6 +109,11 @@ func (h *Handler) CreateApiKey(c echo.Context) error {
// @Failure 422 {object} KError "Invalid id format"
// @Router /keys [delete]
func (h *Handler) DeleteApiKey(c echo.Context) error {
err := CheckPermissions(c, []string{"apikeys.write"})
if err != nil {
return err
}
id, err := uuid.Parse(c.Param("id"))
if err != nil {
return echo.NewHTTPError(422, "Invalid id given: not an uuid")
@ -127,6 +137,11 @@ func (h *Handler) DeleteApiKey(c echo.Context) error {
// @Success 200 {object} Page[ApiKey]
// @Router /keys [get]
func (h *Handler) ListApiKey(c echo.Context) error {
err := CheckPermissions(c, []string{"apikeys.read"})
if err != nil {
return err
}
dbkeys, err := h.db.ListApiKeys(context.Background())
if err != nil {
return err

View File

@ -14,6 +14,7 @@ import (
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"github.com/zoriya/kyoo/keibi/dbc"
)
@ -103,11 +104,14 @@ func LoadConfiguration(db *dbc.Queries) (*Configuration, error) {
}
for _, env := range os.Environ() {
if !strings.HasPrefix(env, "KEIBI_APIKEY_") || strings.HasSuffix(env, "_CLAIMS") {
if !strings.HasPrefix(env, "KEIBI_APIKEY_"){
continue
}
v := strings.Split(env, "=")
if strings.HasSuffix(v[0], "_CLAIMS") {
continue
}
v := strings.Split(env, "=")
name := strings.TrimPrefix(v[0], "KEIBI_APIKEY_")
cstr := os.Getenv(fmt.Sprintf("KEIBI_APIKEY_%s_CLAIMS", name))
@ -117,10 +121,14 @@ func LoadConfiguration(db *dbc.Queries) (*Configuration, error) {
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("missing claims env var KEIBI_APIKEY_%s_CLAIMS", name)
}
name = strings.ToLower(name)
ret.EnvApiKeys[name] = ApiKeyWToken{
ApiKey: ApiKey{
Id: uuid.New(),
Name: name,
Claims: claims,
},

View File

@ -162,7 +162,7 @@ func (h *Handler) TokenToJwt(next echo.HandlerFunc) echo.HandlerFunc {
}
if jwt != nil {
c.Request().Header.Set("Authorization", *jwt)
c.Request().Header.Set("Authorization", fmt.Sprintf("Bearer %s", *jwt))
}
return next(c)
}
@ -236,7 +236,7 @@ func main() {
r.GET("/keys", h.ListApiKey)
r.POST("/keys", h.CreateApiKey)
r.DELETE("/keys", h.DeleteApiKey)
r.DELETE("/keys/:id", h.DeleteApiKey)
g.GET("/jwt", h.CreateJwt)
e.GET("/.well-known/jwks.json", h.GetJwks)

View File

@ -9,7 +9,7 @@ create table apikeys(
created_by integer not null references users(pk) on delete cascade,
created_at timestamptz not null default now()::timestamptz,
last_used timestamptz not null default now()::temistamptz
last_used timestamptz not null default now()::timestamptz
);
commit;

58
auth/tests/apikey.hurl Normal file
View File

@ -0,0 +1,58 @@
# perm check
POST {{host}}/keys
{
"name": "dryflower",
"claims": {
"isAdmin": true,
"permssions": ["core.read"]
}
}
HTTP 401
POST {{host}}/keys
# this is created from the gh workflow file's env var
X-API-KEY: hurl-1234apikey
{
"name": "dryflower",
"claims": {
"isAdmin": true,
"permssions": ["core.read"]
}
}
HTTP 201
[Captures]
token: jsonpath "$.token"
GET {{host}}/jwt
Authorization: Bearer {{token}}
HTTP 200
[Captures]
id: jsonpath "$.id"
jwt: jsonpath "$.token"
# Duplicates email
POST {{host}}/keys
X-API-KEY: hurl-1234apikey
{
"name": "dryflower",
"claims": {
"isAdmin": true,
"permssions": ["core.read"]
}
}
HTTP 409
# List
GET {{host}}/keys
Authorization: Bearer {{token}}
HTTP 200
[Asserts]
jsonpath "$.items[0].id" == {{id}}
jsonpath "$.items[0].name" == "dryflower"
jsonpath "$.items[0].claims.permissions" contains "core.read"
DELETE {{host}}/keys/{{id}}
Authorization: Bearer {{jwt}}
HTTP 200

View File

@ -56,7 +56,12 @@ func GetCurrentSessionId(c echo.Context) (uuid.UUID, error) {
func CheckPermissions(c echo.Context, perms []string) error {
token, ok := c.Get("user").(*jwt.Token)
if !ok {
if !ok{
return echo.NewHTTPError(401, "Not logged in")
}
sub, err := token.Claims.GetSubject()
// ignore guests
if err != nil || sub == "00000000-0000-0000-0000-000000000000" {
return echo.NewHTTPError(401, "Not logged in")
}
claims, ok := token.Claims.(jwt.MapClaims)
@ -68,8 +73,6 @@ func CheckPermissions(c echo.Context, perms []string) error {
if !ok {
return echo.NewHTTPError(403, fmt.Sprintf("Missing permissions: %s.", ", "))
}
fmt.Printf("%v\n", permissions_claims)
fmt.Printf("%t\n", permissions_claims)
permissions_int, ok := permissions_claims.([]any)
if !ok {
return echo.NewHTTPError(403, "Invalid permission claim.")