| package authorization | |
| import ( | |
| "context" | |
| "net/http" | |
| "sync" | |
| "github.com/containerd/log" | |
| "github.com/moby/moby/v2/pkg/plugingetter" | |
| ) | |
| // Middleware uses a list of plugins to | |
| // handle authorization in the API requests. | |
| type Middleware struct { | |
| mu sync.Mutex | |
| plugins []Plugin | |
| } | |
| // NewMiddleware creates a new Middleware | |
| // with a slice of plugins names. | |
| func NewMiddleware(names []string, pg plugingetter.PluginGetter) *Middleware { | |
| SetPluginGetter(pg) | |
| return &Middleware{ | |
| plugins: newPlugins(names), | |
| } | |
| } | |
| func (m *Middleware) getAuthzPlugins() []Plugin { | |
| m.mu.Lock() | |
| defer m.mu.Unlock() | |
| return m.plugins | |
| } | |
| // SetPlugins sets the plugin used for authorization | |
| func (m *Middleware) SetPlugins(names []string) { | |
| m.mu.Lock() | |
| m.plugins = newPlugins(names) | |
| m.mu.Unlock() | |
| } | |
| // 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 | |
| } | |
| // WrapHandler returns a new handler function wrapping the previous one in the request chain. | |
| 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 { | |
| return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { | |
| plugins := m.getAuthzPlugins() | |
| if len(plugins) == 0 { | |
| return handler(ctx, w, r, vars) | |
| } | |
| 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/moby/moby/pull/20883 | |
| if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 { | |
| user = r.TLS.PeerCertificates[0].Subject.CommonName | |
| userAuthNMethod = "TLS" | |
| } | |
| authCtx := NewCtx(plugins, user, userAuthNMethod, r.Method, r.RequestURI) | |
| if err := authCtx.AuthZRequest(w, r); err != nil { | |
| log.G(ctx).Errorf("AuthZRequest for %s %s returned error: %s", r.Method, r.RequestURI, err) | |
| return err | |
| } | |
| rw := NewResponseModifier(w) | |
| var errD error | |
| if errD = handler(ctx, rw, r, vars); errD != nil { | |
| log.G(ctx).Errorf("Handler for %s %s returned error: %s", r.Method, r.RequestURI, errD) | |
| } | |
| // There's a chance that the authCtx.plugins was updated. One of the reasons | |
| // this can happen is when an authzplugin is disabled. | |
| plugins = m.getAuthzPlugins() | |
| if len(plugins) == 0 { | |
| log.G(ctx).Debug("There are no authz plugins in the chain") | |
| return nil | |
| } | |
| authCtx.plugins = plugins | |
| if err := authCtx.AuthZResponse(rw, r); errD == nil && err != nil { | |
| log.G(ctx).Errorf("AuthZResponse for %s %s returned error: %s", r.Method, r.RequestURI, err) | |
| return err | |
| } | |
| if errD != nil { | |
| return errD | |
| } | |
| return nil | |
| } | |
| } |