github.com/npaton/distribution@v2.3.1-rc.0+incompatible/registry/auth/silly/access.go (about)

     1  // Package silly provides a simple authentication scheme that checks for the
     2  // existence of an Authorization header and issues access if is present and
     3  // non-empty.
     4  //
     5  // This package is present as an example implementation of a minimal
     6  // auth.AccessController and for testing. This is not suitable for any kind of
     7  // production security.
     8  package silly
     9  
    10  import (
    11  	"fmt"
    12  	"net/http"
    13  	"strings"
    14  
    15  	"github.com/docker/distribution/context"
    16  	"github.com/docker/distribution/registry/auth"
    17  )
    18  
    19  // accessController provides a simple implementation of auth.AccessController
    20  // that simply checks for a non-empty Authorization header. It is useful for
    21  // demonstration and testing.
    22  type accessController struct {
    23  	realm   string
    24  	service string
    25  }
    26  
    27  var _ auth.AccessController = &accessController{}
    28  
    29  func newAccessController(options map[string]interface{}) (auth.AccessController, error) {
    30  	realm, present := options["realm"]
    31  	if _, ok := realm.(string); !present || !ok {
    32  		return nil, fmt.Errorf(`"realm" must be set for silly access controller`)
    33  	}
    34  
    35  	service, present := options["service"]
    36  	if _, ok := service.(string); !present || !ok {
    37  		return nil, fmt.Errorf(`"service" must be set for silly access controller`)
    38  	}
    39  
    40  	return &accessController{realm: realm.(string), service: service.(string)}, nil
    41  }
    42  
    43  // Authorized simply checks for the existence of the authorization header,
    44  // responding with a bearer challenge if it doesn't exist.
    45  func (ac *accessController) Authorized(ctx context.Context, accessRecords ...auth.Access) (context.Context, error) {
    46  	req, err := context.GetRequest(ctx)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  
    51  	if req.Header.Get("Authorization") == "" {
    52  		challenge := challenge{
    53  			realm:   ac.realm,
    54  			service: ac.service,
    55  		}
    56  
    57  		if len(accessRecords) > 0 {
    58  			var scopes []string
    59  			for _, access := range accessRecords {
    60  				scopes = append(scopes, fmt.Sprintf("%s:%s:%s", access.Type, access.Resource.Name, access.Action))
    61  			}
    62  			challenge.scope = strings.Join(scopes, " ")
    63  		}
    64  
    65  		return nil, &challenge
    66  	}
    67  
    68  	return auth.WithUser(ctx, auth.UserInfo{Name: "silly"}), nil
    69  }
    70  
    71  type challenge struct {
    72  	realm   string
    73  	service string
    74  	scope   string
    75  }
    76  
    77  var _ auth.Challenge = challenge{}
    78  
    79  // SetHeaders sets a simple bearer challenge on the response.
    80  func (ch challenge) SetHeaders(w http.ResponseWriter) {
    81  	header := fmt.Sprintf("Bearer realm=%q,service=%q", ch.realm, ch.service)
    82  
    83  	if ch.scope != "" {
    84  		header = fmt.Sprintf("%s,scope=%q", header, ch.scope)
    85  	}
    86  
    87  	w.Header().Set("WWW-Authenticate", header)
    88  }
    89  
    90  func (ch challenge) Error() string {
    91  	return fmt.Sprintf("silly authentication challenge: %#v", ch)
    92  }
    93  
    94  // init registers the silly auth backend.
    95  func init() {
    96  	auth.Register("silly", auth.InitFunc(newAccessController))
    97  }