package app import ( "net/http" "time" "forge.cadoles.com/Cadoles/emissary/internal/agent/controller/app/spec" appSpec "forge.cadoles.com/Cadoles/emissary/internal/agent/controller/app/spec" "forge.cadoles.com/Cadoles/emissary/internal/jwk" "forge.cadoles.com/arcad/edge/pkg/app" "forge.cadoles.com/arcad/edge/pkg/module" "forge.cadoles.com/arcad/edge/pkg/module/auth" authModule "forge.cadoles.com/arcad/edge/pkg/module/auth" authHTTP "forge.cadoles.com/arcad/edge/pkg/module/auth/http" authModuleMiddleware "forge.cadoles.com/arcad/edge/pkg/module/auth/middleware" "github.com/dop251/goja" "github.com/lestrrat-go/jwx/v2/jwa" "github.com/pkg/errors" ) const ( RoleVisitor string = "visitor" RoleUser string = "user" RoleSuperuser string = "superuser" RoleAdmin string = "admin" RoleSuperadmin string = "superadmin" ) func authModuleFactory(keySet jwk.Set) app.ServerModuleFactory { return module.Extends( authModule.ModuleFactory( authModule.WithJWT(func() (jwk.Set, error) { return keySet, nil }), ), func(o *goja.Object) { if err := o.Set("ROLE_VISITOR", RoleVisitor); err != nil { panic(errors.New("could not set 'ROLE_VISITOR' property")) } if err := o.Set("ROLE_USER", RoleUser); err != nil { panic(errors.New("could not set 'ROLE_USER' property")) } if err := o.Set("ROLE_SUPERUSER", RoleSuperuser); err != nil { panic(errors.New("could not set 'ROLE_SUPERUSER' property")) } if err := o.Set("ROLE_ADMIN", RoleAdmin); err != nil { panic(errors.New("could not set 'ROLE_ADMIN' property")) } if err := o.Set("ROLE_SUPERADMIN", RoleSuperadmin); err != nil { panic(errors.New("could not set 'ROLE_SUPERADMIN' property")) } }, ) } func getAuthMount(auth *spec.Auth, keySet jwk.Set) (auth.MountFunc, error) { switch { case auth.Local != nil: var rawKey any = auth.Local.Key if strKey, ok := rawKey.(string); ok { rawKey = []byte(strKey) } key, err := jwk.FromRaw(rawKey) if err != nil { return nil, errors.WithStack(err) } cookieDuration := defaultCookieDuration if auth.Local.CookieDuration != "" { cookieDuration, err = time.ParseDuration(auth.Local.CookieDuration) if err != nil { return nil, errors.WithStack(err) } } return authModule.Mount( authHTTP.NewLocalHandler( jwa.HS256, key, authHTTP.WithRoutePrefix("/auth"), authHTTP.WithAccounts(auth.Local.Accounts...), authHTTP.WithCookieOptions(getCookieDomain, cookieDuration), ), authModule.WithJWT(func() (jwk.Set, error) { return keySet, nil }), ), nil default: return nil, nil } } func getAnonymousUserMiddleware(auth *appSpec.Auth) (func(http.Handler) http.Handler, error) { anonymousUserSigningKey, err := getAnonymousUserSigningKey(auth) if err != nil { return nil, errors.Wrap(err, "could not get anonymous user signing key") } cookieDuration := defaultCookieDuration if auth.Local.CookieDuration != "" { cookieDuration, err = time.ParseDuration(auth.Local.CookieDuration) if err != nil { return nil, errors.WithStack(err) } } middleware := authModuleMiddleware.AnonymousUser( anonymousUserSigningKey.Algorithm(), anonymousUserSigningKey, authModuleMiddleware.WithCookieOptions(getCookieDomain, cookieDuration), ) return middleware, nil } func getAnonymousUserSigningKey(auth *appSpec.Auth) (jwk.Key, error) { var ( key jwk.Key err error ) generateNewKey := func() (jwk.Key, error) { key, err := jwk.Generate(2048) if err != nil { return nil, errors.WithStack(err) } return key, nil } switch { default: fallthrough case auth == nil: key, err = generateNewKey() if err != nil { return nil, errors.Wrap(err, "could not generate anonymous user signing key") } return key, nil case auth.Local != nil: switch typedKey := auth.Local.Key.(type) { case string: key, err = jwk.FromRaw([]byte(typedKey)) if err != nil { return nil, errors.Wrap(err, "could not parse local auth key") } if err := key.Set(jwk.AlgorithmKey, jwa.HS256); err != nil { return nil, errors.WithStack(err) } default: return nil, errors.Errorf("unexpected key type '%T'", auth.Local.Key) } } return key, nil }