3
0
Files
corteza/provision/system/provision.go
2020-09-30 17:34:39 +02:00

260 lines
5.6 KiB
Go

package system
import (
"context"
"github.com/cortezaproject/corteza-server/pkg/id"
impAux "github.com/cortezaproject/corteza-server/pkg/importer"
"github.com/cortezaproject/corteza-server/pkg/rbac"
"github.com/cortezaproject/corteza-server/pkg/settings"
"github.com/cortezaproject/corteza-server/store"
"github.com/cortezaproject/corteza-server/system/importer"
"github.com/cortezaproject/corteza-server/system/service"
"github.com/cortezaproject/corteza-server/system/types"
"go.uber.org/zap"
"gopkg.in/yaml.v2"
"io"
"time"
)
type (
settingsService interface {
FindByPrefix(context.Context, ...string) (types.SettingValueSet, error)
BulkSet(context.Context, types.SettingValueSet) error
}
)
// Check if any roels
func checkRoles(ctx context.Context, s store.Storer) (bool, error) {
if set, _, err := store.SearchRoles(ctx, s, types.RoleFilter{}); err != nil {
return false, err
} else {
return len(set) > 0, nil
}
}
// Check if any RBAC rules exist
func checkRbacRules(ctx context.Context, s store.Storer) (bool, error) {
if set, _, err := store.SearchRbacRules(ctx, s, rbac.RuleFilter{}); err != nil {
return false, err
} else {
return len(set) > 0, nil
}
}
func Provision(ctx context.Context, log *zap.Logger, s store.Storer) (err error) {
var (
hasRoles, hasRbacRules bool
readers []io.Reader
)
if hasRoles, err = checkRoles(ctx, s); err != nil {
return err
} else if !hasRoles {
rr := types.RoleSet{
&types.Role{ID: rbac.AdminsRoleID, Name: "Administrators", Handle: "admins"},
&types.Role{ID: rbac.EveryoneRoleID, Name: "Everyone", Handle: "everyone"},
}
if err = rr.Walk(func(r *types.Role) error { return store.CreateRole(ctx, s, r) }); err != nil {
return err
}
}
if hasRbacRules, err = checkRbacRules(ctx, s); err != nil {
return err
}
if !hasRbacRules {
log.Info("provisioning system")
readers, err = impAux.ReadStatic(Asset)
if err != nil {
return err
}
if err = importer.Import(ctx, readers...); err != nil {
return err
}
} else {
log.Info("provisioning system settings")
// When already provisioned, make sure settings are re-provisioned
readers, err = impAux.ReadStatic(Asset)
if err != nil {
return err
}
if err = partialImportSettings(ctx, service.DefaultSettings, readers...); err != nil {
return err
}
}
if err = makeDefaultApplications(ctx, log, s); err != nil {
return
}
if err = authSettingsAutoDiscovery(ctx, log, service.DefaultSettings); err != nil {
return
}
if err = authAddExternals(ctx, log); err != nil {
return
}
if err = service.DefaultSettings.UpdateCurrent(ctx); err != nil {
return
}
if err = oidcAutoDiscovery(ctx, log); err != nil {
return
}
return nil
}
// Updates default application directly in the store
func makeDefaultApplications(ctx context.Context, log *zap.Logger, s store.Storer) error {
var (
now = time.Now()
aa, _, err = s.SearchApplications(ctx, types.ApplicationFilter{})
)
if err != nil {
return err
}
// Update icon & logo on all apps
const (
oldIconUrl = "/applications/crust_favicon.png"
oldLogoUrl = "/applications/crust.jpg"
newIconUrl = "/applications/default_icon.png"
newLogoUrl = "/applications/default_logo.jpg"
)
for a := 0; a < len(aa); a++ {
var dirty bool
if aa[a].Unify == nil {
continue
}
if aa[a].Unify.Icon == oldIconUrl {
aa[a].Unify.Icon = newIconUrl
dirty = true
}
if aa[a].Unify.Logo == oldLogoUrl {
aa[a].Unify.Logo = newLogoUrl
dirty = true
}
if !dirty {
continue
}
aa[a].UpdatedAt = &now
if err = s.UpdateApplication(ctx, aa[a]); err != nil {
return err
}
}
// List of apps to create.
//
// We use Unify.Url field for matching,
// so make sure it's always present!
defApps := types.ApplicationSet{
&types.Application{
Name: "CRM",
Enabled: true,
Unify: &types.ApplicationUnify{
Name: "CRM",
Listed: true,
Icon: newIconUrl,
Logo: newLogoUrl,
Url: "/compose/ns/crm/pages",
},
},
&types.Application{
Name: "Service Solution",
Enabled: true,
Unify: &types.ApplicationUnify{
Name: "Service Solution",
Listed: true,
Icon: newIconUrl,
Logo: newLogoUrl,
Url: "/compose/ns/service-solution/pages",
},
},
}
return defApps.Walk(func(defApp *types.Application) error {
for _, a := range aa {
if a.Unify != nil && a.Unify.Url == defApp.Unify.Url {
// App already added.
return nil
}
}
defApp.ID = id.Next()
defApp.CreatedAt = time.Now()
err = s.CreateApplication(ctx, defApp)
log.Info(
"creating default application",
zap.String("name", defApp.Name),
zap.Uint64("name", defApp.ID),
zap.Error(err),
)
return err
})
}
// Partial import of settings from provision files
func partialImportSettings(ctx context.Context, ss settingsService, ff ...io.Reader) (err error) {
var (
// decoded content from YAML files
aux interface{}
si = settings.NewImporter()
// importer w/o permissions & roles
// we need only settings
imp = importer.NewImporter(nil, si, nil)
// current value
current types.SettingValueSet
// unexisting values
unex types.SettingValueSet
)
for _, f := range ff {
if err = yaml.NewDecoder(f).Decode(&aux); err != nil {
return
}
err = imp.Cast(aux)
if err != nil {
return
}
}
// Get all "current" settings storage
current, err = ss.FindByPrefix(ctx)
if err != nil {
return
}
// Compare current settings with imported, get all that do not exist yet
if unex = si.GetValues(); len(unex) > 0 {
// Store non existing
err = ss.BulkSet(ctx, current.New(unex))
if err != nil {
return
}
}
return nil
}