Dynamically Parsing the Latest HTPassword File

To parse the latest account list dynamically instead of restarting the distribution service frequently.

Signed-off-by: CUI Wei <ghostplant@qq.com>
This commit is contained in:
cuiwei13 2016-08-26 13:18:34 +08:00
parent dea554fc7c
commit 169ff1d098

View file

@ -9,6 +9,8 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"os" "os"
"sync"
"time"
"github.com/docker/distribution/context" "github.com/docker/distribution/context"
"github.com/docker/distribution/registry/auth" "github.com/docker/distribution/registry/auth"
@ -16,6 +18,9 @@ import (
type accessController struct { type accessController struct {
realm string realm string
path string
modtime time.Time
mu sync.Mutex
htpasswd *htpasswd htpasswd *htpasswd
} }
@ -32,18 +37,7 @@ func newAccessController(options map[string]interface{}) (auth.AccessController,
return nil, fmt.Errorf(`"path" must be set for htpasswd access controller`) return nil, fmt.Errorf(`"path" must be set for htpasswd access controller`)
} }
f, err := os.Open(path.(string)) return &accessController{realm: realm.(string), path: path.(string)}, nil
if err != nil {
return nil, err
}
defer f.Close()
h, err := newHTPasswd(f)
if err != nil {
return nil, err
}
return &accessController{realm: realm.(string), htpasswd: h}, nil
} }
func (ac *accessController) Authorized(ctx context.Context, accessRecords ...auth.Access) (context.Context, error) { func (ac *accessController) Authorized(ctx context.Context, accessRecords ...auth.Access) (context.Context, error) {
@ -60,7 +54,35 @@ func (ac *accessController) Authorized(ctx context.Context, accessRecords ...aut
} }
} }
if err := ac.AuthenticateUser(username, password); err != nil { // Dynamically parsing the latest account list
fstat, err := os.Stat(ac.path)
if err != nil {
return nil, err
}
lastModified := fstat.ModTime()
ac.mu.Lock()
if ac.htpasswd == nil || !ac.modtime.Equal(lastModified) {
ac.modtime = lastModified
f, err := os.Open(ac.path)
if err != nil {
ac.mu.Unlock()
return nil, err
}
defer f.Close()
h, err := newHTPasswd(f)
if err != nil {
ac.mu.Unlock()
return nil, err
}
ac.htpasswd = h
}
localHTPasswd := ac.htpasswd
ac.mu.Unlock()
if err := localHTPasswd.authenticateUser(username, password); err != nil {
context.GetLogger(ctx).Errorf("error authenticating user %q: %v", username, err) context.GetLogger(ctx).Errorf("error authenticating user %q: %v", username, err)
return nil, &challenge{ return nil, &challenge{
realm: ac.realm, realm: ac.realm,
@ -71,10 +93,6 @@ func (ac *accessController) Authorized(ctx context.Context, accessRecords ...aut
return auth.WithUser(ctx, auth.UserInfo{Name: username}), nil return auth.WithUser(ctx, auth.UserInfo{Name: username}), nil
} }
func (ac *accessController) AuthenticateUser(username, password string) error {
return ac.htpasswd.authenticateUser(username, password)
}
// challenge implements the auth.Challenge interface. // challenge implements the auth.Challenge interface.
type challenge struct { type challenge struct {
realm string realm string