mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-24 22:08:58 -05:00
For certain setups we don't need the ADMIN_USER_ID to be set. It is mainly needed for bootstrapping the internal idm and the initial role assignment. If roles are assigned by other means (e.g. OIDC claims in the future) we don't need it. This makes the ADMIN_USER_ID optional, also if ADMIN_USER_ID is unset we don't need to configure a password for the admin user. We will still generated the admin_id and password when running 'ocis init', but it is ok to run manual setups without those settings.
174 lines
4.6 KiB
Go
174 lines
4.6 KiB
Go
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/ocis-pkg/config/configlog"
|
|
pkgcrypto "github.com/owncloud/ocis/v2/ocis-pkg/crypto"
|
|
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
|
"github.com/owncloud/ocis/v2/services/idm"
|
|
"github.com/owncloud/ocis/v2/services/idm/pkg/config"
|
|
"github.com/owncloud/ocis/v2/services/idm/pkg/config/parser"
|
|
"github.com/owncloud/ocis/v2/services/idm/pkg/logging"
|
|
"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 the %s service without runtime (unsupervised mode)", cfg.Service.Name),
|
|
Category: "server",
|
|
Before: func(c *cli.Context) error {
|
|
return configlog.ReturnFatal(parser.ParseConfig(cfg))
|
|
},
|
|
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: "libregraph",
|
|
Password: cfg.ServiceUserPasswords.Idm,
|
|
},
|
|
{
|
|
Name: "idp",
|
|
Password: cfg.ServiceUserPasswords.Idp,
|
|
},
|
|
{
|
|
Name: "reva",
|
|
Password: cfg.ServiceUserPasswords.Reva,
|
|
},
|
|
}
|
|
|
|
if cfg.AdminUserID != "" {
|
|
serviceUsers = append(serviceUsers, svcUser{
|
|
Name: "admin",
|
|
Password: cfg.ServiceUserPasswords.OcisAdmin,
|
|
ID: cfg.AdminUserID,
|
|
})
|
|
}
|
|
|
|
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
|
|
}
|