2023-04-11 12:05:19 +02:00
|
|
|
package app
|
|
|
|
|
|
|
|
import (
|
2023-09-20 18:02:59 +02:00
|
|
|
"net/http"
|
2023-04-20 12:23:17 +02:00
|
|
|
"time"
|
|
|
|
|
2023-09-20 18:02:59 +02:00
|
|
|
appSpec "forge.cadoles.com/Cadoles/emissary/internal/agent/controller/app/spec"
|
2023-04-11 12:05:19 +02:00
|
|
|
"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"
|
2023-04-20 12:23:17 +02:00
|
|
|
authModule "forge.cadoles.com/arcad/edge/pkg/module/auth"
|
|
|
|
authHTTP "forge.cadoles.com/arcad/edge/pkg/module/auth/http"
|
2023-09-20 18:02:59 +02:00
|
|
|
authModuleMiddleware "forge.cadoles.com/arcad/edge/pkg/module/auth/middleware"
|
|
|
|
|
2023-04-11 12:05:19 +02:00
|
|
|
"github.com/dop251/goja"
|
2023-04-20 12:23:17 +02:00
|
|
|
"github.com/lestrrat-go/jwx/v2/jwa"
|
2023-04-11 12:05:19 +02:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
RoleVisitor string = "visitor"
|
|
|
|
RoleUser string = "user"
|
|
|
|
RoleSuperuser string = "superuser"
|
|
|
|
RoleAdmin string = "admin"
|
|
|
|
RoleSuperadmin string = "superadmin"
|
|
|
|
)
|
|
|
|
|
2023-04-20 12:23:17 +02:00
|
|
|
func authModuleFactory(keySet jwk.Set) app.ServerModuleFactory {
|
2023-04-11 12:05:19 +02:00
|
|
|
return module.Extends(
|
2023-04-20 12:23:17 +02:00
|
|
|
authModule.ModuleFactory(
|
|
|
|
authModule.WithJWT(func() (jwk.Set, error) {
|
2023-04-11 12:05:19 +02:00
|
|
|
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"))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
2023-04-20 12:23:17 +02:00
|
|
|
|
2023-10-03 06:09:15 +02:00
|
|
|
func getAuthMount(auth *appSpec.Auth, keySet jwk.Set) (auth.MountFunc, error) {
|
2023-04-20 12:23:17 +02:00
|
|
|
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(
|
2023-10-03 06:09:15 +02:00
|
|
|
key,
|
|
|
|
jwa.HS256,
|
2023-04-20 12:23:17 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2023-09-20 18:02:59 +02:00
|
|
|
|
|
|
|
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,
|
2023-10-03 06:09:15 +02:00
|
|
|
auth.Local.SigningAlgorithm,
|
2023-09-20 18:02:59 +02:00
|
|
|
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
|
|
|
|
}
|