feat(spec,app): handle local accounts
This commit is contained in:
@ -11,7 +11,6 @@ import (
|
||||
"forge.cadoles.com/Cadoles/emissary/internal/spec/app"
|
||||
"forge.cadoles.com/arcad/edge/pkg/bundle"
|
||||
"forge.cadoles.com/arcad/edge/pkg/storage/sqlite"
|
||||
"github.com/lestrrat-go/jwx/v2/jwk"
|
||||
"github.com/mitchellh/hashstructure/v2"
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/logger"
|
||||
@ -97,32 +96,18 @@ func (c *Controller) updateApps(ctx context.Context, spec *app.Spec) {
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
key jwk.Key
|
||||
err error
|
||||
)
|
||||
|
||||
if spec.Auth != nil {
|
||||
key, err = jwk.FromRaw(spec.Auth.Key)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "could not parse authentication key", logger.E(errors.WithStack(err)))
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// (Re)start apps
|
||||
for appID, appSpec := range spec.Apps {
|
||||
appCtx := logger.With(ctx, logger.F("appID", appID))
|
||||
|
||||
if err := c.updateApp(ctx, appID, appSpec, key); err != nil {
|
||||
if err := c.updateApp(ctx, appID, appSpec, spec.Auth); err != nil {
|
||||
logger.Error(appCtx, "could not update app", logger.E(errors.WithStack(err)))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) updateApp(ctx context.Context, appID string, appSpec app.AppEntry, key jwk.Key) (err error) {
|
||||
func (c *Controller) updateApp(ctx context.Context, appID string, appSpec app.AppEntry, auth *app.Auth) (err error) {
|
||||
newAppSpecHash, err := hashstructure.Hash(appSpec, hashstructure.FormatV2, nil)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
@ -165,7 +150,7 @@ func (c *Controller) updateApp(ctx context.Context, appID string, appSpec app.Ap
|
||||
}
|
||||
|
||||
entry = &serverEntry{
|
||||
Server: NewServer(bundle, db, key),
|
||||
Server: NewServer(bundle, db, auth),
|
||||
SpecHash: 0,
|
||||
}
|
||||
|
||||
|
@ -6,12 +6,14 @@ import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
appSpec "forge.cadoles.com/Cadoles/emissary/internal/spec/app"
|
||||
"forge.cadoles.com/arcad/edge/pkg/app"
|
||||
"forge.cadoles.com/arcad/edge/pkg/bus"
|
||||
"forge.cadoles.com/arcad/edge/pkg/bus/memory"
|
||||
edgeHTTP "forge.cadoles.com/arcad/edge/pkg/http"
|
||||
"forge.cadoles.com/arcad/edge/pkg/module"
|
||||
"forge.cadoles.com/arcad/edge/pkg/module/auth"
|
||||
authHTTP "forge.cadoles.com/arcad/edge/pkg/module/auth/http"
|
||||
"forge.cadoles.com/arcad/edge/pkg/module/cast"
|
||||
"forge.cadoles.com/arcad/edge/pkg/module/net"
|
||||
"forge.cadoles.com/arcad/edge/pkg/storage"
|
||||
@ -22,8 +24,12 @@ import (
|
||||
"github.com/dop251/goja"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/lestrrat-go/jwx/v2/jwa"
|
||||
"github.com/lestrrat-go/jwx/v2/jwk"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
_ "forge.cadoles.com/arcad/edge/pkg/module/auth/http/passwd/argon2id"
|
||||
_ "forge.cadoles.com/arcad/edge/pkg/module/auth/http/passwd/plain"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
@ -31,7 +37,8 @@ type Server struct {
|
||||
db *sql.DB
|
||||
server *http.Server
|
||||
serverMutex sync.RWMutex
|
||||
key jwk.Key
|
||||
auth *appSpec.Auth
|
||||
keySet jwk.Set
|
||||
}
|
||||
|
||||
func (s *Server) Start(ctx context.Context, addr string) (err error) {
|
||||
@ -57,6 +64,37 @@ func (s *Server) Start(ctx context.Context, addr string) (err error) {
|
||||
return errors.Wrap(err, "could not load app bundle")
|
||||
}
|
||||
|
||||
if s.auth != nil {
|
||||
if s.auth.Local != nil {
|
||||
var rawKey any = s.auth.Local.Key
|
||||
if strKey, ok := rawKey.(string); ok {
|
||||
rawKey = []byte(strKey)
|
||||
}
|
||||
|
||||
key, err := jwk.FromRaw(rawKey)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
if err := key.Set(jwk.AlgorithmKey, jwa.HS256); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
keySet := jwk.NewSet()
|
||||
if err := keySet.AddKey(key); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
s.keySet = keySet
|
||||
|
||||
router.Handle("/auth/*", authHTTP.NewLocalHandler(
|
||||
jwa.HS256, key,
|
||||
authHTTP.WithRoutePrefix("/auth"),
|
||||
authHTTP.WithAccounts(s.auth.Local.Accounts...),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
router.Handle("/*", handler)
|
||||
|
||||
server := &http.Server{
|
||||
@ -148,19 +186,13 @@ func (s *Server) getAppModules(bus bus.Bus, ds storage.DocumentStore, bs storage
|
||||
}
|
||||
|
||||
func (s *Server) getJWTKeySet() (jwk.Set, error) {
|
||||
set := jwk.NewSet()
|
||||
|
||||
if err := set.AddKey(s.key); err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return set, nil
|
||||
return s.keySet, nil
|
||||
}
|
||||
|
||||
func NewServer(bundle bundle.Bundle, db *sql.DB, key jwk.Key) *Server {
|
||||
func NewServer(bundle bundle.Bundle, db *sql.DB, auth *appSpec.Auth) *Server {
|
||||
return &Server{
|
||||
bundle: bundle,
|
||||
db: db,
|
||||
key: key,
|
||||
auth: auth,
|
||||
}
|
||||
}
|
||||
|
@ -22,10 +22,18 @@
|
||||
},
|
||||
"format": {
|
||||
"type": "string",
|
||||
"enum": ["zip", "tar.gz"]
|
||||
"enum": [
|
||||
"zip",
|
||||
"tar.gz"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["url", "sha256sum", "address", "format"],
|
||||
"required": [
|
||||
"url",
|
||||
"sha256sum",
|
||||
"address",
|
||||
"format"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
@ -33,13 +41,47 @@
|
||||
"auth": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "object"
|
||||
"local": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": ["object", "string"]
|
||||
},
|
||||
"accounts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"algo": {
|
||||
"type": "string"
|
||||
},
|
||||
"claims": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"username",
|
||||
"password",
|
||||
"algo"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["key"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["apps"],
|
||||
"required": [
|
||||
"apps"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
@ -2,12 +2,13 @@ package app
|
||||
|
||||
import (
|
||||
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
||||
edgeAuth "forge.cadoles.com/arcad/edge/pkg/module/auth/http"
|
||||
)
|
||||
|
||||
const NameApp spec.Name = "app.emissary.cadoles.com"
|
||||
|
||||
type Spec struct {
|
||||
Revision int `json:"revisions"`
|
||||
Revision int `json:"revision"`
|
||||
Apps map[string]AppEntry `json:"apps"`
|
||||
Auth *Auth `json:"auth"`
|
||||
}
|
||||
@ -20,7 +21,12 @@ type AppEntry struct {
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
Key any `json:"key"`
|
||||
Local *LocalAuth `json:"local,omitempty"`
|
||||
}
|
||||
|
||||
type LocalAuth struct {
|
||||
Key any `json:"key"`
|
||||
Accounts []edgeAuth.LocalAccount `json:"accounts"`
|
||||
}
|
||||
|
||||
func (s *Spec) SpecName() spec.Name {
|
||||
|
43
internal/spec/app/testdata/spec-ok.json
vendored
43
internal/spec/app/testdata/spec-ok.json
vendored
@ -1,7 +1,42 @@
|
||||
{
|
||||
"name": "app.emissary.cadoles.com",
|
||||
"data": {
|
||||
"apps": {}
|
||||
"name": "app.emissary.cadoles.com",
|
||||
"data": {
|
||||
"apps": {
|
||||
"edge.sdk.client.test": {
|
||||
"url": "http://example.com/edge.sdk.client.test_0.0.0.zip",
|
||||
"sha256sum": "58019192dacdae17755707719707db007e26dac856102280583fbd18427dd352",
|
||||
"address": ":8081",
|
||||
"format": "zip"
|
||||
}
|
||||
},
|
||||
"revision": 0
|
||||
"auth": {
|
||||
"local": {
|
||||
"key": {
|
||||
"d": "YOre0WZefGfUGFvDg42oL5Oad5Zsb1N_hqPyLVM5ajpTZzcHpB3wT6In9tFO_VshB6lxVtPA9ckPkpMTFY7ygt1Yomc1HkoOKRtmIaqdr4VgNQifU-4yiLiJkSbdYSeMV-KkkN8mGR1keJpJeS34W1X0W6CkU2nw7F5VueBCJfWJA0funRfuWdI68MTUgT9kRZFp-SfvptvRL6jVYHV_5hqxzHCvgEdBSF6QKwx4M6P6QBMt7ft6uMLmFx9abKFw2V51hX3PkxiSepVB3w5CYg4HtS3AHX6bILL4m0R2pdTIkap7i3tkH_xAOuKWt8D6JhadI8X1rEAwXmCS5KrRgQ",
|
||||
"dp": "U0HfvBC6hk-SCpuotGIv3vbHCVt1aF3SHK0y32EYCOe8e_9G6YCEILfcvEJ5fiOCc2kvx6TasHQu4qj1uWRKenZlK1sJ6KDybGCkZL1D3jYnbeLZYBuWBL__YbZiST3ewbxzj_EDMWiZ8sUltahza_1weSgg8auSzTHS2LJBHIE",
|
||||
"dq": "hVom4ScDxgqhCsQNVpZlN7M3v0tgWjl_gTOHjOyzKCHQJeC0QmJJaMKkQZPWJ8jjLqy7VwVpqC2nZU7QDuX1Cq5eJDQcXi9XtaAfIBico9WcYDre6mDyhL588YHpekyRke8HnZ810iesr0G3gU1h0QvZVVuW-pXTJOXhZTt6nFc",
|
||||
"e": "AQAB",
|
||||
"kty": "RSA",
|
||||
"n": "vPnpkE3-HfNgJSru_K40LstkjiG2Bq_Tt-m0d_yUBBSbirFxF3qH4EXi7WrtZdeDahg2iV2BvpbVVj9GlmGo9OLol6jc7AP2yvZrkbABiiJhCbuPdkYbNpx6B7Itl8RT_bUSYAMZhmux5lpsn4weQ01fzjICi1rA-bIJpOfotdOjP4_lol-LxGZOGJQv9kndP8bgmssJb3Y_2s4gPtkmXySLrhpr5So-_6dVksyuBD9aLcnsMLDbywusjEMCdhqzQbvOjryomnmEXwyz_Ewb5HFK2PfgFtoHkdjqDz-mrEs3tw5g4TdYhCftzJxgbyNAEq4aEiOQrAncYyrXlotP_w",
|
||||
"p": "8TNMF0WUe7CEeNVUTsuEcBAAXRguNtpvVifIjlwzFRGOYVGIpKuHsqQPKlZL07I9gPr9LifQnyQus3oEmTOrVs6LB9sfbukbg43ZRKoGVM40JYF5Xjs7R3mEZhgU0WaYOVe3iLtBGMfXNWFwlbfQP-zEb-dPCBX1jWT3LdgNBcE",
|
||||
"q": "yJJLNc9w6O4y2icME8k99FugV9E7ObwUxF3v5JN3y1cmAT0h2njyE3iAGqaDZwcY1_jGCisjwoqX6i5E8xqhxX3Gcy3J7SmUAf8fhY8wU3zv9DK7skg2IdvanDb8Y1OM6GchbYZAOVPEg2IvVio8zI-Ih3DDwDk8Df0ufzoHRb8",
|
||||
"qi": "zOE-4R3cjPesm3MX-4PdwmsaF9QZLUVRUvvHJ08pKs6kAXP18hzjctAoOjhQDxlTYqNYNePfKzKwost3OJoPgRIc9w9qwUCK1gNOS4Z_xozCIaXgMddNFhkoAfZ4JaKjNCiinzjGfqG99Lf-yzmmREuuhRv7SdS3ST4VQjiJQew"
|
||||
},
|
||||
"accounts": [
|
||||
{
|
||||
"username": "foo",
|
||||
"algo": "plain",
|
||||
"password": "bar",
|
||||
"claims": {
|
||||
"arcad_role": "user",
|
||||
"arcad_tenant": "dev.cli",
|
||||
"preferred_username": "Foo",
|
||||
"sub": "foo"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"revision": 0
|
||||
}
|
@ -8,7 +8,7 @@ import (
|
||||
const NameUCI spec.Name = "uci.emissary.cadoles.com"
|
||||
|
||||
type Spec struct {
|
||||
Revision int `json:"revisions"`
|
||||
Revision int `json:"revision"`
|
||||
Config *uci.UCI `json:"config"`
|
||||
PostImportCommands []*UCIPostImportCommand `json:"postImportCommands"`
|
||||
}
|
||||
|
Reference in New Issue
Block a user