pkg/authorization/middleware.go
8d346762
 package authorization
 
 import (
 	"net/http"
a616cf3b
 	"sync"
8d346762
 
c5393ee1
 	"github.com/docker/docker/pkg/plugingetter"
1009e6a4
 	"github.com/sirupsen/logrus"
8d346762
 	"golang.org/x/net/context"
 )
 
 // Middleware uses a list of plugins to
 // handle authorization in the API requests.
 type Middleware struct {
a616cf3b
 	mu      sync.Mutex
8d346762
 	plugins []Plugin
 }
 
 // NewMiddleware creates a new Middleware
4192fe9c
 // with a slice of plugins names.
c5393ee1
 func NewMiddleware(names []string, pg plugingetter.PluginGetter) *Middleware {
 	SetPluginGetter(pg)
4192fe9c
 	return &Middleware{
 		plugins: newPlugins(names),
8d346762
 	}
 }
 
24264697
 func (m *Middleware) getAuthzPlugins() []Plugin {
38de272b
 	m.mu.Lock()
 	defer m.mu.Unlock()
 	return m.plugins
 }
 
4192fe9c
 // SetPlugins sets the plugin used for authorization
 func (m *Middleware) SetPlugins(names []string) {
a616cf3b
 	m.mu.Lock()
4192fe9c
 	m.plugins = newPlugins(names)
a616cf3b
 	m.mu.Unlock()
4192fe9c
 }
 
7da39862
 // RemovePlugin removes a single plugin from this authz middleware chain
 func (m *Middleware) RemovePlugin(name string) {
 	m.mu.Lock()
 	defer m.mu.Unlock()
 	plugins := m.plugins[:0]
 	for _, authPlugin := range m.plugins {
 		if authPlugin.Name() != name {
 			plugins = append(plugins, authPlugin)
 		}
 	}
 	m.plugins = plugins
 }
 
8d346762
 // WrapHandler returns a new handler function wrapping the previous one in the request chain.
4192fe9c
 func (m *Middleware) WrapHandler(handler func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error) func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
8d346762
 	return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
24264697
 		plugins := m.getAuthzPlugins()
a616cf3b
 		if len(plugins) == 0 {
4192fe9c
 			return handler(ctx, w, r, vars)
 		}
 
8d346762
 		user := ""
 		userAuthNMethod := ""
 
 		// Default authorization using existing TLS connection credentials
 		// FIXME: Non trivial authorization mechanisms (such as advanced certificate validations, kerberos support
 		// and ldap) will be extracted using AuthN feature, which is tracked under:
 		// https://github.com/docker/docker/pull/20883
 		if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
 			user = r.TLS.PeerCertificates[0].Subject.CommonName
 			userAuthNMethod = "TLS"
 		}
 
a616cf3b
 		authCtx := NewCtx(plugins, user, userAuthNMethod, r.Method, r.RequestURI)
8d346762
 
 		if err := authCtx.AuthZRequest(w, r); err != nil {
 			logrus.Errorf("AuthZRequest for %s %s returned error: %s", r.Method, r.RequestURI, err)
 			return err
 		}
 
 		rw := NewResponseModifier(w)
 
5a8ff402
 		var errD error
 
 		if errD = handler(ctx, rw, r, vars); errD != nil {
 			logrus.Errorf("Handler for %s %s returned error: %s", r.Method, r.RequestURI, errD)
8d346762
 		}
 
38de272b
 		// There's a chance that the authCtx.plugins was updated. One of the reasons
 		// this can happen is when an authzplugin is disabled.
24264697
 		plugins = m.getAuthzPlugins()
38de272b
 		if len(plugins) == 0 {
 			logrus.Debug("There are no authz plugins in the chain")
 			return nil
 		}
 
 		authCtx.plugins = plugins
 
5a8ff402
 		if err := authCtx.AuthZResponse(rw, r); errD == nil && err != nil {
8d346762
 			logrus.Errorf("AuthZResponse for %s %s returned error: %s", r.Method, r.RequestURI, err)
 			return err
 		}
5a8ff402
 
 		if errD != nil {
 			return errD
 		}
 
8d346762
 		return nil
 	}
 }