rename folder extensions -> services
Signed-off-by: Christian Richter <crichter@owncloud.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
SHELL := bash
|
||||
NAME := idm
|
||||
|
||||
TAGS := disable_crypt
|
||||
|
||||
include ../../.make/recursion.mk
|
||||
|
||||
############ tooling ############
|
||||
ifneq (, $(shell command -v go 2> /dev/null)) # suppress `command not found warnings` for non go targets in CI
|
||||
include ../../.bingo/Variables.mk
|
||||
endif
|
||||
|
||||
############ go tooling ############
|
||||
include ../../.make/go.mk
|
||||
|
||||
############ release ############
|
||||
include ../../.make/release.mk
|
||||
|
||||
############ docs generate ############
|
||||
include ../../.make/docs.mk
|
||||
|
||||
.PHONY: docs-generate
|
||||
docs-generate: config-docs-generate
|
||||
|
||||
############ generate ############
|
||||
include ../../.make/generate.mk
|
||||
|
||||
.PHONY: ci-go-generate
|
||||
ci-go-generate: # CI runs ci-node-generate automatically before this target
|
||||
|
||||
.PHONY: ci-node-generate
|
||||
ci-node-generate:
|
||||
|
||||
############ licenses ############
|
||||
.PHONY: ci-node-check-licenses
|
||||
ci-node-check-licenses:
|
||||
|
||||
.PHONY: ci-node-save-licenses
|
||||
ci-node-save-licenses:
|
||||
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/command"
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/config/defaults"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := command.Execute(defaults.DefaultConfig()); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package idm
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
//go:embed ldif/base.ldif.tmpl
|
||||
var BaseLDIF string
|
||||
|
||||
//go:embed ldif/demousers.ldif
|
||||
var DemoUsersLDIF string
|
||||
@@ -0,0 +1,43 @@
|
||||
dn: o=libregraph-idm
|
||||
o: libregraph-idm
|
||||
objectClass: organization
|
||||
|
||||
dn: ou=users,o=libregraph-idm
|
||||
objectClass: organizationalUnit
|
||||
ou: users
|
||||
|
||||
dn: ou=sysusers,o=libregraph-idm
|
||||
objectClass: organizationalUnit
|
||||
ou: users
|
||||
|
||||
dn: ou=groups,o=libregraph-idm
|
||||
objectClass: organizationalUnit
|
||||
ou: groups
|
||||
|
||||
{{ range . -}}
|
||||
{{ if eq .Name "admin" -}}
|
||||
dn: uid=admin,ou=users,o=libregraph-idm
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: organizationalPerson
|
||||
objectClass: ownCloud
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
uid: admin
|
||||
givenName: Admin
|
||||
sn: Admin
|
||||
cn: admin
|
||||
displayName: Admin
|
||||
description: An admin for this oCIS instance.
|
||||
mail: admin@example.org
|
||||
ownCloudUUID: {{ .ID }}
|
||||
{{ else -}}
|
||||
dn: uid={{ .Name }},ou=sysusers,o=libregraph-idm
|
||||
objectClass: account
|
||||
objectClass: simpleSecurityObject
|
||||
uid: {{ .Name }}
|
||||
{{ end -}}
|
||||
userPassword:: {{ .Password }}
|
||||
|
||||
{{ end -}}
|
||||
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
dn: uid=einstein,ou=users,o=libregraph-idm
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: organizationalPerson
|
||||
objectClass: ownCloud
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
uid: einstein
|
||||
givenName: Albert
|
||||
sn: Einstein
|
||||
cn: einstein
|
||||
displayName: Albert Einstein
|
||||
description: A German-born theoretical physicist who developed the theory of relativity, one of the two pillars of modern physics (alongside quantum mechanics).
|
||||
mail: einstein@example.org
|
||||
ownCloudUUID: 4c510ada-c86b-4815-8820-42cdf82c3d51
|
||||
userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTY1NTM2LHQ9MSxwPTIkOFpyclR0NXA1a0VmVlhL
|
||||
akNHaVBEUSRnemZCWWwrTHdzTUhXQWJSMEJ2NnRiZk1XZjZaOVJ0Mms5Z3VkSWJ5bzg4
|
||||
|
||||
dn: uid=marie,ou=users,o=libregraph-idm
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: organizationalPerson
|
||||
objectClass: ownCloud
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
uid: marie
|
||||
givenName: Marie
|
||||
sn: Curie
|
||||
cn: marie
|
||||
displayName: Marie Skłodowska Curie
|
||||
description: A Polish and naturalized-French physicist and chemist who conducted pioneering research on radioactivity.
|
||||
mail: marie@example.org
|
||||
ownCloudUUID: f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c
|
||||
userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTY1NTM2LHQ9MSxwPTIkUHJzWkpQQW9pMkFwZHlJ
|
||||
a2Q1NGkzQSRnalZzR3doTmk2K0djenJ4SVdPalN2UlBpWXhKSXpHVG4vcnpQZzkvSlZN
|
||||
|
||||
dn: uid=katherine,ou=users,o=libregraph-idm
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: organizationalPerson
|
||||
objectClass: ownCloud
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
uid: katherine
|
||||
givenName: Katherine
|
||||
sn: Johnson
|
||||
cn: katherine
|
||||
displayName: Katherine Johnson
|
||||
description: An American mathematician whose calculations of orbital mechanics as a NASA employee were critical to the success of the first and subsequent U.S. crewed spaceflights.
|
||||
mail: katherine@example.org
|
||||
ownCloudUUID: 534bb038-6f9d-4093-946f-133be61fa4e7
|
||||
userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTY1NTM2LHQ9MSxwPTIkYVd0b1RXdHdZblZuYjB4
|
||||
Nk0wRm1adyRyeXVESVg0a3JSNGdmcnduZEh3aVpaSjVDUQ==
|
||||
|
||||
dn: uid=richard,ou=users,o=libregraph-idm
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: organizationalPerson
|
||||
objectClass: ownCloud
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
uid: richard
|
||||
givenName: Richard
|
||||
sn: Feynman
|
||||
cn: richard
|
||||
displayName: Richard Phillips Feynman
|
||||
description: An American theoretical physicist, known for his work in the path integral formulation of quantum mechanics, the theory of quantum electrodynamics, the physics of the superfluidity of supercooled liquid helium, as well as his work in particle physics for which he proposed the parton model.
|
||||
mail: richard@example.org
|
||||
ownCloudUUID: 932b4540-8d16-481e-8ef4-588e4b6b151c
|
||||
userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTY1NTM2LHQ9MSxwPTIkNjlNcUQxem5sUUZ2SUha
|
||||
d2dxU00xQSRVQmNEa2NDZktMemVpQnlyb0JjOTdCSVRhTFo2WjZIL2dhbytSTVh6OHhn
|
||||
|
||||
dn: uid=moss,ou=users,o=libregraph-idm
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: organizationalPerson
|
||||
objectClass: ownCloud
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
uid: moss
|
||||
givenName: Maurice
|
||||
sn: Moss
|
||||
cn: moss
|
||||
displayName: Maurice Moss
|
||||
description: A worker in the IT Department of Reynholm Industries. Of all the working staff in the IT Department, he is the most hard-working, the most experienced, and the most capable of doing his job well. He puts a lot of effort into his work, however he does not get the credit he deserves.
|
||||
mail: moss@example.org
|
||||
ownCloudUUID: 058bff95-6708-4fe5-91e4-9ea3d377588b
|
||||
userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTY1NTM2LHQ9MSxwPTIkZU0xaXR6amQ2dlNSSERx
|
||||
NlZCbXBlQSQxNzBhcTB3YjJZZ2NLU2cwWDhHY3l6ckZwMUllcGplMTNraDdVNjUyNXk4
|
||||
|
||||
dn: cn=users,ou=groups,o=libregraph-idm
|
||||
objectClass: groupOfNames
|
||||
objectClass: ownCloud
|
||||
objectClass: top
|
||||
cn: users
|
||||
description: Users
|
||||
ownCloudUUID: 509a9dcd-bb37-4f4f-a01a-19dca27d9cfa
|
||||
member: uid=einstein,ou=users,o=libregraph-idm
|
||||
member: uid=marie,ou=users,o=libregraph-idm
|
||||
member: uid=richard,ou=users,o=libregraph-idm
|
||||
member: uid=moss,ou=users,o=libregraph-idm
|
||||
member: uid=admin,ou=users,o=libregraph-idm
|
||||
|
||||
dn: cn=sailing-lovers,ou=groups,o=libregraph-idm
|
||||
objectClass: groupOfNames
|
||||
objectClass: ownCloud
|
||||
objectClass: top
|
||||
cn: sailing-lovers
|
||||
description: Sailing lovers
|
||||
ownCloudUUID: 6040aa17-9c64-4fef-9bd0-77234d71bad0
|
||||
member: uid=einstein,ou=users,o=libregraph-idm
|
||||
|
||||
dn: cn=violin-haters,ou=groups,o=libregraph-idm
|
||||
objectClass: groupOfNames
|
||||
objectClass: ownCloud
|
||||
objectClass: top
|
||||
cn: violin-haters
|
||||
description: Violin haters
|
||||
ownCloudUUID: dd58e5ec-842e-498b-8800-61f2ec6f911f
|
||||
member: uid=einstein,ou=users,o=libregraph-idm
|
||||
|
||||
dn: cn=radium-lovers,ou=groups,o=libregraph-idm
|
||||
objectClass: groupOfNames
|
||||
objectClass: ownCloud
|
||||
objectClass: top
|
||||
cn: radium-lovers
|
||||
description: Radium lovers
|
||||
ownCloudUUID: 7b87fd49-286e-4a5f-bafd-c535d5dd997a
|
||||
member: uid=marie,ou=users,o=libregraph-idm
|
||||
|
||||
dn: cn=polonium-lovers,ou=groups,o=libregraph-idm
|
||||
objectClass: groupOfNames
|
||||
objectClass: ownCloud
|
||||
objectClass: top
|
||||
cn: polonium-lovers
|
||||
description: Polonium lovers
|
||||
ownCloudUUID: cedc21aa-4072-4614-8676-fa9165f598ff
|
||||
member: uid=marie,ou=users,o=libregraph-idm
|
||||
|
||||
dn: cn=quantum-lovers,ou=groups,o=libregraph-idm
|
||||
objectClass: groupOfNames
|
||||
objectClass: ownCloud
|
||||
objectClass: top
|
||||
cn: quantum-lovers
|
||||
description: Quantum lovers
|
||||
ownCloudUUID: a1726108-01f8-4c30-88df-2b1a9d1cba1a
|
||||
member: uid=richard,ou=users,o=libregraph-idm
|
||||
|
||||
dn: cn=philosophy-haters,ou=groups,o=libregraph-idm
|
||||
objectClass: groupOfNames
|
||||
objectClass: ownCloud
|
||||
objectClass: top
|
||||
cn: philosophy-haters
|
||||
description: Philosophy haters
|
||||
ownCloudUUID: 167cbee2-0518-455a-bfb2-031fe0621e5d
|
||||
member: uid=richard,ou=users,o=libregraph-idm
|
||||
|
||||
dn: cn=physics-lovers,ou=groups,o=libregraph-idm
|
||||
objectClass: groupOfNames
|
||||
objectClass: ownCloud
|
||||
objectClass: top
|
||||
cn: physics-lovers
|
||||
description: Physics lovers
|
||||
ownCloudUUID: 262982c1-2362-4afa-bfdf-8cbfef64a06e
|
||||
member: uid=einstein,ou=users,o=libregraph-idm
|
||||
member: uid=marie,ou=users,o=libregraph-idm
|
||||
member: uid=richard,ou=users,o=libregraph-idm
|
||||
@@ -0,0 +1,57 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/config/parser"
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/logging"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// Health is the entrypoint for the health command.
|
||||
func Health(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "health",
|
||||
Usage: "check health status",
|
||||
Category: "info",
|
||||
Before: func(c *cli.Context) error {
|
||||
err := parser.ParseConfig(cfg)
|
||||
if err != nil {
|
||||
fmt.Printf("%v", err)
|
||||
}
|
||||
return err
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := logging.Configure(cfg.Service.Name, cfg.Log)
|
||||
|
||||
resp, err := http.Get(
|
||||
fmt.Sprintf(
|
||||
"http://%s/healthz",
|
||||
cfg.Debug.Addr,
|
||||
),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Fatal().
|
||||
Err(err).
|
||||
Msg("Failed to request health check")
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
logger.Fatal().
|
||||
Int("code", resp.StatusCode).
|
||||
Msg("Health seems to be in bad state")
|
||||
}
|
||||
|
||||
logger.Debug().
|
||||
Int("code", resp.StatusCode).
|
||||
Msg("Health got a good state")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/clihelper"
|
||||
ociscfg "github.com/owncloud/ocis/v2/ocis-pkg/config"
|
||||
"github.com/thejerf/suture/v4"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// GetCommands provides all commands for this service
|
||||
func GetCommands(cfg *config.Config) cli.Commands {
|
||||
return []*cli.Command{
|
||||
// start this service
|
||||
Server(cfg),
|
||||
|
||||
// interaction with this service
|
||||
|
||||
// infos about this service
|
||||
Health(cfg),
|
||||
Version(cfg),
|
||||
}
|
||||
}
|
||||
|
||||
// Execute is the entry point for the ocis-idm command.
|
||||
func Execute(cfg *config.Config) error {
|
||||
app := clihelper.DefaultApp(&cli.App{
|
||||
Name: "idm",
|
||||
Usage: "Embedded LDAP service for oCIS",
|
||||
Commands: GetCommands(cfg),
|
||||
})
|
||||
|
||||
cli.HelpFlag = &cli.BoolFlag{
|
||||
Name: "help,h",
|
||||
Usage: "Show the help",
|
||||
}
|
||||
|
||||
return app.Run(os.Args)
|
||||
}
|
||||
|
||||
// SutureService allows for the idm command to be embedded and supervised by a suture supervisor tree.
|
||||
type SutureService struct {
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
// NewSutureService creates a new idm.SutureService
|
||||
func NewSutureService(cfg *ociscfg.Config) suture.Service {
|
||||
cfg.IDM.Commons = cfg.Commons
|
||||
return SutureService{
|
||||
cfg: cfg.IDM,
|
||||
}
|
||||
}
|
||||
|
||||
func (s SutureService) Serve(ctx context.Context) error {
|
||||
s.cfg.Context = ctx
|
||||
if err := Execute(s.cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-ldap/ldif"
|
||||
"github.com/libregraph/idm/pkg/ldappassword"
|
||||
"github.com/libregraph/idm/pkg/ldbbolt"
|
||||
"github.com/libregraph/idm/server"
|
||||
"github.com/owncloud/ocis/v2/extensions/idm"
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/config/parser"
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/logging"
|
||||
pkgcrypto "github.com/owncloud/ocis/v2/ocis-pkg/crypto"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// Server is the entrypoint for the server command.
|
||||
func Server(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "server",
|
||||
Usage: fmt.Sprintf("start %s extension without runtime (unsupervised mode)", cfg.Service.Name),
|
||||
Category: "server",
|
||||
Before: func(c *cli.Context) error {
|
||||
err := parser.ParseConfig(cfg)
|
||||
if err != nil {
|
||||
fmt.Printf("%v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return err
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := logging.Configure(cfg.Service.Name, cfg.Log)
|
||||
ctx, cancel := func() (context.Context, context.CancelFunc) {
|
||||
if cfg.Context == nil {
|
||||
return context.WithCancel(context.Background())
|
||||
}
|
||||
return context.WithCancel(cfg.Context)
|
||||
}()
|
||||
|
||||
defer cancel()
|
||||
return start(ctx, logger, cfg)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func start(ctx context.Context, logger log.Logger, cfg *config.Config) error {
|
||||
servercfg := server.Config{
|
||||
Logger: log.LogrusWrap(logger.Logger),
|
||||
LDAPHandler: "boltdb",
|
||||
LDAPSListenAddr: cfg.IDM.LDAPSAddr,
|
||||
TLSCertFile: cfg.IDM.Cert,
|
||||
TLSKeyFile: cfg.IDM.Key,
|
||||
LDAPBaseDN: "o=libregraph-idm",
|
||||
LDAPAdminDN: "uid=libregraph,ou=sysusers,o=libregraph-idm",
|
||||
|
||||
BoltDBFile: cfg.IDM.DatabasePath,
|
||||
}
|
||||
|
||||
if cfg.IDM.LDAPSAddr != "" {
|
||||
// Generate a self-signing cert if no certificate is present
|
||||
if err := pkgcrypto.GenCert(cfg.IDM.Cert, cfg.IDM.Key, logger); err != nil {
|
||||
logger.Fatal().Err(err).Msgf("Could not generate test-certificate")
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(servercfg.BoltDBFile); errors.Is(err, os.ErrNotExist) {
|
||||
logger.Debug().Msg("Bootstrapping IDM database")
|
||||
if err = bootstrap(logger, cfg, servercfg); err != nil {
|
||||
logger.Error().Err(err).Msg("failed to bootstrap idm database")
|
||||
}
|
||||
}
|
||||
|
||||
svc, err := server.NewServer(&servercfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return svc.Serve(ctx)
|
||||
}
|
||||
|
||||
func bootstrap(logger log.Logger, cfg *config.Config, srvcfg server.Config) error {
|
||||
// Hash password if the config does not supply a hash already
|
||||
var err error
|
||||
|
||||
type svcUser struct {
|
||||
Name string
|
||||
Password string
|
||||
ID string
|
||||
}
|
||||
|
||||
serviceUsers := []svcUser{
|
||||
{
|
||||
Name: "admin",
|
||||
Password: cfg.ServiceUserPasswords.OcisAdmin,
|
||||
ID: cfg.AdminUserID,
|
||||
},
|
||||
{
|
||||
Name: "libregraph",
|
||||
Password: cfg.ServiceUserPasswords.Idm,
|
||||
},
|
||||
{
|
||||
Name: "idp",
|
||||
Password: cfg.ServiceUserPasswords.Idp,
|
||||
},
|
||||
{
|
||||
Name: "reva",
|
||||
Password: cfg.ServiceUserPasswords.Reva,
|
||||
},
|
||||
}
|
||||
|
||||
bdb := &ldbbolt.LdbBolt{}
|
||||
|
||||
if err := bdb.Configure(srvcfg.Logger, srvcfg.LDAPBaseDN, srvcfg.BoltDBFile, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
defer bdb.Close()
|
||||
|
||||
if err := bdb.Initialize(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prepare the initial Data from template. To be able to set the
|
||||
// supplied service user passwords
|
||||
tmpl, err := template.New("baseldif").Parse(idm.BaseLDIF)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range serviceUsers {
|
||||
if strings.HasPrefix(serviceUsers[i].Password, "$argon2id$") {
|
||||
// password is alread hashed
|
||||
serviceUsers[i].Password = "{ARGON2}" + serviceUsers[i].Password
|
||||
} else {
|
||||
if serviceUsers[i].Password, err = ldappassword.Hash(serviceUsers[i].Password, "{ARGON2}"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// We need to treat the hash as binary in the LDIF template to avoid
|
||||
// go-ldap/ldif to to any fancy escaping
|
||||
serviceUsers[i].Password = base64.StdEncoding.EncodeToString([]byte(serviceUsers[i].Password))
|
||||
}
|
||||
var tmplWriter strings.Builder
|
||||
err = tmpl.Execute(&tmplWriter, serviceUsers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bootstrapData := tmplWriter.String()
|
||||
if cfg.CreateDemoUsers {
|
||||
bootstrapData = bootstrapData + "\n" + idm.DemoUsersLDIF
|
||||
}
|
||||
|
||||
s := strings.NewReader(bootstrapData)
|
||||
lf := &ldif.LDIF{}
|
||||
err = ldif.Unmarshal(s, lf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, entry := range lf.AllEntries() {
|
||||
logger.Debug().Str("dn", entry.DN).Msg("Adding entry")
|
||||
if err := bdb.EntryPut(entry); err != nil {
|
||||
return fmt.Errorf("error adding Entry '%s': %w", entry.DN, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/config"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// Version prints the service versions of all running instances.
|
||||
func Version(cfg *config.Config) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "version",
|
||||
Usage: "print the version of this binary and the running extension instances",
|
||||
Category: "info",
|
||||
Action: func(c *cli.Context) error {
|
||||
// not implemented
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
|
||||
)
|
||||
|
||||
// Config combines all available configuration parts.
|
||||
type Config struct {
|
||||
Commons *shared.Commons `yaml:"-"` // don't use this directly as configuration for a service
|
||||
|
||||
Service Service `yaml:"-"`
|
||||
|
||||
Tracing *Tracing `yaml:"tracing"`
|
||||
Log *Log `yaml:"log"`
|
||||
Debug Debug `yaml:"debug"`
|
||||
|
||||
IDM Settings `yaml:"idm"`
|
||||
CreateDemoUsers bool `yaml:"create_demo_users" env:"IDM_CREATE_DEMO_USERS;ACCOUNTS_DEMO_USERS_AND_GROUPS" desc:"Flag to enabe/disable the creation of the demo users"`
|
||||
|
||||
ServiceUserPasswords ServiceUserPasswords `yaml:"service_user_passwords"`
|
||||
AdminUserID string `yaml:"admin_user_id" env:"OCIS_ADMIN_USER_ID;IDM_ADMIN_USER_ID" desc:"ID of a user, that should receive admin privileges."`
|
||||
|
||||
Context context.Context `yaml:"-"`
|
||||
}
|
||||
|
||||
type Settings struct {
|
||||
LDAPSAddr string `yaml:"ldaps_addr" env:"IDM_LDAPS_ADDR" desc:"Listen address for the ldaps listener (ip-addr:port)"`
|
||||
Cert string `yaml:"cert" env:"IDM_LDAPS_CERT" desc:"File name of the TLS server certificate for the ldaps listener"`
|
||||
Key string `yaml:"key" env:"IDM_LDAPS_KEY" desc:"File name for the TLS certificate key for the server certificate"`
|
||||
DatabasePath string `yaml:"database" env:"IDM_DATABASE_PATH" desc:"Full path to the idm backend database"`
|
||||
}
|
||||
|
||||
type ServiceUserPasswords struct {
|
||||
OcisAdmin string `yaml:"admin_password" env:"IDM_ADMIN_PASSWORD" desc:"Password to set for the ocis \"admin\" user. Either cleartext or an argon2id hash"`
|
||||
Idm string `yaml:"idm_password" env:"IDM_SVC_PASSWORD" desc:"Password to set for the \"idm\" service user. Either cleartext or an argon2id hash"`
|
||||
Reva string `yaml:"reva_password" env:"IDM_REVASVC_PASSWORD" desc:"Password to set for the \"reva\" service user. Either cleartext or an argon2id hash"`
|
||||
Idp string `yaml:"idp_password" env:"IDM_IDPSVC_PASSWORD" desc:"Password to set for the \"idp\" service user. Either cleartext or an argon2id hash"`
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package config
|
||||
|
||||
// Debug defines the available debug configuration.
|
||||
type Debug struct {
|
||||
Addr string `yaml:"addr" env:"IDM_DEBUG_ADDR" desc:"Bind address of the debug server, where metrics, health, config and debug endpoints will be exposed."`
|
||||
Token string `yaml:"token" env:"IDM_DEBUG_TOKEN" desc:"Token to secure the metrics endpoint"`
|
||||
Pprof bool `yaml:"pprof" env:"IDM_DEBUG_PPROF" desc:"Enables pprof, which can be used for profiling"`
|
||||
Zpages bool `yaml:"zpages" env:"IDM_DEBUG_ZPAGES" desc:"Enables zpages, which can be used for collecting and viewing in-memory traces."`
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"path"
|
||||
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/config/defaults"
|
||||
)
|
||||
|
||||
func FullDefaultConfig() *config.Config {
|
||||
cfg := DefaultConfig()
|
||||
EnsureDefaults(cfg)
|
||||
Sanitize(cfg)
|
||||
return cfg
|
||||
}
|
||||
|
||||
func DefaultConfig() *config.Config {
|
||||
return &config.Config{
|
||||
Debug: config.Debug{
|
||||
Addr: "127.0.0.1:9239",
|
||||
},
|
||||
Service: config.Service{
|
||||
Name: "idm",
|
||||
},
|
||||
CreateDemoUsers: false,
|
||||
IDM: config.Settings{
|
||||
LDAPSAddr: "127.0.0.1:9235",
|
||||
Cert: path.Join(defaults.BaseDataPath(), "idm", "ldap.crt"),
|
||||
Key: path.Join(defaults.BaseDataPath(), "idm", "ldap.key"),
|
||||
DatabasePath: path.Join(defaults.BaseDataPath(), "idm", "ocis.boltdb"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func EnsureDefaults(cfg *config.Config) {
|
||||
// provide with defaults for shared logging, since we need a valid destination address for BindEnv.
|
||||
if cfg.Log == nil && cfg.Commons != nil && cfg.Commons.Log != nil {
|
||||
cfg.Log = &config.Log{
|
||||
Level: cfg.Commons.Log.Level,
|
||||
Pretty: cfg.Commons.Log.Pretty,
|
||||
Color: cfg.Commons.Log.Color,
|
||||
File: cfg.Commons.Log.File,
|
||||
}
|
||||
} else if cfg.Log == nil {
|
||||
cfg.Log = &config.Log{}
|
||||
}
|
||||
// provide with defaults for shared tracing, since we need a valid destination address for BindEnv.
|
||||
if cfg.Tracing == nil && cfg.Commons != nil && cfg.Commons.Tracing != nil {
|
||||
cfg.Tracing = &config.Tracing{
|
||||
Enabled: cfg.Commons.Tracing.Enabled,
|
||||
Type: cfg.Commons.Tracing.Type,
|
||||
Endpoint: cfg.Commons.Tracing.Endpoint,
|
||||
Collector: cfg.Commons.Tracing.Collector,
|
||||
}
|
||||
} else if cfg.Tracing == nil {
|
||||
cfg.Tracing = &config.Tracing{}
|
||||
}
|
||||
|
||||
if cfg.AdminUserID == "" && cfg.Commons != nil {
|
||||
cfg.AdminUserID = cfg.Commons.AdminUserID
|
||||
}
|
||||
}
|
||||
|
||||
func Sanitize(cfg *config.Config) {
|
||||
// nothing to sanitize here
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package config
|
||||
|
||||
// Log defines the available log configuration.
|
||||
type Log struct {
|
||||
Level string `mapstructure:"level" env:"OCIS_LOG_LEVEL;IDM_LOG_LEVEL" desc:"The log level. Valid values are: \"panic\", \"fatal\", \"error\", \"warn\", \"info\", \"debug\", \"trace\"."`
|
||||
Pretty bool `mapstructure:"pretty" env:"OCIS_LOG_PRETTY;IDM_LOG_PRETTY" desc:"Activates pretty log output."`
|
||||
Color bool `mapstructure:"color" env:"OCIS_LOG_COLOR;IDM_LOG_COLOR" desc:"Activates colorized log output."`
|
||||
File string `mapstructure:"file" env:"OCIS_LOG_FILE;IDM_LOG_FILE" desc:"The path to the log file. Activates logging to this file if set."`
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/config/defaults"
|
||||
ociscfg "github.com/owncloud/ocis/v2/ocis-pkg/config"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
|
||||
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/config/envdecode"
|
||||
)
|
||||
|
||||
// ParseConfig loads configuration from known paths.
|
||||
func ParseConfig(cfg *config.Config) error {
|
||||
_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defaults.EnsureDefaults(cfg)
|
||||
// load all env variables relevant to the config in the current context.
|
||||
if err := envdecode.Decode(cfg); err != nil {
|
||||
// no environment variable set for this config is an expected "error"
|
||||
if !errors.Is(err, envdecode.ErrNoTargetFieldsAreSet) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
defaults.Sanitize(cfg)
|
||||
|
||||
return Validate(cfg)
|
||||
}
|
||||
|
||||
func Validate(cfg *config.Config) error {
|
||||
if cfg.AdminUserID == "" {
|
||||
return shared.MissingAdminUserID(cfg.Service.Name)
|
||||
}
|
||||
|
||||
if cfg.ServiceUserPasswords.Idm == "" {
|
||||
return shared.MissingServiceUserPassword(cfg.Service.Name, "IDM")
|
||||
}
|
||||
|
||||
if cfg.ServiceUserPasswords.OcisAdmin == "" {
|
||||
return shared.MissingServiceUserPassword(cfg.Service.Name, "admin")
|
||||
}
|
||||
|
||||
if cfg.ServiceUserPasswords.Idp == "" {
|
||||
return shared.MissingServiceUserPassword(cfg.Service.Name, "IDP")
|
||||
}
|
||||
|
||||
if cfg.ServiceUserPasswords.Reva == "" {
|
||||
return shared.MissingServiceUserPassword(cfg.Service.Name, "REVA")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package config
|
||||
|
||||
// Service defines the available service configuration.
|
||||
type Service struct {
|
||||
Name string `yaml:"-"`
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package config
|
||||
|
||||
// Tracing defines the available tracing configuration.
|
||||
type Tracing struct {
|
||||
Enabled bool `yaml:"enabled" env:"OCIS_TRACING_ENABLED;IDM_TRACING_ENABLED" desc:"Activates tracing."`
|
||||
Type string `yaml:"type" env:"OCIS_TRACING_TYPE;IDM_TRACING_TYPE" desc:"The type of tracing. Defaults to \"\", which is the same as \"jaeger\". Allowed tracing types are \"jaeger\" and \"\" as of now."`
|
||||
Endpoint string `yaml:"endpoint" env:"OCIS_TRACING_ENDPOINT;IDM_TRACING_ENDPOINT" desc:"The endpoint of the tracing agent."`
|
||||
Collector string `yaml:"collector" env:"OCIS_TRACING_COLLECTOR;IDM_TRACING_COLLECTOR" desc:"The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset."`
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
)
|
||||
|
||||
// LoggerFromConfig initializes a service-specific logger instance.
|
||||
func Configure(name string, cfg *config.Log) log.Logger {
|
||||
return log.NewLogger(
|
||||
log.Name(name),
|
||||
log.Level(cfg.Level),
|
||||
log.Pretty(cfg.Pretty),
|
||||
log.Color(cfg.Color),
|
||||
log.File(cfg.File),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Logger log.Logger
|
||||
Context context.Context
|
||||
Config *config.Config
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
func Logger(val log.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
|
||||
// Context provides a function to set the context option.
|
||||
func Context(val context.Context) Option {
|
||||
return func(o *Options) {
|
||||
o.Context = val
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
func Config(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncloud/ocis/v2/extensions/idm/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/service/debug"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/version"
|
||||
)
|
||||
|
||||
// Server initializes the debug service and server.
|
||||
func Server(opts ...Option) (*http.Server, error) {
|
||||
options := newOptions(opts...)
|
||||
|
||||
return debug.NewService(
|
||||
debug.Logger(options.Logger),
|
||||
debug.Name(options.Config.Service.Name),
|
||||
debug.Version(version.GetString()),
|
||||
debug.Address(options.Config.Debug.Addr),
|
||||
debug.Token(options.Config.Debug.Token),
|
||||
debug.Pprof(options.Config.Debug.Pprof),
|
||||
debug.Zpages(options.Config.Debug.Zpages),
|
||||
debug.Health(health(options.Config)),
|
||||
debug.Ready(ready(options.Config)),
|
||||
), nil
|
||||
}
|
||||
|
||||
// health implements the health check.
|
||||
func health(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
// TODO: check if services are up and running
|
||||
|
||||
_, err := io.WriteString(w, http.StatusText(http.StatusOK))
|
||||
// io.WriteString should not fail but if it does we want to know.
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ready implements the ready check.
|
||||
func ready(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
// TODO: check if services are up and running
|
||||
|
||||
_, err := io.WriteString(w, http.StatusText(http.StatusOK))
|
||||
// io.WriteString should not fail but if it does we want to know.
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user