diff --git a/server/config.go b/server/config.go index 6400138acc53043138a9d178ac906f761f56cf1c..415c8e18985a20ddbbd958d55711fbc71920c122 100644 --- a/server/config.go +++ b/server/config.go @@ -38,6 +38,7 @@ type Config struct { URLPrefix string `yaml:"url_path_prefix"` DeviceManager *device.Config `yaml:"device_manager"` KeyStore *clientutil.BackendConfig `yaml:"keystore"` + KeyStoreEnableGroups []string `yaml:"keystore_enable_groups"` allowedServicesRx []*regexp.Regexp } diff --git a/server/http.go b/server/http.go index 208067ba900f910c9a48def255d403d0aef2444f..be96d516eefc0cc6e632d083df18a6ffeeab7d6e 100644 --- a/server/http.go +++ b/server/http.go @@ -4,6 +4,7 @@ package server //go:generate go-bindata --nocompress --pkg server static/... templates/... import ( + "context" "encoding/gob" "encoding/json" "fmt" @@ -88,6 +89,7 @@ type Server struct { loginHandler *loginHandler loginService *LoginService keystore ksclient.Client + keystoreGroups []string csrfSecret []byte tpl *template.Template urlPrefix string @@ -134,6 +136,7 @@ func New(loginService *LoginService, authClient authclient.Client, config *Confi } log.Printf("keystore client enabled") s.keystore = ks + s.keystoreGroups = config.KeyStoreEnableGroups } devMgr, err := device.New(config.DeviceManager) @@ -145,25 +148,51 @@ func New(loginService *LoginService, authClient authclient.Client, config *Confi return s, nil } +func inAnyGroups(groups, ref []string) bool { + for _, rr := range ref { + for _, gg := range groups { + if gg == rr { + return true + } + } + } + return false +} + +// We unlock the keystore if the following conditions are met: +// keystore_enable_groups is set, userinfo is not nil, and the groups match. +func (h *Server) maybeUnlockKeystore(ctx context.Context, username, password string, userinfo *auth.UserInfo) (bool, error) { + if h.keystore == nil { + return false, nil + } + + var shard string + if len(h.keystoreGroups) > 0 { + if userinfo == nil { + return false, nil + } + if !inAnyGroups(userinfo.Groups, h.keystoreGroups) { + return false, nil + } + shard = userinfo.Shard + } + return true, h.keystore.Open(ctx, shard, username, password, int(h.authSessionLifetime.Seconds())) +} + func (h *Server) loginCallback(w http.ResponseWriter, req *http.Request, username, password string, userinfo *auth.UserInfo) error { // Open the keystore for this user with the password used to // authenticate. Set the TTL to the duration of the // authenticated session. - var kmsg string - if h.keystore != nil { - var shard string - if userinfo != nil { - shard = userinfo.Shard - kmsg = fmt.Sprintf(" (unlocked key on shard %s)", shard) - } else { - kmsg = " (unlocked key)" - } - if err := h.keystore.Open(req.Context(), shard, username, password, int(h.authSessionLifetime.Seconds())); err != nil { - log.Printf("failed to unlock keystore for user %s: %v", username, err) - return err - } + decrypted, err := h.maybeUnlockKeystore(req.Context(), username, password, userinfo) + if err != nil { + log.Printf("failed to unlock keystore for user %s: %v", username, err) + return err } + var kmsg string + if decrypted { + kmsg = " (key unlocked)" + } log.Printf("successful login for user %s%s", username, kmsg) // Create cookie-based session for the authenticated user.