diff --git a/changelog/unreleased/enhancment-admin-pwreset.md b/changelog/unreleased/enhancment-admin-pwreset.md new file mode 100644 index 0000000000..87931228b4 --- /dev/null +++ b/changelog/unreleased/enhancment-admin-pwreset.md @@ -0,0 +1,8 @@ +Enhancement: added command to reset administrator password + +The new command `ocis idm resetpassword` allows to reset the administrator +password when ocis is not running. So it is possible to recover setups +where the admin password was lost. + +https://github.com/owncloud/ocis/issues/4084 +https://github.com/owncloud/ocis/pull/4365 diff --git a/docs/services/idm/admin_password_reset.md b/docs/services/idm/admin_password_reset.md new file mode 100644 index 0000000000..1e3065c37c --- /dev/null +++ b/docs/services/idm/admin_password_reset.md @@ -0,0 +1,24 @@ +--- +title: Resetting a lost administrator password +date: 2022-08-29:00:00+00:00 +weight: 10 +geekdocRepo: https://github.com/owncloud/ocis +geekdocEditPath: edit/master/docs/services/idm +geekdocFilePath: admin_password_reset.md +geekdocCollapseSection: true +--- + +## Resetting a lost administrator password +By default, when using oCIS with the builtin IDM an ad generates the +user `admin` (DN `uid=admin,ou=users,o=libregraph-idm`) if, for any +reason, the password of that user is lost, it can be reset using +the `resetpassword` sub-command: + +``` +ocis idm resetpassword +``` + +It will prompt for a new password and set the password of that user +accordingly. Note: As this command is accessing the idm database directly +will only work while ocis is not running and nothing else is accessing +database. diff --git a/go.mod b/go.mod index fdec690bc1..2ff7116ab2 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.2 github.com/justinas/alice v1.2.0 - github.com/libregraph/idm v0.3.1-0.20220718103124-c4f6613dce2b + github.com/libregraph/idm v0.3.1-0.20220808071235-17bb032176de github.com/libregraph/lico v0.54.1-0.20220325072321-31efc3995d63 github.com/mitchellh/mapstructure v1.5.0 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 @@ -64,6 +64,7 @@ require ( github.com/urfave/cli/v2 v2.11.1 github.com/xhit/go-simple-mail/v2 v2.11.0 go-micro.dev/v4 v4.8.0 + go.etcd.io/bbolt v1.3.6 go.opencensus.io v0.23.0 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.34.0 go.opentelemetry.io/otel v1.9.0 @@ -75,6 +76,7 @@ require ( golang.org/x/image v0.0.0-20220321031419-a8550c1d254a golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c + golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 google.golang.org/genproto v0.0.0-20220805133916-01dd62135a58 google.golang.org/grpc v1.48.0 google.golang.org/protobuf v1.28.1 @@ -120,6 +122,7 @@ require ( github.com/blevesearch/zapx/v15 v15.3.4 // indirect github.com/bluele/gcache v0.0.2 // indirect github.com/bmizerany/pat v0.0.0-20210406213842-e4b6760bdd6f // indirect + github.com/bombsimon/logrusr/v3 v3.0.0 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/ceph/go-ceph v0.15.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect @@ -254,7 +257,6 @@ require ( github.com/wk8/go-ordered-map v1.0.0 // indirect github.com/xanzy/ssh-agent v0.3.1 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - go.etcd.io/bbolt v1.3.6 // indirect go.etcd.io/etcd/api/v3 v3.5.2 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.2 // indirect go.etcd.io/etcd/client/v3 v3.5.2 // indirect diff --git a/go.sum b/go.sum index e7436784f0..6480caa8e2 100644 --- a/go.sum +++ b/go.sum @@ -83,7 +83,6 @@ github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvd github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= -github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU= github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -224,6 +223,8 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dR github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/bmizerany/pat v0.0.0-20210406213842-e4b6760bdd6f h1:gOO/tNZMjjvTKZWpY7YnXC72ULNLErRtp94LountVE8= github.com/bmizerany/pat v0.0.0-20210406213842-e4b6760bdd6f/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= +github.com/bombsimon/logrusr/v3 v3.0.0 h1:tcAoLfuAhKP9npBxWzSdpsvKPQt1XV02nSf2lZA82TQ= +github.com/bombsimon/logrusr/v3 v3.0.0/go.mod h1:PksPPgSFEL2I52pla2glgCyyd2OqOHAnFF5E+g8Ixco= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= @@ -400,7 +401,6 @@ github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-ldap/ldap/v3 v3.1.7/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= github.com/go-ldap/ldap/v3 v3.4.2/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= -github.com/go-ldap/ldap/v3 v3.4.3/go.mod h1:7LdHfVt6iIOESVEe3Bs4Jp2sHEKgDeduAhgM1/f9qmo= github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs= github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI= github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3 h1:sfz1YppV05y4sYaW7kXZtrocU/+vimnIWt4cxAYh7+o= @@ -809,8 +809,8 @@ github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/libregraph/idm v0.3.1-0.20220718103124-c4f6613dce2b h1:/BNTnfP6lqixKUP2kP9gKWiPRaBWvE5BJcFcIVd2Ekg= -github.com/libregraph/idm v0.3.1-0.20220718103124-c4f6613dce2b/go.mod h1:ggVmYkaK5fu680QOnxkuyCRW5Wl5qzaYXgIiieNBOJE= +github.com/libregraph/idm v0.3.1-0.20220808071235-17bb032176de h1:iDKkd+RQt/sddvPNQrfFQkExbyt4gxDyTQyi9DkP/c0= +github.com/libregraph/idm v0.3.1-0.20220808071235-17bb032176de/go.mod h1:syzZjsjzpnjGibVayqnIywXSvvGanU8cDd9uotqoPcw= github.com/libregraph/lico v0.54.1-0.20220325072321-31efc3995d63 h1:oPqyRePmq+59YF1tAur7WXuM/z/epRd+HGGyPPx2Vv8= github.com/libregraph/lico v0.54.1-0.20220325072321-31efc3995d63/go.mod h1:KZ4X+bEbOQMSV6iPysZEqVO/Pa5Mvo7xhhcLwUNPjmw= github.com/linode/linodego v0.25.3/go.mod h1:GSBKPpjoQfxEfryoCRcgkuUOCuVtGHWhzI8OMdycNTE= @@ -1014,7 +1014,6 @@ github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= github.com/prometheus/client_model v0.0.0-20170216185247-6f3806018612/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -1308,7 +1307,6 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -1449,6 +1447,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1558,6 +1557,7 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1568,6 +1568,7 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= +golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/services/idm/pkg/command/resetpw.go b/services/idm/pkg/command/resetpw.go new file mode 100644 index 0000000000..7fd5c52eba --- /dev/null +++ b/services/idm/pkg/command/resetpw.go @@ -0,0 +1,118 @@ +package command + +import ( + "context" + "errors" + "fmt" + "os" + "syscall" + "time" + + "github.com/go-ldap/ldap/v3" + "github.com/libregraph/idm/pkg/ldbbolt" + "github.com/libregraph/idm/server" + "github.com/owncloud/ocis/v2/ocis-pkg/log" + "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" + bolt "go.etcd.io/bbolt" + "golang.org/x/term" +) + +// ResetPassword is the entrypoint for the resetpassword command +func ResetPassword(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "resetpassword", + Usage: fmt.Sprintf("Reset admin password"), + Category: "password reset", + 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 resetPassword(ctx, logger, cfg) + }, + } +} + +func resetPassword(ctx context.Context, logger log.Logger, cfg *config.Config) error { + servercfg := server.Config{ + Logger: log.LogrusWrap(logger.Logger), + LDAPHandler: "boltdb", + LDAPBaseDN: "o=libregraph-idm", + + BoltDBFile: cfg.IDM.DatabasePath, + } + + adminUserDN := "uid=admin,ou=users," + servercfg.LDAPBaseDN + fmt.Printf("Resetting password for user '%s'.\n", adminUserDN) + if _, err := os.Stat(servercfg.BoltDBFile); errors.Is(err, os.ErrNotExist) { + fmt.Fprintf(os.Stderr, "IDM database does not exist.\n") + return err + } + + newPw, err := getPassword() + if err != nil { + fmt.Fprintf(os.Stderr, "Error reading password: %v\n", err) + return err + } + + bdb := &ldbbolt.LdbBolt{} + + opts := bolt.Options{ + Timeout: 1 * time.Millisecond, + } + if err := bdb.Configure(servercfg.Logger, servercfg.LDAPBaseDN, servercfg.BoltDBFile, &opts); err != nil { + fmt.Fprintf(os.Stderr, "Failed to open database: '%s'. Please stop any running ocis/idm instance, as this tool requires exclusive access to the database.\n", err) + return err + } + defer bdb.Close() + + if err := bdb.Initialize(); err != nil { + return err + } + + pwRequest := ldap.NewPasswordModifyRequest(adminUserDN, "", newPw) + if err := bdb.UpdatePassword(pwRequest); err != nil { + fmt.Fprintf(os.Stderr, "Failed to update admin password: %v\n", err) + } + fmt.Printf("Password for user '%s' updated.\n", adminUserDN) + return nil +} + +func getPassword() (string, error) { + fmt.Print("Enter new password: ") + bytePassword, err := term.ReadPassword(int(syscall.Stdin)) + if err != nil { + return "", err + } + fmt.Println("") + fmt.Print("Re-enter new password: ") + bytePasswordVerify, err := term.ReadPassword(int(syscall.Stdin)) + if err != nil { + return "", err + } + fmt.Println("") + + password := string(bytePassword) + passwordVerify := string(bytePasswordVerify) + + if password != passwordVerify { + return "", errors.New("Passwords do not match") + } + return password, nil +} diff --git a/services/idm/pkg/command/root.go b/services/idm/pkg/command/root.go index 84208cba84..ee1ccb95d9 100644 --- a/services/idm/pkg/command/root.go +++ b/services/idm/pkg/command/root.go @@ -18,6 +18,7 @@ func GetCommands(cfg *config.Config) cli.Commands { Server(cfg), // interaction with this service + ResetPassword(cfg), // infos about this service Health(cfg),