3
0

Add support for exporting auth assets, improve dev-exp

This commit is contained in:
Denis Arh 2021-03-26 07:21:40 +01:00
parent 1d761aad13
commit cd2b0c8998
6 changed files with 184 additions and 23 deletions

View File

@ -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(),
)

View File

@ -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
View 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
}

View File

@ -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

View File

@ -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")
}

View File

@ -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: |-