diff --git a/auth/.env.example b/auth/.env.example index 830ca1ac..f1c14b4d 100644 --- a/auth/.env.example +++ b/auth/.env.example @@ -7,6 +7,11 @@ KEIBI_PREFIX="" # path of the private key used to sign jwts. If this is empty, a new one will be generated on startup RSA_PRIVATE_KEY_PATH="" +# json object with the claims to add to every jwt (this is read when creating a new user) +EXTRA_CLAIMS='{}' +# json object with the claims to add to every jwt of the FIRST user (this can be used to mark the first user as admin). +# Those claims are merged with the `EXTRA_CLAIMS`. +FIRST_USER_CLAIMS='{}' # The url you can use to reach your kyoo instance. This is used during oidc to redirect users to your instance. PUBLIC_URL=http://localhost:8901 diff --git a/auth/config.go b/auth/config.go index 0eb53d66..7c1d32b7 100644 --- a/auth/config.go +++ b/auth/config.go @@ -1,11 +1,12 @@ package main import ( - "context" "crypto/rand" "crypto/rsa" "crypto/x509" + "encoding/json" "encoding/pem" + "maps" "os" "time" @@ -19,11 +20,13 @@ type Configuration struct { JwtPublicKey *rsa.PublicKey PublicUrl string DefaultClaims jwt.MapClaims + FirstUserClaims jwt.MapClaims ExpirationDelay time.Duration } var DefaultConfig = Configuration{ DefaultClaims: make(jwt.MapClaims), + FirstUserClaims: make(jwt.MapClaims), ExpirationDelay: 30 * 24 * time.Hour, } @@ -33,6 +36,25 @@ func LoadConfiguration(db *dbc.Queries) (*Configuration, error) { ret.PublicUrl = os.Getenv("PUBLIC_URL") ret.Prefix = os.Getenv("KEIBI_PREFIX") + claims := os.Getenv("EXTRA_CLAIMS") + if claims != "" { + err := json.Unmarshal([]byte(claims), &ret.DefaultClaims) + if err != nil { + return nil, err + } + } + claims = os.Getenv("FIRST_USER_CLAIMS") + if claims != "" { + err := json.Unmarshal([]byte(claims), &ret.FirstUserClaims) + if err != nil { + return nil, err + } + + maps.Insert(ret.FirstUserClaims, maps.All(ret.DefaultClaims)) + } else { + ret.FirstUserClaims = ret.DefaultClaims + } + rsa_pk_path := os.Getenv("RSA_PRIVATE_KEY_PATH") if rsa_pk_path != "" { privateKeyData, err := os.ReadFile(rsa_pk_path) diff --git a/auth/dbc/users.sql.go b/auth/dbc/users.sql.go index 88360e27..22035cee 100644 --- a/auth/dbc/users.sql.go +++ b/auth/dbc/users.sql.go @@ -14,16 +14,25 @@ import ( const createUser = `-- name: CreateUser :one insert into users(username, email, password, claims) - values ($1, $2, $3, $4) + values ($1, $2, $3, case when not exists ( + select + pk, id, username, email, password, claims, created_date, last_seen + from + users) then + $4::jsonb + else + $5::jsonb + end) returning pk, id, username, email, password, claims, created_date, last_seen ` type CreateUserParams struct { - Username string `json:"username"` - Email string `json:"email"` - Password *string `json:"password"` - Claims jwt.MapClaims `json:"claims"` + Username string `json:"username"` + Email string `json:"email"` + Password *string `json:"password"` + FirstClaims interface{} `json:"firstClaims"` + Claims interface{} `json:"claims"` } func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) { @@ -31,6 +40,7 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, e arg.Username, arg.Email, arg.Password, + arg.FirstClaims, arg.Claims, ) var i User diff --git a/auth/sql/queries/users.sql b/auth/sql/queries/users.sql index c57315ec..6881c861 100644 --- a/auth/sql/queries/users.sql +++ b/auth/sql/queries/users.sql @@ -51,7 +51,15 @@ where -- name: CreateUser :one insert into users(username, email, password, claims) - values ($1, $2, $3, $4) + values ($1, $2, $3, case when not exists ( + select + * + from + users) then + sqlc.arg(first_claims)::jsonb + else + sqlc.arg(claims)::jsonb + end) returning *; diff --git a/auth/sqlc.yaml b/auth/sqlc.yaml index 3bc35e05..2c48d06e 100644 --- a/auth/sqlc.yaml +++ b/auth/sqlc.yaml @@ -27,6 +27,9 @@ sql: go_type: import: "github.com/google/uuid" type: "UUID" + - db_type: "jsonb" + go_type: + type: "interface{}" - column: "users.claims" go_type: import: "github.com/golang-jwt/jwt/v5" diff --git a/auth/users.go b/auth/users.go index 7781c0ec..53589652 100644 --- a/auth/users.go +++ b/auth/users.go @@ -208,10 +208,11 @@ func (h *Handler) Register(c echo.Context) error { } duser, err := h.db.CreateUser(context.Background(), dbc.CreateUserParams{ - Username: req.Username, - Email: req.Email, - Password: &pass, - Claims: h.config.DefaultClaims, + Username: req.Username, + Email: req.Email, + Password: &pass, + Claims: h.config.DefaultClaims, + FirstClaims: h.config.FirstUserClaims, }) if ErrIs(err, pgerrcode.UniqueViolation) { return echo.NewHTTPError(409, "Email or username already taken") diff --git a/shell.nix b/shell.nix index 84eefc02..b6a13543 100644 --- a/shell.nix +++ b/shell.nix @@ -40,7 +40,7 @@ in go-migrate sqlc go-swag - robotframework-tidy + # robotframework-tidy bun pkg-config node-gyp