Add support for exporting auth assets, improve dev-exp
This commit is contained in:
parent
1d761aad13
commit
cd2b0c8998
@ -3,6 +3,7 @@ package app
|
||||
import (
|
||||
"context"
|
||||
|
||||
authCommands "github.com/cortezaproject/corteza-server/auth/commands"
|
||||
federationCommands "github.com/cortezaproject/corteza-server/federation/commands"
|
||||
"github.com/cortezaproject/corteza-server/pkg/cli"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rbac"
|
||||
@ -51,7 +52,6 @@ func (app *CortezaApp) InitCLI() {
|
||||
app.Command.AddCommand(
|
||||
systemCommands.Users(app),
|
||||
systemCommands.Roles(app),
|
||||
systemCommands.Auth(app, app.Opt.Auth),
|
||||
systemCommands.RBAC(app),
|
||||
systemCommands.Sink(app),
|
||||
systemCommands.Settings(app),
|
||||
@ -60,6 +60,7 @@ func (app *CortezaApp) InitCLI() {
|
||||
serveCmd,
|
||||
upgradeCmd,
|
||||
provisionCmd,
|
||||
authCommands.General(app, app.Opt.Auth),
|
||||
federationCommands.Sync(app),
|
||||
cli.VersionCommand(),
|
||||
)
|
||||
|
||||
70
auth/auth.go
70
auth/auth.go
@ -22,6 +22,7 @@ import (
|
||||
"go.uber.org/zap"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -38,7 +39,7 @@ type (
|
||||
)
|
||||
|
||||
//go:embed assets/public
|
||||
var publicAssets embed.FS
|
||||
var PublicAssets embed.FS
|
||||
|
||||
// New initializes Auth service that orchestrates session manager, oauth2 manager and http request handlers
|
||||
func New(ctx context.Context, log *zap.Logger, s store.Storer, opt options.AuthOpt) (svc *service, err error) {
|
||||
@ -52,12 +53,12 @@ func New(ctx context.Context, log *zap.Logger, s store.Storer, opt options.AuthO
|
||||
|
||||
svc = &service{
|
||||
opt: opt,
|
||||
log: log,
|
||||
log: log.WithOptions(zap.AddStacktrace(zap.PanicLevel)),
|
||||
store: s,
|
||||
settings: &settings.Settings{ /* all disabled by default. */ },
|
||||
}
|
||||
|
||||
// use modified log ger for the resrt
|
||||
// use modified logger for the rest
|
||||
if opt.LogEnabled {
|
||||
log = log.WithOptions(zap.AddStacktrace(zap.PanicLevel))
|
||||
} else {
|
||||
@ -157,6 +158,8 @@ func New(ctx context.Context, log *zap.Logger, s store.Storer, opt options.AuthO
|
||||
}
|
||||
|
||||
var (
|
||||
tplLoader templateLoader
|
||||
|
||||
tplBase = template.New("").
|
||||
Funcs(sprig.FuncMap()).
|
||||
Funcs(template.FuncMap{
|
||||
@ -164,10 +167,14 @@ func New(ctx context.Context, log *zap.Logger, s store.Storer, opt options.AuthO
|
||||
"buildtime": func() string { return version.BuildTime },
|
||||
"links": handlers.GetLinks,
|
||||
})
|
||||
tplLoader templateLoader
|
||||
|
||||
useEmbedded = len(opt.AssetsPath) == 0
|
||||
)
|
||||
|
||||
if len(opt.AssetsPath) > 0 {
|
||||
if useEmbedded {
|
||||
tplLoader = EmbeddedTemplates
|
||||
log.Info("using embedded templates")
|
||||
} else {
|
||||
tplLoader = func(t *template.Template) (tpl *template.Template, err error) {
|
||||
if tpl, err = t.Clone(); err != nil {
|
||||
return nil, fmt.Errorf("cannot clone templates: %w", err)
|
||||
@ -175,21 +182,27 @@ func New(ctx context.Context, log *zap.Logger, s store.Storer, opt options.AuthO
|
||||
return tpl.ParseGlob(opt.AssetsPath + "/templates/*.tpl")
|
||||
}
|
||||
}
|
||||
log.Info("loading assets from filesystem", zap.String("path", opt.AssetsPath))
|
||||
} else {
|
||||
tplLoader = EmbeddedTemplates
|
||||
log.Info("using embedded assets")
|
||||
log.Info(
|
||||
"loading templates from filesystem",
|
||||
zap.String("AUTH_ASSETS_PATH", svc.opt.AssetsPath),
|
||||
)
|
||||
}
|
||||
|
||||
if !opt.DevelopmentMode || len(opt.AssetsPath) == 0 {
|
||||
log.Info("initializing templates without reloading (production mode)")
|
||||
if !useEmbedded && opt.DevelopmentMode {
|
||||
log.Info(
|
||||
"initializing reloadable templates",
|
||||
zap.Bool("AUTH_DEVELOPMENT_MODE", opt.DevelopmentMode),
|
||||
)
|
||||
tpls = NewReloadableTemplates(tplBase, tplLoader)
|
||||
} else {
|
||||
log.Info(
|
||||
"initializing templates without reloading",
|
||||
zap.Bool("AUTH_DEVELOPMENT_MODE", opt.DevelopmentMode),
|
||||
)
|
||||
tpls, err = NewStaticTemplates(tplBase, tplLoader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot load templates: %w", err)
|
||||
}
|
||||
} else {
|
||||
log.Info("initializing reloadable templates (development mode)")
|
||||
tpls = NewReloadableTemplates(tplBase, tplLoader)
|
||||
}
|
||||
|
||||
svc.handlers = &handlers.AuthHandlers{
|
||||
@ -294,12 +307,33 @@ func (svc service) MountHttpRoutes(r chi.Router) {
|
||||
svc.handlers.MountHttpRoutes(r)
|
||||
|
||||
const uriRoot = "/auth/assets/public"
|
||||
if len(svc.opt.AssetsPath) == 0 && !svc.opt.DevelopmentMode {
|
||||
r.Handle(uriRoot+"/*", http.StripPrefix("/auth/", http.FileServer(http.FS(publicAssets))))
|
||||
} else {
|
||||
if svc.opt.DevelopmentMode {
|
||||
var root = strings.TrimRight(svc.opt.AssetsPath, "/") + "/public"
|
||||
r.Handle(uriRoot+"/*", http.StripPrefix(uriRoot, http.FileServer(http.Dir(root))))
|
||||
|
||||
if err := dirCheck(root); err != nil {
|
||||
svc.log.Error(
|
||||
"failed to run in development mode (AUTH_DEVELOPMENT_MODE=true)",
|
||||
zap.Error(err),
|
||||
zap.String("AUTH_ASSETS_PATH", svc.opt.AssetsPath),
|
||||
)
|
||||
} else {
|
||||
r.Handle(uriRoot+"/*", http.StripPrefix(uriRoot, http.FileServer(http.Dir(root))))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to embedded assets
|
||||
r.Handle(uriRoot+"/*", http.StripPrefix("/auth/", http.FileServer(http.FS(PublicAssets))))
|
||||
}
|
||||
|
||||
// checks if directory exists & is readable
|
||||
func dirCheck(path string) (err error) {
|
||||
_, err = os.Stat(path)
|
||||
if !os.IsNotExist(err) {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//func (svc service) WellKnownOpenIDConfiguration() http.HandlerFunc {
|
||||
|
||||
109
auth/commands/assets.go
Normal file
109
auth/commands/assets.go
Normal file
@ -0,0 +1,109 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"github.com/cortezaproject/corteza-server/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/cli"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
func assets(opt options.AuthOpt) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "assets",
|
||||
Short: "Authentication flow assets (styling, images) and templates",
|
||||
}
|
||||
|
||||
exportCmd := &cobra.Command{
|
||||
Use: "export",
|
||||
Short: "Exports embedded assets into provided path (must exists)",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
assetsRoot := opt.AssetsPath
|
||||
if len(args) > 0 {
|
||||
assetsRoot = args[0]
|
||||
}
|
||||
|
||||
if len(assetsRoot) == 0 {
|
||||
cmd.Println("can not export, no path provided and AUTH_ASSETS_PATH is empty")
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := os.Stat(assetsRoot); err != nil {
|
||||
cli.HandleError(err)
|
||||
}
|
||||
|
||||
emb := map[string]embed.FS{
|
||||
"public": auth.PublicAssets,
|
||||
"templates": auth.Templates,
|
||||
}
|
||||
|
||||
var (
|
||||
fh *os.File
|
||||
buf []byte
|
||||
dst string
|
||||
src string
|
||||
assetsSub string
|
||||
)
|
||||
for dir, efs := range emb {
|
||||
assetsSub = path.Join(assetsRoot, dir)
|
||||
|
||||
cmd.Printf("exporting auth assets to %s\n", dir)
|
||||
if _, err := os.Stat(assetsSub); os.IsNotExist(err) {
|
||||
cli.HandleError(os.Mkdir(assetsSub, 0755))
|
||||
cmd.Println("directory created")
|
||||
} else if err != nil {
|
||||
cli.HandleError(err)
|
||||
}
|
||||
|
||||
cc, err := efs.ReadDir(path.Join("assets", dir))
|
||||
if err != nil {
|
||||
cli.HandleError(err)
|
||||
}
|
||||
|
||||
for _, c := range cc {
|
||||
src = path.Join("assets", dir, c.Name())
|
||||
dst = path.Join(assetsSub, c.Name())
|
||||
|
||||
if c.IsDir() {
|
||||
cmd.Println("skipping directory:", assetsRoot)
|
||||
continue
|
||||
}
|
||||
|
||||
cmd.Print("exporting asset ", dst, ": ")
|
||||
|
||||
buf, err = efs.ReadFile(src)
|
||||
if err != nil {
|
||||
cmd.Println("\n => error:", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
fh, err = os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
|
||||
if os.IsExist(err) {
|
||||
cmd.Println("exists")
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
cmd.Println("\n => error:", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = fh.Write(buf)
|
||||
if err != nil {
|
||||
cmd.Println("\n => error:", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
cmd.Println("ok")
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
cmd.AddCommand(exportCmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza-server/auth/external"
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/cli"
|
||||
@ -10,8 +11,20 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type (
|
||||
serviceInitializer interface {
|
||||
InitServices(ctx context.Context) error
|
||||
}
|
||||
)
|
||||
|
||||
func commandPreRunInitService(app serviceInitializer) func(*cobra.Command, []string) error {
|
||||
return func(_ *cobra.Command, _ []string) error {
|
||||
return app.InitServices(cli.Context())
|
||||
}
|
||||
}
|
||||
|
||||
// Will perform OpenID connect auto-configuration
|
||||
func Auth(app serviceInitializer, opt options.AuthOpt) *cobra.Command {
|
||||
func General(app serviceInitializer, opt options.AuthOpt) *cobra.Command {
|
||||
var (
|
||||
enableDiscoveredProvider bool
|
||||
skipValidationOnAutoDiscoveredProvider bool
|
||||
@ -19,7 +32,7 @@ func Auth(app serviceInitializer, opt options.AuthOpt) *cobra.Command {
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "auth",
|
||||
Short: "External authentication",
|
||||
Short: "Authentication management",
|
||||
}
|
||||
|
||||
autoDiscoverCmd := &cobra.Command{
|
||||
@ -111,6 +124,7 @@ func Auth(app serviceInitializer, opt options.AuthOpt) *cobra.Command {
|
||||
autoDiscoverCmd,
|
||||
testEmails,
|
||||
jwtCmd,
|
||||
assets(opt),
|
||||
)
|
||||
|
||||
return cmd
|
||||
@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
//go:embed assets/templates/*.tpl
|
||||
var embeddedTemplates embed.FS
|
||||
var Templates embed.FS
|
||||
|
||||
type (
|
||||
templateLoader func(tpls *template.Template) (tpl *template.Template, err error)
|
||||
@ -62,5 +62,5 @@ func (t templateStatic) ExecuteTemplate(w io.Writer, name string, data interface
|
||||
|
||||
// EmbeddedTemplates returns embedded templates.
|
||||
func EmbeddedTemplates(t *template.Template) (tpl *template.Template, err error) {
|
||||
return t.ParseFS(embeddedTemplates, "assets/templates/*.html.tpl")
|
||||
return t.ParseFS(Templates, "assets/templates/*.html.tpl")
|
||||
}
|
||||
|
||||
@ -142,6 +142,9 @@ props:
|
||||
When corteza starts, if path exists it tries to load template files from it.
|
||||
If not it uses statically embedded files.
|
||||
|
||||
When empty path is set (default value), embedded files are used.
|
||||
|
||||
|
||||
- name: developmentMode
|
||||
type: bool
|
||||
description: |-
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user