3
0

Refactored configuration provisioning

This commit is contained in:
Denis Arh
2019-09-26 15:27:26 +02:00
parent 792c2aa62d
commit c240bb6a20
45 changed files with 11328 additions and 423 deletions
+7 -11
View File
@@ -120,14 +120,10 @@ function database {
}
function files {
yellow "> files"
FOLDERS=$(find . -type d -wholename '*/data')
for FOLDER in $FOLDERS; do
FOLDER=$(dirname $FOLDER)
FOLDER=${FOLDER:2}
echo $FOLDER
cd $FOLDER && $GOPATH/bin/statik -p files -m -Z -f -src=data && cd $_PWD
function provision {
yellow "> provision files"
for FOLDER in system compose messaging; do
$GOPATH/bin/statik -p $FOLDER -m -Z -f -src="./provision/$FOLDER/src" -dest "./provision"
done
green "OK"
}
@@ -200,8 +196,8 @@ case ${1:-"all"} in
database)
database
;;
files)
files
provision)
provision
;;
specs)
specs
@@ -212,7 +208,7 @@ case ${1:-"all"} in
all)
types
database
files
provision
specs
proto
esac
-19
View File
@@ -1,19 +0,0 @@
package compose
import (
"context"
"github.com/spf13/cobra"
"github.com/cortezaproject/corteza-server/compose/service"
"github.com/cortezaproject/corteza-server/pkg/cli"
)
func accessControlSetup(ctx context.Context, cmd *cobra.Command, c *cli.Config) error {
c.InitServices(ctx, c)
// Calling grant directly on internal permissions service to avoid AC check for "grant"
var p = service.DefaultPermissions
var ac = service.DefaultAccessControl
return p.Grant(ctx, ac.Whitelist(), ac.DefaultRules()...)
}
+47 -32
View File
@@ -9,6 +9,7 @@ import (
"strings"
sqlTypes "github.com/jmoiron/sqlx/types"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
@@ -47,6 +48,10 @@ func Exporter(ctx context.Context, c *cli.Config) *cobra.Command {
nsOut = Namespace{}
)
if nsFlag == "" {
cli.HandleError(errors.New("Specify namespace to export from"))
}
if namespaceID, _ := strconv.ParseUint(nsFlag, 10, 64); namespaceID > 0 {
ns, err = service.DefaultNamespace.FindByID(namespaceID)
if err != repository.ErrNamespaceNotFound {
@@ -65,8 +70,8 @@ func Exporter(ctx context.Context, c *cli.Config) *cobra.Command {
//
// Roles are use for resolving access control
roles = sysTypes.RoleSet{
&sysTypes.Role{ID: 1, Handle: "everyone"},
&sysTypes.Role{ID: 2, Handle: "admins"},
&sysTypes.Role{ID: permissions.EveryoneRoleID, Handle: "everyone"},
&sysTypes.Role{ID: permissions.AdminRoleID, Handle: "admins"},
}
modules, _, err := service.DefaultModule.Find(types.ModuleFilter{NamespaceID: ns.ID})
@@ -90,13 +95,13 @@ func Exporter(ctx context.Context, c *cli.Config) *cobra.Command {
y := yaml.NewEncoder(cmd.OutOrStdout())
nsOut.Name = ns.Name
nsOut.Handle = ns.Slug
nsOut.Enabled = ns.Enabled
nsOut.Meta = ns.Meta
nsOut.Allow = expResourcePermissions(permissions.Allow, ns.PermissionResource())
nsOut.Deny = expResourcePermissions(permissions.Deny, ns.PermissionResource())
// nsOut.Name = ns.Name
// nsOut.Handle = ns.Slug
// nsOut.Enabled = ns.Enabled
// nsOut.Meta = ns.Meta
//
// nsOut.Allow = expResourcePermissions(permissions.Allow, ns.PermissionResource())
// nsOut.Deny = expResourcePermissions(permissions.Deny, ns.PermissionResource())
for _, arg := range args {
switch arg {
@@ -114,14 +119,15 @@ func Exporter(ctx context.Context, c *cli.Config) *cobra.Command {
}
}
out.Namespaces[ns.Slug] = nsOut
// out.Namespaces[ns.Slug] = nsOut
nsOut.Namespace = ns.Slug
_, _ = y, out
cli.HandleError(y.Encode(out))
cli.HandleError(y.Encode(nsOut))
},
}
cmd.Flags().String("namespace", "crm", "Export namespace resources (by ID or string)")
cmd.Flags().String("namespace", "", "Export namespace resources (by ID or string)")
return cmd
}
@@ -138,13 +144,16 @@ type (
}
Namespace struct {
// This is used when exporting one single namespace
Namespace string `yaml:",omitempty"`
Name string `yaml:",omitempty"`
Handle string `yaml:",omitempty"`
Enabled bool `yaml:",omitempty"`
Meta types.NamespaceMeta `yaml:",omitempty"`
Modules map[string]Module `yaml:",omitempty"`
Pages map[string]Page `yaml:",omitempty"`
Pages yaml.MapSlice `yaml:",omitempty"`
Charts map[string]Chart `yaml:",omitempty"`
Scripts map[string]Script `yaml:",omitempty"`
@@ -155,7 +164,7 @@ type (
Module struct {
Name string
Meta string `yaml:"meta,omitempty"`
Fields map[string]Field
Fields yaml.MapSlice
Allow map[string][]string `yaml:",omitempty"`
Deny map[string][]string `yaml:",omitempty"`
@@ -184,7 +193,7 @@ type (
Blocks types.PageBlocks `yaml:",omitempty"`
Pages map[string]Page `yaml:",omitempty"`
Pages yaml.MapSlice `yaml:",omitempty"`
Visible bool
@@ -275,21 +284,24 @@ func expModuleMetaCleanup(meta sqlTypes.JSONText) string {
return meta.String()
}
func expModuleFields(ff types.ModuleFieldSet, modules types.ModuleSet) (o map[string]Field) {
o = make(map[string]Field)
func expModuleFields(ff types.ModuleFieldSet, modules types.ModuleSet) (o yaml.MapSlice) {
o = make(yaml.MapSlice, len(ff))
for _, f := range ff {
o[f.Name] = Field{
Label: f.Label,
Kind: f.Kind,
Options: expModuleFieldOptions(f, modules),
Private: f.Private,
Required: f.Required,
Visible: f.Visible,
Multi: f.Multi,
for i, f := range ff {
o[i] = yaml.MapItem{
Key: f.Name,
Value: Field{
Label: f.Label,
Kind: f.Kind,
Options: expModuleFieldOptions(f, modules),
Private: f.Private,
Required: f.Required,
Visible: f.Visible,
Multi: f.Multi,
Allow: expResourcePermissions(permissions.Allow, types.ModuleFieldPermissionResource),
Deny: expResourcePermissions(permissions.Deny, types.ModuleFieldPermissionResource),
Allow: expResourcePermissions(permissions.Allow, types.ModuleFieldPermissionResource),
Deny: expResourcePermissions(permissions.Deny, types.ModuleFieldPermissionResource),
},
}
}
@@ -352,12 +364,12 @@ func expModuleFieldOptions(f *types.ModuleField, modules types.ModuleSet) types.
return out
}
func expPages(parentID uint64, pages types.PageSet, modules types.ModuleSet, charts types.ChartSet, scripts automation.ScriptSet) (o map[string]Page) {
func expPages(parentID uint64, pages types.PageSet, modules types.ModuleSet, charts types.ChartSet, scripts automation.ScriptSet) (o yaml.MapSlice) {
var (
children = pages.FindByParent(parentID)
handle string
)
o = map[string]Page{}
o = yaml.MapSlice{}
for _, child := range children {
page := Page{
@@ -388,7 +400,10 @@ func expPages(parentID uint64, pages types.PageSet, modules types.ModuleSet, cha
pagesHandles[handle] = true
o[handle] = page
o = append(o, yaml.MapItem{
Key: handle,
Value: page,
})
}
return
@@ -698,7 +713,7 @@ func makeHandleFromName(name, currentHandle, def string, id uint64) string {
return currentHandle
}
newHandle := strings.ReplaceAll(name, " ", "_")
newHandle := strings.ReplaceAll(strings.Title(name), " ", "")
newHandle = regexp.MustCompile(`[^0-9A-Za-z_\-.]+`).ReplaceAllString(newHandle, "")
if handle.IsValid(newHandle) {
return newHandle
+4 -4
View File
@@ -51,8 +51,8 @@ func Configure() *cli.Config {
c.InitServices(ctx, c)
if c.ProvisionOpt.AutoSetup {
cli.HandleError(accessControlSetup(ctx, cmd, c))
if c.ProvisionOpt.Configuration {
cli.HandleError(provisionConfig(ctx, cmd, c))
}
go service.Watchers(ctx)
@@ -86,8 +86,8 @@ func Configure() *cli.Config {
},
},
ProvisionAccessControl: cli.Runners{
accessControlSetup,
ProvisionConfig: cli.Runners{
provisionConfig,
},
}
}
+5
View File
@@ -224,6 +224,11 @@ func (asImp *AutomationScript) Store(ctx context.Context, k automationScriptKeep
func (asImp *AutomationScript) resolveRefs() error {
for _, ref := range asImp.modRefs {
s := asImp.set.FindByName(ref.as, asImp.namespace.ID)
if s == nil {
// try to find it in no-mans land
s = asImp.set.FindByName(ref.as, 0)
}
if s == nil {
return errors.Errorf("invalid reference, unknown automation script (%v)", ref)
}
+2 -2
View File
@@ -30,8 +30,8 @@ func Import(ctx context.Context, ns *types.Namespace, ff ...io.Reader) (err erro
//
// Roles are use for resolving access control
roles = sysTypes.RoleSet{
&sysTypes.Role{ID: 1, Handle: "everyone"},
&sysTypes.Role{ID: 2, Handle: "admins"},
&sysTypes.Role{ID: permissions.EveryoneRoleID, Handle: "everyone"},
&sysTypes.Role{ID: permissions.AdminRoleID, Handle: "admins"},
}
)
+8 -22
View File
@@ -147,27 +147,7 @@ func (mImp *Module) Cast(handle string, def interface{}) (err error) {
func (mImp *Module) castFields(module *types.Module, def interface{}) (err error) {
return deinterfacer.Each(def, func(_ int, fieldName string, val interface{}) (err error) {
if fieldKind, ok := val.(string); ok && fieldName != "" {
// Not much more to do here
field := module.Fields.FindByName(fieldName)
if field == nil {
module.Fields = append(module.Fields, &types.ModuleField{
Kind: fieldKind,
Name: fieldName,
Label: fieldName,
})
} else {
field.Kind = fieldKind
}
return
}
if !deinterfacer.IsMap(def) {
return errors.New("expecting map of values for module field")
}
deinterfacer.KVsetString(&fieldName, "name", def)
deinterfacer.KVsetString(&fieldName, "name", val)
field := module.Fields.FindByName(fieldName)
if field == nil {
@@ -182,21 +162,27 @@ func (mImp *Module) castFields(module *types.Module, def interface{}) (err error
return deinterfacer.Each(val, func(_ int, key string, val interface{}) (err error) {
switch key {
case "name":
// already handled
case "label":
field.Label = deinterfacer.ToString(val)
case "kind", "type":
field.Kind = deinterfacer.ToString(val)
case "options":
// @todo ModuleField.Options
return mImp.castFieldOptions(field, val)
case "private":
field.Private = deinterfacer.ToBool(val)
case "required":
field.Required = deinterfacer.ToBool(val)
case "visible":
field.Visible = deinterfacer.ToBool(val)
case "multi":
field.Multi = deinterfacer.ToBool(val)
+22 -19
View File
@@ -9,32 +9,35 @@ import (
)
func TestModuleImport_CastSet(t *testing.T) {
impFixTester(t, "module_full", func(t *testing.T, module *Module) {
stdFieldsAsTest := func(t *testing.T, module *Module) {
// map definition's sort order is unreliable
req := require.New(t)
req.Len(module.set, 1)
tc := module.set.FindByHandle("mod1")
req.Equal(tc.Name, "Module with fields")
req.Equal(tc.Fields, types.ModuleFieldSet{
{
Name: "f1",
Kind: "Number",
Label: "f1",
},
{
req.Len(tc.Fields, 2)
req.Equal(tc.Fields.FindByName("f1"), &types.ModuleField{
Name: "f1",
Kind: "Number",
Label: "f1",
})
Name: "f2",
Kind: "String",
Label: "F2",
Place: 1,
Required: true,
Options: map[string]interface{}{
"multiLine": true,
"useRichTextEditor": true,
"multiDelimiter": "\n",
},
req.Equal(tc.Fields.FindByName("f2"), &types.ModuleField{
Name: "f2",
Kind: "String",
Label: "F2",
Place: 1,
Required: true,
Options: map[string]interface{}{
"multiLine": true,
"useRichTextEditor": true,
"multiDelimiter": "\n",
},
})
})
}
impFixTester(t, "module_fields_as_slice", stdFieldsAsTest)
impFixTester(t, "module_fields_as_map", stdFieldsAsTest)
}
+4
View File
@@ -247,14 +247,18 @@ func (nsImp *Namespace) Store(ctx context.Context, nsk namespaceKeeper, mk modul
if err = nsImp.modules[handle].Store(ctx, mk); err != nil {
return errors.Wrap(err, "could not import modules")
}
nsImp.scripts[handle].namespace = namespace
if err = nsImp.scripts[handle].Store(ctx, sk); err != nil {
return errors.Wrap(err, "could not import automation scripts")
}
nsImp.charts[handle].namespace = namespace
if err = nsImp.charts[handle].Store(ctx, ck); err != nil {
return errors.Wrap(err, "could not import charts")
}
nsImp.pages[handle].namespace = namespace
if err = nsImp.pages[handle].Store(ctx, pk); err != nil {
return errors.Wrap(err, "could not import pages")
}
+135 -94
View File
@@ -3,7 +3,6 @@ package importer
import (
"context"
"fmt"
"sort"
"strconv"
"github.com/pkg/errors"
@@ -131,9 +130,6 @@ func (pImp *Page) cast(parent, handle string, def interface{}) (err error) {
pImp.tree[parent] = append(pImp.tree[parent], handle)
// Make pages are always sorted
sort.Strings(pImp.tree[parent])
if title, ok := def.(string); ok && title != "" {
page.Title = title
return nil
@@ -280,17 +276,29 @@ func (pImp *Page) storeChildren(ctx context.Context, parent string, k pageKeeper
return nil
}
var parentPage *types.Page
if parent != "" {
parentPage, err = pImp.Get(parent)
if err != nil {
return
} else if parentPage == nil {
return errors.Errorf("could not load parent %q", parent)
}
}
var page *types.Page
for _, child := range children {
for w, child := range children {
if page, err = pImp.Get(child); err != nil {
return
}
if err = pImp.resolveRefs(page); err != nil {
return
if parentPage != nil {
page.SelfID = parentPage.ID
}
page.Weight = w
if page.ID == 0 {
page.NamespaceID = pImp.namespace.ID
page, err = k.Create(page)
@@ -314,127 +322,160 @@ func (pImp *Page) storeChildren(ctx context.Context, parent string, k pageKeeper
}
}
// We do that at the end - and save all pages with resolved references
//
// Many because internal page referencing from page blocks
var refs uint
for _, child := range children {
if page, err = pImp.Get(child); err != nil {
return
}
if refs, err = pImp.resolveRefs(page); err != nil {
return
} else if refs > 0 {
// make sure we do not get stale-data error
page.UpdatedAt = nil
if _, err = k.Update(page); err != nil {
return errors.Wrap(err, "could not update resolved refs")
}
}
}
return
}
// Resolve all refs for this page (page module, inside block)
func (pImp *Page) resolveRefs(page *types.Page) error {
if moduleHandle, ok := pImp.modules[page.Handle]; ok {
if module, err := pImp.getModule(moduleHandle); err != nil {
return err
} else if module == nil {
return errors.Wrapf(err, "could not load module %q for page %q",
moduleHandle, page.Handle)
} else {
page.ModuleID = module.ID
}
}
//
// It counts number of resolved refs so that caller can know
// if there is anything to save
func (pImp *Page) resolveRefs(page *types.Page) (uint, error) {
var refs uint
for i, b := range page.Blocks {
if b.Options == nil {
continue
}
if h, ok := b.Options["module"]; ok {
if refm, err := pImp.getModule(deinterfacer.ToString(h)); err != nil || refm == nil {
return errors.Errorf("could not load module %q for page %q block #%d (%v)",
h, page.Handle, i+1, err)
return refs, func() error {
if moduleHandle, ok := pImp.modules[page.Handle]; ok {
if module, err := pImp.getModule(moduleHandle); err != nil {
return err
} else if module == nil {
return errors.Wrapf(err, "could not load module %q for page %q",
moduleHandle, page.Handle)
} else {
b.Options["moduleID"] = strconv.FormatUint(refm.ID, 10)
delete(b.Options, "module")
page.ModuleID = module.ID
refs++
}
}
if h, ok := b.Options["page"]; ok {
if refp, err := pImp.Get(deinterfacer.ToString(h)); err != nil || refp == nil {
return errors.Errorf("could not load page %q for page %q block #%d (%v)",
h, page.Handle, i+1, err)
} else {
b.Options["pageID"] = strconv.FormatUint(refp.ID, 10)
delete(b.Options, "page")
for i, b := range page.Blocks {
if b.Options == nil {
continue
}
}
if h, ok := b.Options["chart"]; ok {
if refc, err := pImp.getChart(deinterfacer.ToString(h)); err != nil || refc == nil {
return errors.Errorf("could not load chart %q for page %q block #%d (%v)",
h, page.Handle, i+1, err)
} else {
b.Options["chartID"] = strconv.FormatUint(refc.ID, 10)
delete(b.Options, "chart")
if h, ok := b.Options["module"]; ok {
if refm, err := pImp.getModule(deinterfacer.ToString(h)); err != nil || refm == nil {
return errors.Errorf("could not load module %q for page %q block #%d (%v)",
h, page.Handle, i+1, err)
} else {
b.Options["moduleID"] = strconv.FormatUint(refm.ID, 10)
delete(b.Options, "module")
refs++
}
}
}
if b.Kind == "Automation" {
bb := make([]interface{}, 0)
err := deinterfacer.Each(b.Options["buttons"], func(_ int, _ string, btn interface{}) (err error) {
button := map[string]interface{}{}
if h, ok := b.Options["page"]; ok {
if refp, err := pImp.Get(deinterfacer.ToString(h)); err != nil || refp == nil {
return errors.Errorf("could not load page %q for page %q block #%d (%v)",
h, page.Handle, i+1, err)
} else {
b.Options["pageID"] = strconv.FormatUint(refp.ID, 10)
delete(b.Options, "page")
refs++
}
}
err = deinterfacer.Each(btn, func(_ int, k string, v interface{}) error {
switch k {
case "script":
if s, err := pImp.getScript(deinterfacer.ToString(v)); err != nil || s == nil {
return errors.Errorf("could not load script %q for page %q block #%d (%v)",
v, page.Handle, i+1, err)
} else {
button["scriptID"] = s.ID
if h, ok := b.Options["chart"]; ok {
if refc, err := pImp.getChart(deinterfacer.ToString(h)); err != nil || refc == nil {
return errors.Errorf("could not load chart %q for page %q block #%d (%v)",
h, page.Handle, i+1, err)
} else {
b.Options["chartID"] = strconv.FormatUint(refc.ID, 10)
delete(b.Options, "chart")
refs++
}
}
if b.Kind == "Automation" {
bb := make([]interface{}, 0)
err := deinterfacer.Each(b.Options["buttons"], func(_ int, _ string, btn interface{}) (err error) {
button := map[string]interface{}{}
err = deinterfacer.Each(btn, func(_ int, k string, v interface{}) error {
switch k {
case "script":
if s, err := pImp.getScript(deinterfacer.ToString(v)); err != nil || s == nil {
return errors.Errorf("could not load script %q for page %q block #%d (%v)",
v, page.Handle, i+1, err)
} else {
button["scriptID"] = s.ID
refs++
}
default:
button[k] = v
}
default:
button[k] = v
return nil
})
if err != nil {
return err
}
bb = append(bb, button)
return nil
})
b.Options["buttons"] = bb
if err != nil {
return err
}
} else if b.Kind == "Calendar" {
ff := make([]interface{}, 0)
err := deinterfacer.Each(b.Options["feeds"], func(_ int, _ string, def interface{}) (err error) {
feed := map[string]interface{}{}
bb = append(bb, button)
return nil
})
b.Options["buttons"] = bb
if err != nil {
return err
}
} else if b.Kind == "Calendar" {
ff := make([]interface{}, 0)
err := deinterfacer.Each(b.Options["feeds"], func(_ int, _ string, def interface{}) (err error) {
feed := map[string]interface{}{}
err = deinterfacer.Each(def, func(_ int, k string, v interface{}) error {
switch k {
case "module":
if m, err := pImp.getModule(deinterfacer.ToString(v)); err != nil || m == nil {
return errors.Errorf("could not load module %q for page %q block #%d (%v)",
v, page.Handle, i+1, err)
} else {
feed["moduleID"] = m.ID
err = deinterfacer.Each(def, func(_ int, k string, v interface{}) error {
switch k {
case "module":
if m, err := pImp.getModule(deinterfacer.ToString(v)); err != nil || m == nil {
return errors.Errorf("could not load module %q for page %q block #%d (%v)",
v, page.Handle, i+1, err)
} else {
feed["moduleID"] = m.ID
refs++
}
default:
feed[k] = v
}
default:
feed[k] = v
return nil
})
if err != nil {
return err
}
ff = append(ff, feed)
return nil
})
b.Options["feeds"] = ff
if err != nil {
return err
}
ff = append(ff, feed)
return nil
})
b.Options["feeds"] = ff
if err != nil {
return err
}
}
}
return nil
return nil
}()
}
@@ -2,13 +2,14 @@ modules:
mod1:
name: Module with fields
fields:
f1: Number
f1:
kind: Number
f2:
kind: String
label: F2
required: true
options:
multiLine: true
useRichTextEditor: true
multiDelimiter: |2+
useRichTextEditor: true
+15
View File
@@ -0,0 +1,15 @@
modules:
mod1:
name: Module with fields
fields:
- name: f1
type: Number
- name: f2
kind: String
label: F2
required: true
options:
multiLine: true
multiDelimiter: |2+
useRichTextEditor: true
+4 -4
View File
@@ -1,8 +1,8 @@
pages:
root:
- handle: root
title: Root page
pages:
sub1:
- handle: sub1
title: Sub page 1
blocks:
- title: B1
@@ -12,8 +12,8 @@ pages:
- title: B2
xywh: [ 11, 12, 13, 14 ]
kind: TheTestingKind
sub2:
- handle: sub2
title: Sub page 2
pages:
sub21:
- handle: sub21
title: Sub-sub page 2.1
+47
View File
@@ -0,0 +1,47 @@
package compose
import (
"context"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/cortezaproject/corteza-server/compose/importer"
"github.com/cortezaproject/corteza-server/compose/service"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/pkg/cli"
impAux "github.com/cortezaproject/corteza-server/pkg/importer"
provision "github.com/cortezaproject/corteza-server/provision/compose"
)
func provisionConfig(ctx context.Context, cmd *cobra.Command, c *cli.Config) error {
c.Log.Debug("running configuration provision")
c.InitServices(ctx, c)
// Make sure we have all full access for provisioning
ctx = auth.SetSuperUserContext(ctx)
if provisioned, err := isProvisioned(ctx); err != nil {
return err
} else if provisioned {
c.Log.Debug("configuration already provisioned")
return nil
}
readers, err := impAux.ReadStatic(provision.Asset)
if err != nil {
return err
}
return errors.Wrap(
importer.Import(ctx, nil, readers...),
"could not provision configuration for compose service",
)
}
// Provision only where there are no namespaces
func isProvisioned(ctx context.Context) (bool, error) {
_, f, err := service.DefaultNamespace.With(ctx).Find(types.NamespaceFilter{})
return f.Count > 0, err
}
-55
View File
@@ -1,55 +0,0 @@
package messaging
import (
"context"
"github.com/spf13/cobra"
"github.com/titpetric/factory"
"github.com/cortezaproject/corteza-server/messaging/repository"
"github.com/cortezaproject/corteza-server/messaging/service"
"github.com/cortezaproject/corteza-server/messaging/types"
"github.com/cortezaproject/corteza-server/pkg/cli"
)
func accessControlSetup(ctx context.Context, cmd *cobra.Command, c *cli.Config) error {
c.InitServices(ctx, c)
// Calling grant directly on internal permissions service to avoid AC check for "grant"
var p = service.DefaultPermissions
var ac = service.DefaultAccessControl
return p.Grant(ctx, ac.Whitelist(), ac.DefaultRules()...)
}
// Add default channels when there are none
func makeDefaultChannels(ctx context.Context, cmd *cobra.Command, c *cli.Config) error {
db, err := factory.Database.Get(messaging)
if err != nil {
return err
}
repo := repository.Channel(ctx, db)
cc, err := repo.Find(nil)
if err != nil {
return err
}
if len(cc) == 0 {
cc = types.ChannelSet{
&types.Channel{Name: "General"},
&types.Channel{Name: "Random"},
}
err = cc.Walk(func(c *types.Channel) error {
_, err := repo.Create(c)
return err
})
if err != nil {
return err
}
}
return nil
}
+61
View File
@@ -0,0 +1,61 @@
package importer
import (
"context"
"io"
"gopkg.in/yaml.v2"
"github.com/cortezaproject/corteza-server/internal/permissions"
"github.com/cortezaproject/corteza-server/messaging/service"
"github.com/cortezaproject/corteza-server/messaging/types"
sysTypes "github.com/cortezaproject/corteza-server/system/types"
)
// Import performs standard import procedure with default services
func Import(ctx context.Context, ff ...io.Reader) (err error) {
var (
cc types.ChannelSet
aux interface{}
)
cc, err = service.DefaultChannel.With(ctx).Find(&types.ChannelFilter{})
if err != nil {
return err
}
var (
p = permissions.NewImporter(service.DefaultAccessControl.Whitelist())
imp = NewImporter(
p,
NewChannelImport(p, cc),
)
// At the moment, we can not load roles from system service
// so we'll just use static set of known roles
//
// Roles are use for resolving access control
roles = sysTypes.RoleSet{
&sysTypes.Role{ID: permissions.EveryoneRoleID, Handle: "everyone"},
&sysTypes.Role{ID: permissions.AdminRoleID, Handle: "admins"},
}
)
for _, f := range ff {
if err = yaml.NewDecoder(f).Decode(&aux); err != nil {
return
}
err = imp.Cast(aux)
if err != nil {
return
}
}
// Store all imported
return imp.Store(
ctx,
service.DefaultChannel.With(ctx),
service.DefaultAccessControl,
roles,
)
}
+6 -7
View File
@@ -54,6 +54,10 @@ func Configure() *cli.Config {
cli.HandleError(c.ProvisionMigrateDatabase.Run(ctx, cmd, c))
}
if c.ProvisionOpt.Configuration {
cli.HandleError(provisionConfig(ctx, cmd, c))
}
c.InitServices(ctx, c)
var websocketOpt = options.Websocket(messaging)
@@ -64,11 +68,6 @@ func Configure() *cli.Config {
PingPeriod: websocketOpt.PingPeriod,
})
if c.ProvisionOpt.AutoSetup {
cli.HandleError(accessControlSetup(ctx, cmd, c))
cli.HandleError(makeDefaultChannels(ctx, cmd, c))
}
go service.Watchers(ctx)
return nil
},
@@ -93,8 +92,8 @@ func Configure() *cli.Config {
},
},
ProvisionAccessControl: cli.Runners{
accessControlSetup,
ProvisionConfig: cli.Runners{
provisionConfig,
},
}
}
+47
View File
@@ -0,0 +1,47 @@
package messaging
import (
"context"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/messaging/importer"
"github.com/cortezaproject/corteza-server/messaging/service"
"github.com/cortezaproject/corteza-server/messaging/types"
"github.com/cortezaproject/corteza-server/pkg/cli"
impAux "github.com/cortezaproject/corteza-server/pkg/importer"
provision "github.com/cortezaproject/corteza-server/provision/messaging"
)
func provisionConfig(ctx context.Context, cmd *cobra.Command, c *cli.Config) error {
c.Log.Debug("running configuration provision")
c.InitServices(ctx, c)
// Make sure we have all full access for provisioning
ctx = auth.SetSuperUserContext(ctx)
if provisioned, err := isProvisioned(ctx); err != nil {
return err
} else if provisioned {
c.Log.Debug("configuration already provisioned")
return nil
}
readers, err := impAux.ReadStatic(provision.Asset)
if err != nil {
return err
}
return errors.Wrap(
importer.Import(ctx, readers...),
"could not provision configuration for messaging service",
)
}
// Provision ONLY when there are no channels (even if we find delete channels we abort provisioning
func isProvisioned(ctx context.Context) (bool, error) {
cc, err := service.DefaultChannel.With(ctx).Find(&types.ChannelFilter{IncludeDeleted: true})
return len(cc) > 0, err
}
+4 -4
View File
@@ -111,11 +111,11 @@ func Configure() *cli.Config {
},
},
ProvisionAccessControl: cli.Runners{
ProvisionConfig: cli.Runners{
func(ctx context.Context, cmd *cobra.Command, c *cli.Config) (err error) {
cli.HandleError(cmp.ProvisionAccessControl.Run(ctx, cmd, cmp))
cli.HandleError(msg.ProvisionAccessControl.Run(ctx, cmd, msg))
cli.HandleError(sys.ProvisionAccessControl.Run(ctx, cmd, sys))
cli.HandleError(cmp.ProvisionConfig.Run(ctx, cmd, cmp))
cli.HandleError(msg.ProvisionConfig.Run(ctx, cmd, msg))
cli.HandleError(sys.ProvisionConfig.Run(ctx, cmd, sys))
return
},
},
+2 -2
View File
@@ -3,14 +3,14 @@ package options
type (
ProvisionOpt struct {
MigrateDatabase bool `env:"PROVISION_MIGRATE_DATABASE"`
AutoSetup bool `env:"PROVISION_AUTO_SETUP"`
Configuration bool `env:"PROVISION_CONFIGURATION"`
}
)
func Provision(pfix string) (o *ProvisionOpt) {
o = &ProvisionOpt{
MigrateDatabase: true,
AutoSetup: true,
Configuration: true,
}
fill(o, pfix)
+6 -6
View File
@@ -95,7 +95,7 @@ type (
// Access control initial setup
// Reapplies default access control rules for roles "everyone" [1] and "admin" [2]
ProvisionAccessControl Runners
ProvisionConfig Runners
// ******************************************************************
@@ -264,7 +264,7 @@ func (c *Config) MakeCLI(ctx context.Context) (cmd *cobra.Command) {
cmd.AddCommand(serveApiCmd)
if len(c.ProvisionMigrateDatabase) > 0 || len(c.ProvisionAccessControl) > 0 {
if len(c.ProvisionMigrateDatabase) > 0 || len(c.ProvisionConfig) > 0 {
var (
provisionCmd = &cobra.Command{
Use: "provision",
@@ -275,17 +275,17 @@ func (c *Config) MakeCLI(ctx context.Context) (cmd *cobra.Command) {
// Add only commands with defined callbacks
if len(c.ProvisionMigrateDatabase) > 0 {
provisionCmd.AddCommand(&cobra.Command{
Use: "access-control-rules",
Short: "Reset access control rules & roles",
Use: "configuration",
Short: "Create permissions & resources",
RunE: func(cmd *cobra.Command, args []string) error {
return c.ProvisionAccessControl.Run(ctx, nil, c)
return c.ProvisionConfig.Run(ctx, nil, c)
},
})
}
// Add only commands with defined callbacks
if len(c.ProvisionAccessControl) > 0 {
if len(c.ProvisionConfig) > 0 {
provisionCmd.AddCommand(&cobra.Command{
Use: "migrate-database",
Short: "Run database migration scripts",
+57
View File
@@ -0,0 +1,57 @@
package importer
import (
"bytes"
"io"
"os"
"path/filepath"
"sort"
"github.com/goware/statik/fs"
"github.com/pkg/errors"
)
// ReadStatic reads static FS and returns slice of io.Readers
func ReadStatic(data string) ([]io.Reader, error) {
var (
files = make([]string, 0)
yamlFilter = func(filename string, info os.FileInfo, err error) error {
if err == nil && !info.IsDir() {
if matched, err := filepath.Match("/*.yaml", filename); matched && err == nil {
files = append(files, filename)
}
}
return err
}
sfs, err = fs.New(data)
readers []io.Reader
)
if err != nil {
return nil, errors.Wrap(err, "could not read static filesystem")
}
if err = fs.Walk(sfs, "/", yamlFilter); err != nil {
return nil, errors.Wrap(err, "could not filter files")
}
if len(files) == 0 {
return nil, nil
}
sort.Strings(files)
for _, file := range files {
if bb, err := fs.ReadFile(sfs, file); err != nil {
return nil, errors.Wrapf(err, "could not read %s", file)
} else {
readers = append(readers, bytes.NewBuffer(bb))
}
}
return readers, nil
}
@@ -0,0 +1,43 @@
allow:
everyone:
compose:
- access
admins:
compose:
- access
- grant
- namespace.create
compose:namespace:
- read
- update
- delete
- manage
- page.create
- module.create
- chart.create
compose:module:
- read
- update
- delete
- record.create
- record.read
- record.update
- record.delete
compose:chart:
- read
- update
- delete
compose:page:
- read
- update
- delete
compose:automation-script:
- read
- update
- delete
+10
View File
@@ -0,0 +1,10 @@
namespaces:
crm:
name: CRM
allow:
everyone:
- module.create
- read
deny:
everyone:
- delete
File diff suppressed because it is too large Load Diff
+103
View File
@@ -0,0 +1,103 @@
namespace: crm
charts:
LeadsByCountry:
name: Leads by country
config:
reports:
- metrics:
- backgroundColor: '#e5a83b'
beginAtZero: true
field: count
fixTooltips: true
label: Number of leads
type: bar
dimensions:
- conditions: {}
field: Country
modifier: (no grouping / buckets)
module: Lead
LeadsBySource:
name: Leads by source
config:
reports:
- metrics:
- backgroundColor: '#36d436'
beginAtZero: true
field: count
label: Number of leads
type: bar
dimensions:
- conditions: {}
field: LeadSource
modifier: (no grouping / buckets)
module: Lead
LeadsByType:
name: Leads by type
config:
reports:
- metrics:
- backgroundColor: '#63da1a'
beginAtZero: true
field: count
label: Number of leads
type: bar
dimensions:
- conditions: {}
field: Status
modifier: (no grouping / buckets)
module: Lead
LeadsPerDay:
name: Leads per day
config:
reports:
- metrics:
- aggregate: SUM
axisType: linear
backgroundColor: '#fc7507'
beginAtZero: true
field: count
fill: false
label: Leads per day
type: line
dimensions:
- conditions: {}
field: created_at
modifier: DATE
module: Lead
OpportunitiesByValue:
name: Opportunities by value
config:
reports:
- metrics:
- aggregate: SUM
backgroundColor: '#5977ff'
beginAtZero: true
field: Amount
label: Total value
type: bar
dimensions:
- conditions: {}
field: StageName
modifier: (no grouping / buckets)
module: Opportunity
QuarterlyPerformance:
name: Quarterly performance
config:
reports:
- filter: Amount > 0
metrics:
- aggregate: SUM
axisPosition: left
axisType: linear
backgroundColor: '#009eff'
beginAtZero: true
field: Amount
fill: true
label: Amount
type: line
dimensions:
- autoSkip: true
conditions: {}
field: CloseDate
modifier: QUARTER
module: Opportunity
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,34 @@
allow:
everyone:
messaging:
- access
admins:
messaging:
- access
- grant
- channel.public.create
- channel.private.create
- channel.group.create
messaging:channel:
- update
- leave
- read
- join
- delete
- undelete
- archive
- unarchive
- members.manage
- attachments.manage
- message.attach
- message.update.all
- message.update.own
- message.delete.all
- message.delete.own
- message.embed
- message.send
- message.reply
- message.react
@@ -0,0 +1,5 @@
channels:
- name: General
type: public
- name: Random
type: public
+6
View File
@@ -0,0 +1,6 @@
// Code generated by statik. DO NOT EDIT.
// Package contains static assets.
package messaging
var Asset = "PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00 \x000000_access_control.yamlUT\x05\x00\x01\x80Cm8allow:\n everyone:\n messaging:\n - access\n\n admins:\n messaging:\n - access\n - grant\n - channel.public.create\n - channel.private.create\n - channel.group.create\n\n messaging:channel:\n - update\n - leave\n - read\n - join\n - delete\n - undelete\n - archive\n - unarchive\n - members.manage\n - attachments.manage\n - message.attach\n - message.update.all\n - message.update.own\n - message.delete.all\n - message.delete.own\n - message.embed\n - message.send\n - message.reply\n - message.react\n\nPK\x07\x08\xa5\xf4PT`\x02\x00\x00`\x02\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00 \x001000_channels.yamlUT\x05\x00\x01\x80Cm8channels:\n - name: General\n type: public\n - name: Random\n type: public\nPK\x07\x08\xe8\x83F\xf8O\x00\x00\x00O\x00\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xa5\xf4PT`\x02\x00\x00`\x02\x00\x00\x18\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x000000_access_control.yamlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xe8\x83F\xf8O\x00\x00\x00O\x00\x00\x00\x12\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xaf\x02\x00\x001000_channels.yamlUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00\x98\x00\x00\x00G\x03\x00\x00\x00\x00"
@@ -0,0 +1,38 @@
roles:
admins: Administrators
allow:
admins:
system:
- access
- grant
- settings.read
- settings.manage
- organisation.create
- application.create
- user.create
- role.create
- automation-script.create
system:application:
- read
- update
- delete
system:user:
- read
- update
- suspend
- unsuspend
- delete
system:role:
- read
- update
- delete
- members.manage
system:automation-script:
- read
- update
- delete
+6
View File
@@ -0,0 +1,6 @@
// Code generated by statik. DO NOT EDIT.
// Package contains static assets.
package system
var Asset = "PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00 \x000000_access_control.yamlUT\x05\x00\x01\x80Cm8roles:\n admins: Administrators\n\nallow:\n admins:\n system:\n - access\n - grant\n - settings.read\n - settings.manage\n - organisation.create\n - application.create\n - user.create\n - role.create\n - automation-script.create\n\n system:application:\n - read\n - update\n - delete\n\n system:user:\n - read\n - update\n - suspend\n - unsuspend\n - delete\n\n system:role:\n - read\n - update\n - delete\n - members.manage\n\n system:automation-script:\n - read\n - update\n - delete\nPK\x07\x08\xe5T\x99\x88J\x02\x00\x00J\x02\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xe5T\x99\x88J\x02\x00\x00J\x02\x00\x00\x18\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x000000_access_control.yamlUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00O\x00\x00\x00\x99\x02\x00\x00\x00\x00"
+3 -19
View File
@@ -1,5 +1,6 @@
#!/bin/bash
cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
function download {
SRC="https://raw.githubusercontent.com/cortezaproject/corteza-configs/master/${1}"
@@ -13,26 +14,9 @@ function getCrmConfig {
NAMES="1000_namespace 1100_modules 1200_charts 1300_scripts 1400_pages"
for NAME in $NAMES; do
download "crm/${NAME}.yaml" "./compose/${NAME}.yaml"
download "crm/${NAME}.yaml" "./compose/src/${NAME}.yaml"
done
}
function get {
getCrmConfig
}
getCrmConfig
function gen {
echo "generating..."
}
case ${1:-"all"} in
gen)
gen
;;
get)
get
;;
all)
get
gen
esac
+1 -1
View File
@@ -115,7 +115,7 @@ func RegisterOidcProvider(ctx context.Context, name, providerUrl string, force,
}
if as.ExternalRedirectUrl == "" {
return nil, errors.New("refusing to register oidc privider withoit redirect url")
return nil, errors.New("refusing to register OIDC provider without redirect url")
}
p, err := parseExternalProviderUrl(providerUrl)
+2 -30
View File
@@ -6,14 +6,10 @@ import (
"os"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
"github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/internal/permissions"
"github.com/cortezaproject/corteza-server/pkg/cli"
"github.com/cortezaproject/corteza-server/system/importer"
"github.com/cortezaproject/corteza-server/system/service"
"github.com/cortezaproject/corteza-server/system/types"
)
func Importer(ctx context.Context, c *cli.Config) *cobra.Command {
@@ -26,7 +22,6 @@ func Importer(ctx context.Context, c *cli.Config) *cobra.Command {
c.InitServices(ctx, c)
var (
aux interface{}
ff []io.Reader
err error
)
@@ -39,33 +34,10 @@ func Importer(ctx context.Context, c *cli.Config) *cobra.Command {
ff[a], err = os.Open(arg)
cli.HandleError(err)
}
cli.HandleError(importer.Import(ctx, ff...))
} else {
args = []string{"STDIN"}
ff = []io.Reader{os.Stdin}
cli.HandleError(importer.Import(ctx, os.Stdin))
}
roles, err := service.DefaultRole.With(ctx).Find(&types.RoleFilter{})
cli.HandleError(err)
perm := permissions.NewImporter(service.DefaultAccessControl.Whitelist())
imp := importer.NewImporter(perm,
importer.NewRoleImport(perm, roles),
)
for i, f := range ff {
cmd.Printf("Importing from %s\n", args[i])
cli.HandleError(yaml.NewDecoder(f).Decode(&aux))
cli.HandleError(imp.Cast(aux))
}
cli.HandleError(imp.Store(
ctx,
service.DefaultRole.With(ctx),
service.DefaultAccessControl,
roles,
))
},
}
+27 -10
View File
@@ -16,6 +16,10 @@ import (
)
func Users(ctx context.Context, c *cli.Config) *cobra.Command {
var (
flagNoPassword bool
)
// User management commands.
cmd := &cobra.Command{
Use: "users",
@@ -82,28 +86,41 @@ func Users(ctx context.Context, c *cli.Config) *cobra.Command {
password []byte
)
if existing, _ := userRepo.FindByEmail(user.Email); existing != nil && existing.ID > 0 {
cmd.Printf("User already exists [%d].\n", existing.ID)
return
}
if user, err = userRepo.Create(user); err != nil {
cli.HandleError(err)
}
cmd.Printf("User created [%d].\n", user.ID)
cmd.Print("Set password: ")
if password, err = terminal.ReadPassword(syscall.Stdin); err != nil {
cli.HandleError(err)
}
if !flagNoPassword {
cmd.Print("Set password: ")
if password, err = terminal.ReadPassword(syscall.Stdin); err != nil {
cli.HandleError(err)
}
if len(password) == 0 {
// Password not set, that's ok too.
return
}
if len(password) == 0 {
// Password not set, that's ok too.
return
}
if err = authSvc.SetPassword(user.ID, string(password)); err != nil {
cli.HandleError(err)
if err = authSvc.SetPassword(user.ID, string(password)); err != nil {
cli.HandleError(err)
}
}
},
}
addCmd.Flags().BoolVar(
&flagNoPassword,
"no-password",
false,
"Create user without password")
pwdCmd := &cobra.Command{
Use: "password [email]",
Short: "Change password for user",
+56
View File
@@ -0,0 +1,56 @@
package importer
import (
"context"
"io"
"gopkg.in/yaml.v2"
"github.com/cortezaproject/corteza-server/internal/permissions"
"github.com/cortezaproject/corteza-server/system/service"
"github.com/cortezaproject/corteza-server/system/types"
)
// Import performs standard import procedure with default services
func Import(ctx context.Context, ff ...io.Reader) (err error) {
var (
roles types.RoleSet
aux interface{}
)
roles, err = service.DefaultRole.With(ctx).Find(&types.RoleFilter{})
if err != nil {
return err
}
pi := permissions.NewImporter(service.DefaultAccessControl.Whitelist())
imp := NewImporter(
pi,
NewRoleImport(pi, roles),
)
for _, f := range ff {
if err = yaml.NewDecoder(f).Decode(&aux); err != nil {
return
}
err = imp.Cast(aux)
if err != nil {
return
}
}
// Get roles across the system
// roles, err := service.DefaultSystemRole.Find(ctx)
// if err != nil {
// return
// }
// Store all imported
return imp.Store(
ctx,
service.DefaultRole.With(ctx),
service.DefaultAccessControl,
roles,
)
}
+102
View File
@@ -0,0 +1,102 @@
package system
import (
"context"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/titpetric/factory"
"go.uber.org/zap"
"github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/pkg/cli"
impAux "github.com/cortezaproject/corteza-server/pkg/importer"
provision "github.com/cortezaproject/corteza-server/provision/system"
"github.com/cortezaproject/corteza-server/system/importer"
"github.com/cortezaproject/corteza-server/system/repository"
"github.com/cortezaproject/corteza-server/system/service"
"github.com/cortezaproject/corteza-server/system/types"
)
func provisionConfig(ctx context.Context, cmd *cobra.Command, c *cli.Config) error {
c.Log.Debug("running configuration provision")
c.InitServices(ctx, c)
// Make sure we have all full access for provisioning
ctx = auth.SetSuperUserContext(ctx)
if provisioned, err := isProvisioned(ctx); err != nil {
return err
} else if provisioned {
c.Log.Debug("configuration already provisioned")
return nil
}
readers, err := impAux.ReadStatic(provision.Asset)
if err != nil {
return err
}
return errors.Wrap(
importer.Import(ctx, readers...),
"could not provision configuration for system service",
)
}
// Provision ONLY when there are no roles
func isProvisioned(ctx context.Context) (bool, error) {
rr, err := service.DefaultRole.With(ctx).Find(&types.RoleFilter{})
return len(rr) > 0, err
}
func makeDefaultApplications(ctx context.Context, cmd *cobra.Command, c *cli.Config) error {
db, err := factory.Database.Get(system)
if err != nil {
return err
}
repo := repository.Application(ctx, db)
aa, err := repo.Find()
if 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: "/applications/crust_favicon.png",
Logo: "/applications/crust.jpg",
Url: "/compose/ns/crm/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, err = repo.Create(defApp)
c.Log.Info(
"creating default application",
zap.String("name", defApp.Name),
zap.Uint64("name", defApp.ID),
zap.Error(err),
)
return err
return nil
})
}
@@ -2,86 +2,18 @@ package system
import (
"context"
"errors"
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/titpetric/factory"
"go.uber.org/zap"
"github.com/cortezaproject/corteza-server/pkg/cli"
"github.com/cortezaproject/corteza-server/pkg/cli/options"
"github.com/cortezaproject/corteza-server/system/auth/external"
"github.com/cortezaproject/corteza-server/system/repository"
"github.com/cortezaproject/corteza-server/system/service"
"github.com/cortezaproject/corteza-server/system/types"
)
func accessControlSetup(ctx context.Context, cmd *cobra.Command, c *cli.Config) error {
c.InitServices(ctx, c)
// Calling grant directly on internal permissions service to avoid AC check for "grant"
var p = service.DefaultPermissions
var ac = service.DefaultAccessControl
return p.Grant(ctx, ac.Whitelist(), ac.DefaultRules()...)
}
func makeDefaultApplications(ctx context.Context, cmd *cobra.Command, c *cli.Config) error {
db, err := factory.Database.Get(system)
if err != nil {
return err
}
repo := repository.Application(ctx, db)
aa, err := repo.Find()
if 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: "/applications/crust_favicon.png",
Logo: "/applications/crust.jpg",
Url: "/compose/ns/crm/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, err = repo.Create(defApp)
c.Log.Info(
"creating default application",
zap.String("name", defApp.Name),
zap.Uint64("name", defApp.ID),
zap.Error(err),
)
return err
return nil
})
}
func discoverSettings(ctx context.Context, cmd *cobra.Command, c *cli.Config) error {
return service.DefaultSettings.With(ctx).AutoDiscovery()
}
func oidcAutoDiscovery(ctx context.Context, cmd *cobra.Command, c *cli.Config) (err error) {
var provider = strings.TrimSpace(options.EnvString("", "PROVISION_OIDC_PROVIDER", ""))
@@ -117,7 +49,7 @@ func oidcAutoDiscovery(ctx context.Context, cmd *cobra.Command, c *cli.Config) (
if err != nil {
c.Log.Error(
"could not register oidc provider",
"could not register OIDC provider",
zap.String("url", purl),
zap.String("name", name),
zap.Error(err))
@@ -126,7 +58,7 @@ func oidcAutoDiscovery(ctx context.Context, cmd *cobra.Command, c *cli.Config) (
c.Log.Info("provider already exists",
zap.String("name", name))
} else {
c.Log.Info("provider successfuly registered",
c.Log.Info("provider successfully registered",
zap.String("url", purl),
zap.String("key", eap.Key),
zap.String("name", name))
+14
View File
@@ -0,0 +1,14 @@
package system
import (
"context"
"github.com/spf13/cobra"
"github.com/cortezaproject/corteza-server/pkg/cli"
"github.com/cortezaproject/corteza-server/system/service"
)
func settingsAutoDiscovery(ctx context.Context, cmd *cobra.Command, c *cli.Config) error {
return service.DefaultSettings.With(ctx).AutoDiscovery()
}
+5 -5
View File
@@ -57,11 +57,11 @@ func Configure() *cli.Config {
c.InitServices(ctx, c)
if c.ProvisionOpt.AutoSetup {
cli.HandleError(accessControlSetup(ctx, cmd, c))
if c.ProvisionOpt.Configuration {
cli.HandleError(provisionConfig(ctx, cmd, c))
cli.HandleError(makeDefaultApplications(ctx, cmd, c))
cli.HandleError(discoverSettings(ctx, cmd, c))
cli.HandleError(settingsAutoDiscovery(ctx, cmd, c))
// Reload auto-configured settings
// adding externals and oidc auto discovery depends on redirect-url setting
@@ -154,8 +154,8 @@ func Configure() *cli.Config {
},
},
ProvisionAccessControl: cli.Runners{
accessControlSetup,
ProvisionConfig: cli.Runners{
provisionConfig,
},
}
}
+2 -2
View File
@@ -20,8 +20,8 @@ func TestProvisioning(t *testing.T) {
aux interface{}
roles = sysTypes.RoleSet{
&sysTypes.Role{ID: 1, Handle: "everyone"},
&sysTypes.Role{ID: 2, Handle: "admins"},
&sysTypes.Role{ID: permissions.EveryoneRoleID, Handle: "everyone"},
&sysTypes.Role{ID: permissions.AdminRoleID, Handle: "admins"},
}
ctx = h.secCtx()
+2 -2
View File
@@ -19,8 +19,8 @@ func TestProvisioning(t *testing.T) {
aux interface{}
roles = types.RoleSet{
&types.Role{ID: 1, Handle: "everyone"},
&types.Role{ID: 2, Handle: "admins"},
&types.Role{ID: permissions.EveryoneRoleID, Handle: "everyone"},
&types.Role{ID: permissions.AdminRoleID, Handle: "admins"},
}
ctx = h.secCtx()