From f7de237ffc96f09ff63edd83a6333e22fcbf5149 Mon Sep 17 00:00:00 2001 From: Evan Cordell Date: Tue, 10 Jan 2017 17:36:53 -0500 Subject: [PATCH 1/6] small refactoring to make it easier to use notary as a library Signed-off-by: Evan Cordell --- server/server.go | 64 +++++++++++++++++++++++------------- server/storage/sql_models.go | 14 ++++++-- utils/http.go | 3 ++ 3 files changed, 57 insertions(+), 24 deletions(-) diff --git a/server/server.go b/server/server.go index eb0bd9fee..e7c7d4146 100644 --- a/server/server.go +++ b/server/server.go @@ -116,13 +116,27 @@ func filterImagePrefixes(requiredPrefixes []string, err error, handler http.Hand }) } -type _serverEndpoint struct { +// EndpointConfig stores settings used to create a Notary handler +type EndpointConfig struct { OperationName string ServerHandler utils.ContextHandler ErrorIfGUNInvalid error IncludeCacheHeaders bool CacheControlConfig utils.CacheControlConfig PermissionsRequired []string + AuthWrapper utils.AuthWrapper + RepoPrefixes []string +} + +// CreateHandler creates a server handler from an EndpointConfig +func CreateHandler(opts EndpointConfig) http.Handler { + var wrapped http.Handler + wrapped = opts.AuthWrapper(opts.ServerHandler, opts.PermissionsRequired...) + if opts.IncludeCacheHeaders { + wrapped = utils.WrapWithCacheHandler(opts.CacheControlConfig, wrapped) + } + wrapped = filterImagePrefixes(opts.RepoPrefixes, opts.ErrorIfGUNInvalid, wrapped) + return prometheus.InstrumentHandlerWithOpts(prometheusOpts(opts.OperationName), wrapped) } // RootHandler returns the handler that routes all the paths from / for the @@ -132,84 +146,90 @@ func RootHandler(ctx context.Context, ac auth.AccessController, trust signed.Cry authWrapper := utils.RootHandlerFactory(ctx, ac, trust) - createHandler := func(opts _serverEndpoint) http.Handler { - var wrapped http.Handler - wrapped = authWrapper(opts.ServerHandler, opts.PermissionsRequired...) - if opts.IncludeCacheHeaders { - wrapped = utils.WrapWithCacheHandler(opts.CacheControlConfig, wrapped) - } - wrapped = filterImagePrefixes(repoPrefixes, opts.ErrorIfGUNInvalid, wrapped) - return prometheus.InstrumentHandlerWithOpts(prometheusOpts(opts.OperationName), wrapped) - } - invalidGUNErr := errors.ErrInvalidGUN.WithDetail(fmt.Sprintf("Require GUNs with prefix: %v", repoPrefixes)) notFoundError := errors.ErrMetadataNotFound.WithDetail(nil) r := mux.NewRouter() r.Methods("GET").Path("/v2/").Handler(authWrapper(handlers.MainHandler)) - - r.Methods("POST").Path("/v2/{imageName:[^*]+}/_trust/tuf/").Handler(createHandler(_serverEndpoint{ + r.Methods("POST").Path("/v2/{imageName:[^*]+}/_trust/tuf/").Handler(CreateHandler(EndpointConfig{ OperationName: "UpdateTUF", ErrorIfGUNInvalid: invalidGUNErr, ServerHandler: handlers.AtomicUpdateHandler, PermissionsRequired: []string{"push", "pull"}, + AuthWrapper: authWrapper, + RepoPrefixes: repoPrefixes, })) - r.Methods("GET").Path("/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.{checksum:[a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128}}.json").Handler(createHandler(_serverEndpoint{ + r.Methods("GET").Path("/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.{checksum:[a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128}}.json").Handler(CreateHandler(EndpointConfig{ OperationName: "GetRoleByHash", ErrorIfGUNInvalid: notFoundError, IncludeCacheHeaders: true, CacheControlConfig: consistent, ServerHandler: handlers.GetHandler, PermissionsRequired: []string{"pull"}, + AuthWrapper: authWrapper, + RepoPrefixes: repoPrefixes, })) - r.Methods("GET").Path("/v2/{imageName:[^*]+}/_trust/tuf/{version:[1-9]*[0-9]+}.{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.json").Handler(createHandler(_serverEndpoint{ + r.Methods("GET").Path("/v2/{imageName:[^*]+}/_trust/tuf/{version:[1-9]*[0-9]+}.{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.json").Handler(CreateHandler(EndpointConfig{ OperationName: "GetRoleByVersion", ErrorIfGUNInvalid: notFoundError, IncludeCacheHeaders: true, CacheControlConfig: consistent, ServerHandler: handlers.GetHandler, PermissionsRequired: []string{"pull"}, + AuthWrapper: authWrapper, + RepoPrefixes: repoPrefixes, })) - r.Methods("GET").Path("/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.json").Handler(createHandler(_serverEndpoint{ + r.Methods("GET").Path("/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.json").Handler(CreateHandler(EndpointConfig{ OperationName: "GetRole", ErrorIfGUNInvalid: notFoundError, IncludeCacheHeaders: true, CacheControlConfig: current, ServerHandler: handlers.GetHandler, PermissionsRequired: []string{"pull"}, + AuthWrapper: authWrapper, + RepoPrefixes: repoPrefixes, })) r.Methods("GET").Path( - "/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:snapshot|timestamp}.key").Handler(createHandler(_serverEndpoint{ + "/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:snapshot|timestamp}.key").Handler(CreateHandler(EndpointConfig{ OperationName: "GetKey", ErrorIfGUNInvalid: notFoundError, ServerHandler: handlers.GetKeyHandler, PermissionsRequired: []string{"push", "pull"}, + AuthWrapper: authWrapper, + RepoPrefixes: repoPrefixes, })) r.Methods("POST").Path( - "/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:snapshot|timestamp}.key").Handler(createHandler(_serverEndpoint{ + "/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:snapshot|timestamp}.key").Handler(CreateHandler(EndpointConfig{ OperationName: "RotateKey", ErrorIfGUNInvalid: notFoundError, ServerHandler: handlers.RotateKeyHandler, PermissionsRequired: []string{"*"}, + AuthWrapper: authWrapper, + RepoPrefixes: repoPrefixes, })) - r.Methods("DELETE").Path("/v2/{imageName:[^*]+}/_trust/tuf/").Handler(createHandler(_serverEndpoint{ + r.Methods("DELETE").Path("/v2/{imageName:[^*]+}/_trust/tuf/").Handler(CreateHandler(EndpointConfig{ OperationName: "DeleteTUF", ErrorIfGUNInvalid: notFoundError, ServerHandler: handlers.DeleteHandler, PermissionsRequired: []string{"*"}, + AuthWrapper: authWrapper, + RepoPrefixes: repoPrefixes, })) - r.Methods("GET").Path("/v2/{imageName:[^*]+}/_trust/changefeed").Handler(createHandler(_serverEndpoint{ + r.Methods("GET").Path("/v2/{imageName:[^*]+}/_trust/changefeed").Handler(CreateHandler(EndpointConfig{ OperationName: "Changefeed", ErrorIfGUNInvalid: notFoundError, ServerHandler: handlers.Changefeed, PermissionsRequired: []string{"pull"}, + AuthWrapper: authWrapper, + RepoPrefixes: repoPrefixes, })) - r.Methods("GET").Path("/v2/_trust/changefeed").Handler(createHandler(_serverEndpoint{ + r.Methods("GET").Path("/v2/_trust/changefeed").Handler(CreateHandler(EndpointConfig{ OperationName: "Changefeed", ServerHandler: handlers.Changefeed, PermissionsRequired: []string{"*"}, + AuthWrapper: authWrapper, + RepoPrefixes: repoPrefixes, })) - r.Methods("GET").Path("/_notary_server/health").HandlerFunc(health.StatusHandler) r.Methods("GET").Path("/metrics").Handler(prometheus.Handler()) r.Methods("GET", "POST", "PUT", "HEAD", "DELETE").Path("/{other:.*}").Handler( diff --git a/server/storage/sql_models.go b/server/storage/sql_models.go index 50aec1783..7e1bcce58 100644 --- a/server/storage/sql_models.go +++ b/server/storage/sql_models.go @@ -11,6 +11,16 @@ const ( changeCategoryDeletion = "deletion" ) +// TUFFileTableName returns the name used for the tuf file table +var TUFFileTableName = func() string { + return "tuf_files" +} + +// ChangefeedTableName returns the name used for the changefeed table +var ChangefeedTableName = func() string { + return "changefeed" +} + // TUFFile represents a TUF file in the database type TUFFile struct { gorm.Model @@ -23,7 +33,7 @@ type TUFFile struct { // TableName sets a specific table name for TUFFile func (g TUFFile) TableName() string { - return "tuf_files" + return TUFFileTableName() } // Change defines the the fields required for an object in the changefeed @@ -38,7 +48,7 @@ type Change struct { // TableName sets a specific table name for Changefeed func (c Change) TableName() string { - return "changefeed" + return ChangefeedTableName() } // CreateTUFTable creates the DB table for TUFFile diff --git a/utils/http.go b/utils/http.go index ea47a3c95..d02d8fa8f 100644 --- a/utils/http.go +++ b/utils/http.go @@ -29,6 +29,9 @@ type rootHandler struct { trust signed.CryptoService } +// AuthWrapper wraps a Handler with and Auth requirement +type AuthWrapper func(ContextHandler, ...string) *rootHandler + // RootHandlerFactory creates a new rootHandler factory using the given // Context creator and authorizer. The returned factory allows creating // new rootHandlers from the alternate http handler contextHandler and From df0a50d1bce7aac6a8707eacf88c3dd15b27aef4 Mon Sep 17 00:00:00 2001 From: Evan Cordell Date: Wed, 22 Feb 2017 11:14:06 -0500 Subject: [PATCH 2/6] Rename `imageName` -> `gun` Signed-off-by: Evan Cordell --- server/handlers/changefeed.go | 8 ++--- server/handlers/changefeed_test.go | 32 ++++++++++---------- server/handlers/default.go | 8 ++--- server/handlers/default_test.go | 48 +++++++++++++++--------------- server/server.go | 24 +++++++-------- utils/http.go | 10 +++---- 6 files changed, 65 insertions(+), 65 deletions(-) diff --git a/server/handlers/changefeed.go b/server/handlers/changefeed.go index 04ef67e2d..c42353ebe 100644 --- a/server/handlers/changefeed.go +++ b/server/handlers/changefeed.go @@ -26,7 +26,7 @@ func Changefeed(ctx context.Context, w http.ResponseWriter, r *http.Request) err vars = mux.Vars(r) logger = ctxu.GetLogger(ctx) qs = r.URL.Query() - imageName = vars["imageName"] + gun = vars["gun"] changeID = qs.Get("change_id") store, records, err = checkChangefeedInputs(logger, ctx.Value(notary.CtxKeyMetaStore), qs.Get("records")) ) @@ -34,15 +34,15 @@ func Changefeed(ctx context.Context, w http.ResponseWriter, r *http.Request) err // err already logged and in correct format. return err } - out, err := changefeed(logger, store, imageName, changeID, records) + out, err := changefeed(logger, store, gun, changeID, records) if err == nil { w.Write(out) } return err } -func changefeed(logger ctxu.Logger, store storage.MetaStore, imageName, changeID string, records int64) ([]byte, error) { - changes, err := store.GetChanges(changeID, int(records), imageName) +func changefeed(logger ctxu.Logger, store storage.MetaStore, gun, changeID string, records int64) ([]byte, error) { + changes, err := store.GetChanges(changeID, int(records), gun) if err != nil { logger.Errorf("%d GET could not retrieve records: %s", http.StatusInternalServerError, err.Error()) return nil, errors.ErrUnknown.WithDetail(err) diff --git a/server/handlers/changefeed_test.go b/server/handlers/changefeed_test.go index e2a77183c..27da41e52 100644 --- a/server/handlers/changefeed_test.go +++ b/server/handlers/changefeed_test.go @@ -12,11 +12,11 @@ import ( ) type changefeedArgs struct { - logger ctxu.Logger - store storage.MetaStore - imageName string - changeID string - pageSize int64 + logger ctxu.Logger + store storage.MetaStore + gun string + changeID string + pageSize int64 } type changefeedTest struct { @@ -33,11 +33,11 @@ func Test_changefeed(t *testing.T) { { name: "Empty Store", args: changefeedArgs{ - logger: logrus.New(), - store: s, - imageName: "", - changeID: "0", - pageSize: notary.DefaultPageSize, + logger: logrus.New(), + store: s, + gun: "", + changeID: "0", + pageSize: notary.DefaultPageSize, }, want: []byte("{\"count\":0,\"records\":null}"), wantErr: false, @@ -45,11 +45,11 @@ func Test_changefeed(t *testing.T) { { name: "Bad ChangeID", args: changefeedArgs{ - logger: logrus.New(), - store: s, - imageName: "", - changeID: "not_a_number", - pageSize: notary.DefaultPageSize, + logger: logrus.New(), + store: s, + gun: "", + changeID: "not_a_number", + pageSize: notary.DefaultPageSize, }, want: nil, wantErr: true, @@ -61,7 +61,7 @@ func Test_changefeed(t *testing.T) { func runChangefeedTests(t *testing.T, tests []changefeedTest) { for _, tt := range tests { - got, err := changefeed(tt.args.logger, tt.args.store, tt.args.imageName, tt.args.changeID, tt.args.pageSize) + got, err := changefeed(tt.args.logger, tt.args.store, tt.args.gun, tt.args.changeID, tt.args.pageSize) if tt.wantErr { require.Error(t, err, "%q. changefeed() error = %v, wantErr %v", tt.name, err, tt.wantErr) diff --git a/server/handlers/default.go b/server/handlers/default.go index 23112116c..09b37e80e 100644 --- a/server/handlers/default.go +++ b/server/handlers/default.go @@ -44,7 +44,7 @@ func AtomicUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Req } func atomicUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - gun := data.GUN(vars["imageName"]) + gun := data.GUN(vars["gun"]) s := ctx.Value(notary.CtxKeyMetaStore) logger := ctxu.GetLoggerWithField(ctx, gun, "gun") store, ok := s.(storage.MetaStore) @@ -125,7 +125,7 @@ func GetHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) err } func getHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - gun := data.GUN(vars["imageName"]) + gun := data.GUN(vars["gun"]) checksum := vars["checksum"] version := vars["version"] tufRole := vars["tufRole"] @@ -161,7 +161,7 @@ func getHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, var // DeleteHandler deletes all data for a GUN. A 200 responses indicates success. func DeleteHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) error { vars := mux.Vars(r) - gun := data.GUN(vars["imageName"]) + gun := data.GUN(vars["gun"]) logger := ctxu.GetLoggerWithField(ctx, gun, "gun") s := ctx.Value(notary.CtxKeyMetaStore) store, ok := s.(storage.MetaStore) @@ -256,7 +256,7 @@ func rotateKeyHandler(ctx context.Context, w http.ResponseWriter, r *http.Reques // To be called before getKeyHandler or rotateKeyHandler func setupKeyHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string, actionVerb string) (data.RoleName, data.GUN, string, storage.MetaStore, signed.CryptoService, error) { - gun := data.GUN(vars["imageName"]) + gun := data.GUN(vars["gun"]) logger := ctxu.GetLoggerWithField(ctx, gun, "gun") if gun == "" { logger.Infof("400 %s no gun in request", actionVerb) diff --git a/server/handlers/default_test.go b/server/handlers/default_test.go index fd14b7614..1b637c4d8 100644 --- a/server/handlers/default_test.go +++ b/server/handlers/default_test.go @@ -106,8 +106,8 @@ func TestKeyHandlersInvalidConfiguration(t *testing.T) { } vars := map[string]string{ - "imageName": "gun", - "tufRole": data.CanonicalTimestampRole.String(), + "gun": "gun", + "tufRole": data.CanonicalTimestampRole.String(), } req := &http.Request{Body: ioutil.NopCloser(bytes.NewBuffer(nil))} for _, keyHandler := range []simplerHandler{getKeyHandler, rotateKeyHandler} { @@ -121,16 +121,16 @@ func TestKeyHandlersInvalidConfiguration(t *testing.T) { } } -// GetKeyHandler and RotateKeyHandler need to be set up such that an imageName and tufRole are both +// GetKeyHandler and RotateKeyHandler need to be set up such that an gun and tufRole are both // provided and non-empty. func TestKeyHandlersNoRoleOrRepo(t *testing.T) { state := defaultState() req := &http.Request{Body: ioutil.NopCloser(bytes.NewBuffer(nil))} for _, keyHandler := range []simplerHandler{getKeyHandler, rotateKeyHandler} { - for _, key := range []string{"imageName", "tufRole"} { + for _, key := range []string{"gun", "tufRole"} { vars := map[string]string{ - "imageName": "gun", - "tufRole": data.CanonicalTimestampRole.String(), + "gun": "gun", + "tufRole": data.CanonicalTimestampRole.String(), } // not provided @@ -154,8 +154,8 @@ func TestKeyHandlersInvalidRole(t *testing.T) { for _, keyHandler := range []simplerHandler{getKeyHandler, rotateKeyHandler} { for _, role := range []string{data.CanonicalRootRole.String(), data.CanonicalTargetsRole.String(), "targets/a", "invalidrole"} { vars := map[string]string{ - "imageName": "gun", - "tufRole": role, + "gun": "gun", + "tufRole": role, } req := &http.Request{Body: ioutil.NopCloser(bytes.NewBuffer(nil))} @@ -173,7 +173,7 @@ func TestGetKeyHandlerCreatesOnce(t *testing.T) { req := &http.Request{Body: ioutil.NopCloser(bytes.NewBuffer(nil))} for _, role := range roles { - vars := map[string]string{"imageName": "gun", "tufRole": role} + vars := map[string]string{"gun": "gun", "tufRole": role} recorder := httptest.NewRecorder() err := getKeyHandler(getContext(state), recorder, req, vars) require.NoError(t, err) @@ -187,7 +187,7 @@ func TestKeyHandlersInvalidKeyAlgo(t *testing.T) { req := &http.Request{Body: ioutil.NopCloser(bytes.NewBuffer(nil))} for _, keyHandler := range []simplerHandler{getKeyHandler, rotateKeyHandler} { for _, role := range roles { - vars := map[string]string{"imageName": "gun", "tufRole": role} + vars := map[string]string{"gun": "gun", "tufRole": role} recorder := httptest.NewRecorder() invalidKeyAlgoState := defaultState() invalidKeyAlgoState.keyAlgo = "notactuallyakeyalgorithm" @@ -204,7 +204,7 @@ func TestRotateKeyHandlerSuccessfulRotation(t *testing.T) { req := &http.Request{Body: ioutil.NopCloser(bytes.NewBuffer(nil))} for _, role := range roles { - vars := map[string]string{"imageName": "gun", "tufRole": role} + vars := map[string]string{"gun": "gun", "tufRole": role} recorder := httptest.NewRecorder() err := rotateKeyHandler(getContext(state), recorder, req, vars) require.NoError(t, err) @@ -231,8 +231,8 @@ func TestGetHandlerRoot(t *testing.T) { } vars := map[string]string{ - "imageName": "gun", - "tufRole": "root", + "gun": "gun", + "tufRole": "root", } rw := httptest.NewRecorder() @@ -275,8 +275,8 @@ func TestGetHandlerTimestamp(t *testing.T) { } vars := map[string]string{ - "imageName": "gun", - "tufRole": "timestamp", + "gun": "gun", + "tufRole": "timestamp", } rw := httptest.NewRecorder() @@ -312,8 +312,8 @@ func TestGetHandlerSnapshot(t *testing.T) { } vars := map[string]string{ - "imageName": "gun", - "tufRole": "snapshot", + "gun": "gun", + "tufRole": "snapshot", } rw := httptest.NewRecorder() @@ -333,8 +333,8 @@ func TestGetHandler404(t *testing.T) { } vars := map[string]string{ - "imageName": "gun", - "tufRole": "root", + "gun": "gun", + "tufRole": "root", } rw := httptest.NewRecorder() @@ -355,8 +355,8 @@ func TestGetHandlerNilData(t *testing.T) { } vars := map[string]string{ - "imageName": "gun", - "tufRole": "root", + "gun": "gun", + "tufRole": "root", } rw := httptest.NewRecorder() @@ -382,7 +382,7 @@ func TestGetHandlerNoStorage(t *testing.T) { func TestAtomicUpdateValidationFailurePropagated(t *testing.T) { metaStore := storage.NewMemStorage() var gun data.GUN = "testGUN" - vars := map[string]string{"imageName": gun.String()} + vars := map[string]string{"gun": gun.String()} repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) @@ -425,7 +425,7 @@ func (s *failStore) GetCurrent(_ data.GUN, _ data.RoleName) (*time.Time, []byte, func TestAtomicUpdateNonValidationFailureNotPropagated(t *testing.T) { metaStore := storage.NewMemStorage() var gun data.GUN = "testGUN" - vars := map[string]string{"imageName": gun.String()} + vars := map[string]string{"gun": gun.String()} repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) @@ -467,7 +467,7 @@ func (s *invalidVersionStore) UpdateMany(_ data.GUN, _ []storage.MetaUpdate) err func TestAtomicUpdateVersionErrorPropagated(t *testing.T) { metaStore := storage.NewMemStorage() var gun data.GUN = "testGUN" - vars := map[string]string{"imageName": gun.String()} + vars := map[string]string{"gun": gun.String()} repo, cs, err := testutils.EmptyRepo(gun) require.NoError(t, err) diff --git a/server/server.go b/server/server.go index e7c7d4146..572c37296 100644 --- a/server/server.go +++ b/server/server.go @@ -98,15 +98,15 @@ func filterImagePrefixes(requiredPrefixes []string, err error, handler http.Hand } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - imageName := mux.Vars(r)["imageName"] + gun := mux.Vars(r)["gun"] - if imageName == "" { + if gun == "" { handler.ServeHTTP(w, r) return } for _, prefix := range requiredPrefixes { - if strings.HasPrefix(imageName, prefix) { + if strings.HasPrefix(gun, prefix) { handler.ServeHTTP(w, r) return } @@ -116,7 +116,7 @@ func filterImagePrefixes(requiredPrefixes []string, err error, handler http.Hand }) } -// EndpointConfig stores settings used to create a Notary handler +// EndpointConfig stores settings used to create a ServerHandler type EndpointConfig struct { OperationName string ServerHandler utils.ContextHandler @@ -151,7 +151,7 @@ func RootHandler(ctx context.Context, ac auth.AccessController, trust signed.Cry r := mux.NewRouter() r.Methods("GET").Path("/v2/").Handler(authWrapper(handlers.MainHandler)) - r.Methods("POST").Path("/v2/{imageName:[^*]+}/_trust/tuf/").Handler(CreateHandler(EndpointConfig{ + r.Methods("POST").Path("/v2/{gun:[^*]+}/_trust/tuf/").Handler(CreateHandler(EndpointConfig{ OperationName: "UpdateTUF", ErrorIfGUNInvalid: invalidGUNErr, ServerHandler: handlers.AtomicUpdateHandler, @@ -159,7 +159,7 @@ func RootHandler(ctx context.Context, ac auth.AccessController, trust signed.Cry AuthWrapper: authWrapper, RepoPrefixes: repoPrefixes, })) - r.Methods("GET").Path("/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.{checksum:[a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128}}.json").Handler(CreateHandler(EndpointConfig{ + r.Methods("GET").Path("/v2/{gun:[^*]+}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.{checksum:[a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128}}.json").Handler(CreateHandler(EndpointConfig{ OperationName: "GetRoleByHash", ErrorIfGUNInvalid: notFoundError, IncludeCacheHeaders: true, @@ -169,7 +169,7 @@ func RootHandler(ctx context.Context, ac auth.AccessController, trust signed.Cry AuthWrapper: authWrapper, RepoPrefixes: repoPrefixes, })) - r.Methods("GET").Path("/v2/{imageName:[^*]+}/_trust/tuf/{version:[1-9]*[0-9]+}.{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.json").Handler(CreateHandler(EndpointConfig{ + r.Methods("GET").Path("/v2/{gun:[^*]+}/_trust/tuf/{version:[1-9]*[0-9]+}.{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.json").Handler(CreateHandler(EndpointConfig{ OperationName: "GetRoleByVersion", ErrorIfGUNInvalid: notFoundError, IncludeCacheHeaders: true, @@ -179,7 +179,7 @@ func RootHandler(ctx context.Context, ac auth.AccessController, trust signed.Cry AuthWrapper: authWrapper, RepoPrefixes: repoPrefixes, })) - r.Methods("GET").Path("/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.json").Handler(CreateHandler(EndpointConfig{ + r.Methods("GET").Path("/v2/{gun:[^*]+}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.json").Handler(CreateHandler(EndpointConfig{ OperationName: "GetRole", ErrorIfGUNInvalid: notFoundError, IncludeCacheHeaders: true, @@ -190,7 +190,7 @@ func RootHandler(ctx context.Context, ac auth.AccessController, trust signed.Cry RepoPrefixes: repoPrefixes, })) r.Methods("GET").Path( - "/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:snapshot|timestamp}.key").Handler(CreateHandler(EndpointConfig{ + "/v2/{gun:[^*]+}/_trust/tuf/{tufRole:snapshot|timestamp}.key").Handler(CreateHandler(EndpointConfig{ OperationName: "GetKey", ErrorIfGUNInvalid: notFoundError, ServerHandler: handlers.GetKeyHandler, @@ -199,7 +199,7 @@ func RootHandler(ctx context.Context, ac auth.AccessController, trust signed.Cry RepoPrefixes: repoPrefixes, })) r.Methods("POST").Path( - "/v2/{imageName:[^*]+}/_trust/tuf/{tufRole:snapshot|timestamp}.key").Handler(CreateHandler(EndpointConfig{ + "/v2/{gun:[^*]+}/_trust/tuf/{tufRole:snapshot|timestamp}.key").Handler(CreateHandler(EndpointConfig{ OperationName: "RotateKey", ErrorIfGUNInvalid: notFoundError, ServerHandler: handlers.RotateKeyHandler, @@ -207,7 +207,7 @@ func RootHandler(ctx context.Context, ac auth.AccessController, trust signed.Cry AuthWrapper: authWrapper, RepoPrefixes: repoPrefixes, })) - r.Methods("DELETE").Path("/v2/{imageName:[^*]+}/_trust/tuf/").Handler(CreateHandler(EndpointConfig{ + r.Methods("DELETE").Path("/v2/{gun:[^*]+}/_trust/tuf/").Handler(CreateHandler(EndpointConfig{ OperationName: "DeleteTUF", ErrorIfGUNInvalid: notFoundError, ServerHandler: handlers.DeleteHandler, @@ -215,7 +215,7 @@ func RootHandler(ctx context.Context, ac auth.AccessController, trust signed.Cry AuthWrapper: authWrapper, RepoPrefixes: repoPrefixes, })) - r.Methods("GET").Path("/v2/{imageName:[^*]+}/_trust/changefeed").Handler(CreateHandler(EndpointConfig{ + r.Methods("GET").Path("/v2/{gun:[^*]+}/_trust/changefeed").Handler(CreateHandler(EndpointConfig{ OperationName: "Changefeed", ErrorIfGUNInvalid: notFoundError, ServerHandler: handlers.Changefeed, diff --git a/utils/http.go b/utils/http.go index d02d8fa8f..f9e5fc0be 100644 --- a/utils/http.go +++ b/utils/http.go @@ -65,8 +65,8 @@ func (root *rootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { }() if root.auth != nil { - ctx = context.WithValue(ctx, notary.CtxKeyRepo, vars["imageName"]) - if ctx, err = root.doAuth(ctx, vars["imageName"], w); err != nil { + ctx = context.WithValue(ctx, notary.CtxKeyRepo, vars["gun"]) + if ctx, err = root.doAuth(ctx, vars["gun"], w); err != nil { // errors have already been logged/output to w inside doAuth // just return return @@ -95,12 +95,12 @@ func serveError(log ctxu.Logger, w http.ResponseWriter, err error) { return } -func (root *rootHandler) doAuth(ctx context.Context, imageName string, w http.ResponseWriter) (context.Context, error) { +func (root *rootHandler) doAuth(ctx context.Context, gun string, w http.ResponseWriter) (context.Context, error) { var access []auth.Access - if imageName == "" { + if gun == "" { access = buildCatalogRecord(root.actions...) } else { - access = buildAccessRecords(imageName, root.actions...) + access = buildAccessRecords(gun, root.actions...) } log := ctxu.GetRequestLogger(ctx) From fe6bcf21362849853876b3993f055c5a524e37ba Mon Sep 17 00:00:00 2001 From: Evan Cordell Date: Fri, 17 Feb 2017 18:16:19 -0500 Subject: [PATCH 3/6] revert table names to string constants Signed-off-by: Evan Cordell --- server/storage/sql_models.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/server/storage/sql_models.go b/server/storage/sql_models.go index 7e1bcce58..4e891099e 100644 --- a/server/storage/sql_models.go +++ b/server/storage/sql_models.go @@ -12,14 +12,10 @@ const ( ) // TUFFileTableName returns the name used for the tuf file table -var TUFFileTableName = func() string { - return "tuf_files" -} +var TUFFileTableName = "tuf_files" // ChangefeedTableName returns the name used for the changefeed table -var ChangefeedTableName = func() string { - return "changefeed" -} +var ChangefeedTableName = "changefeed" // TUFFile represents a TUF file in the database type TUFFile struct { @@ -33,7 +29,7 @@ type TUFFile struct { // TableName sets a specific table name for TUFFile func (g TUFFile) TableName() string { - return TUFFileTableName() + return TUFFileTableName } // Change defines the the fields required for an object in the changefeed @@ -48,7 +44,7 @@ type Change struct { // TableName sets a specific table name for Changefeed func (c Change) TableName() string { - return ChangefeedTableName() + return ChangefeedTableName } // CreateTUFTable creates the DB table for TUFFile From f86baa5c3c95ae178bb13f6934ac61666727c9d6 Mon Sep 17 00:00:00 2001 From: Evan Cordell Date: Fri, 17 Feb 2017 18:58:28 -0500 Subject: [PATCH 4/6] get rid of EndpointConfig Signed-off-by: Evan Cordell --- server/server.go | 179 +++++++++++++++++++++++++---------------------- 1 file changed, 96 insertions(+), 83 deletions(-) diff --git a/server/server.go b/server/server.go index 572c37296..3a5505f04 100644 --- a/server/server.go +++ b/server/server.go @@ -129,14 +129,14 @@ type EndpointConfig struct { } // CreateHandler creates a server handler from an EndpointConfig -func CreateHandler(opts EndpointConfig) http.Handler { +func CreateHandler(operationName string, serverHandler utils.ContextHandler, errorIfGUNInvalid error, includeCacheHeaders bool, cacheControlConfig utils.CacheControlConfig, permissionsRequired []string, authWrapper utils.AuthWrapper, repoPrefixes []string) http.Handler { var wrapped http.Handler - wrapped = opts.AuthWrapper(opts.ServerHandler, opts.PermissionsRequired...) - if opts.IncludeCacheHeaders { - wrapped = utils.WrapWithCacheHandler(opts.CacheControlConfig, wrapped) + wrapped = authWrapper(serverHandler, permissionsRequired...) + if includeCacheHeaders { + wrapped = utils.WrapWithCacheHandler(cacheControlConfig, wrapped) } - wrapped = filterImagePrefixes(opts.RepoPrefixes, opts.ErrorIfGUNInvalid, wrapped) - return prometheus.InstrumentHandlerWithOpts(prometheusOpts(opts.OperationName), wrapped) + wrapped = filterImagePrefixes(repoPrefixes, errorIfGUNInvalid, wrapped) + return prometheus.InstrumentHandlerWithOpts(prometheusOpts(operationName), wrapped) } // RootHandler returns the handler that routes all the paths from / for the @@ -151,85 +151,98 @@ func RootHandler(ctx context.Context, ac auth.AccessController, trust signed.Cry r := mux.NewRouter() r.Methods("GET").Path("/v2/").Handler(authWrapper(handlers.MainHandler)) - r.Methods("POST").Path("/v2/{gun:[^*]+}/_trust/tuf/").Handler(CreateHandler(EndpointConfig{ - OperationName: "UpdateTUF", - ErrorIfGUNInvalid: invalidGUNErr, - ServerHandler: handlers.AtomicUpdateHandler, - PermissionsRequired: []string{"push", "pull"}, - AuthWrapper: authWrapper, - RepoPrefixes: repoPrefixes, - })) - r.Methods("GET").Path("/v2/{gun:[^*]+}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.{checksum:[a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128}}.json").Handler(CreateHandler(EndpointConfig{ - OperationName: "GetRoleByHash", - ErrorIfGUNInvalid: notFoundError, - IncludeCacheHeaders: true, - CacheControlConfig: consistent, - ServerHandler: handlers.GetHandler, - PermissionsRequired: []string{"pull"}, - AuthWrapper: authWrapper, - RepoPrefixes: repoPrefixes, - })) - r.Methods("GET").Path("/v2/{gun:[^*]+}/_trust/tuf/{version:[1-9]*[0-9]+}.{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.json").Handler(CreateHandler(EndpointConfig{ - OperationName: "GetRoleByVersion", - ErrorIfGUNInvalid: notFoundError, - IncludeCacheHeaders: true, - CacheControlConfig: consistent, - ServerHandler: handlers.GetHandler, - PermissionsRequired: []string{"pull"}, - AuthWrapper: authWrapper, - RepoPrefixes: repoPrefixes, - })) - r.Methods("GET").Path("/v2/{gun:[^*]+}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.json").Handler(CreateHandler(EndpointConfig{ - OperationName: "GetRole", - ErrorIfGUNInvalid: notFoundError, - IncludeCacheHeaders: true, - CacheControlConfig: current, - ServerHandler: handlers.GetHandler, - PermissionsRequired: []string{"pull"}, - AuthWrapper: authWrapper, - RepoPrefixes: repoPrefixes, - })) + r.Methods("POST").Path("/v2/{gun:[^*]+}/_trust/tuf/").Handler(CreateHandler( + "UpdateTUF", + handlers.AtomicUpdateHandler, + invalidGUNErr, + false, + nil, + []string{"push", "pull"}, + authWrapper, + repoPrefixes, + )) + r.Methods("GET").Path("/v2/{gun:[^*]+}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.{checksum:[a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128}}.json").Handler(CreateHandler( + "GetRoleByHash", + handlers.GetHandler, + notFoundError, + true, + consistent, + []string{"pull"}, + authWrapper, + repoPrefixes, + )) + r.Methods("GET").Path("/v2/{gun:[^*]+}/_trust/tuf/{version:[1-9]*[0-9]+}.{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.json").Handler(CreateHandler( + "GetRoleByVersion", + handlers.GetHandler, + notFoundError, + true, + consistent, + []string{"pull"}, + authWrapper, + repoPrefixes, + )) + r.Methods("GET").Path("/v2/{gun:[^*]+}/_trust/tuf/{tufRole:root|targets(?:/[^/\\s]+)*|snapshot|timestamp}.json").Handler(CreateHandler( + "GetRole", + handlers.GetHandler, + notFoundError, + true, + current, + []string{"pull"}, + authWrapper, + repoPrefixes, + )) r.Methods("GET").Path( - "/v2/{gun:[^*]+}/_trust/tuf/{tufRole:snapshot|timestamp}.key").Handler(CreateHandler(EndpointConfig{ - OperationName: "GetKey", - ErrorIfGUNInvalid: notFoundError, - ServerHandler: handlers.GetKeyHandler, - PermissionsRequired: []string{"push", "pull"}, - AuthWrapper: authWrapper, - RepoPrefixes: repoPrefixes, - })) + "/v2/{gun:[^*]+}/_trust/tuf/{tufRole:snapshot|timestamp}.key").Handler(CreateHandler( + "GetKey", + handlers.GetKeyHandler, + notFoundError, + false, + nil, + []string{"push", "pull"}, + authWrapper, + repoPrefixes, + )) r.Methods("POST").Path( - "/v2/{gun:[^*]+}/_trust/tuf/{tufRole:snapshot|timestamp}.key").Handler(CreateHandler(EndpointConfig{ - OperationName: "RotateKey", - ErrorIfGUNInvalid: notFoundError, - ServerHandler: handlers.RotateKeyHandler, - PermissionsRequired: []string{"*"}, - AuthWrapper: authWrapper, - RepoPrefixes: repoPrefixes, - })) - r.Methods("DELETE").Path("/v2/{gun:[^*]+}/_trust/tuf/").Handler(CreateHandler(EndpointConfig{ - OperationName: "DeleteTUF", - ErrorIfGUNInvalid: notFoundError, - ServerHandler: handlers.DeleteHandler, - PermissionsRequired: []string{"*"}, - AuthWrapper: authWrapper, - RepoPrefixes: repoPrefixes, - })) - r.Methods("GET").Path("/v2/{gun:[^*]+}/_trust/changefeed").Handler(CreateHandler(EndpointConfig{ - OperationName: "Changefeed", - ErrorIfGUNInvalid: notFoundError, - ServerHandler: handlers.Changefeed, - PermissionsRequired: []string{"pull"}, - AuthWrapper: authWrapper, - RepoPrefixes: repoPrefixes, - })) - r.Methods("GET").Path("/v2/_trust/changefeed").Handler(CreateHandler(EndpointConfig{ - OperationName: "Changefeed", - ServerHandler: handlers.Changefeed, - PermissionsRequired: []string{"*"}, - AuthWrapper: authWrapper, - RepoPrefixes: repoPrefixes, - })) + "/v2/{gun:[^*]+}/_trust/tuf/{tufRole:snapshot|timestamp}.key").Handler(CreateHandler( + "RotateKey", + handlers.RotateKeyHandler, + notFoundError, + false, + nil, + []string{"*"}, + authWrapper, + repoPrefixes, + )) + r.Methods("DELETE").Path("/v2/{gun:[^*]+}/_trust/tuf/").Handler(CreateHandler( + "DeleteTUF", + handlers.DeleteHandler, + notFoundError, + false, + nil, + []string{"*"}, + authWrapper, + repoPrefixes, + )) + r.Methods("GET").Path("/v2/{gun:[^*]+}/_trust/changefeed").Handler(CreateHandler( + "Changefeed", + handlers.Changefeed, + notFoundError, + false, + nil, + []string{"pull"}, + authWrapper, + repoPrefixes, + )) + r.Methods("GET").Path("/v2/_trust/changefeed").Handler(CreateHandler( + "Changefeed", + handlers.Changefeed, + notFoundError, + false, + nil, + []string{"*"}, + authWrapper, + repoPrefixes, + )) r.Methods("GET").Path("/_notary_server/health").HandlerFunc(health.StatusHandler) r.Methods("GET").Path("/metrics").Handler(prometheus.Handler()) r.Methods("GET", "POST", "PUT", "HEAD", "DELETE").Path("/{other:.*}").Handler( From 0b042e93828956ae6385c685f6da29065579c802 Mon Sep 17 00:00:00 2001 From: Evan Cordell Date: Wed, 22 Feb 2017 13:41:36 -0500 Subject: [PATCH 5/6] remove unused EndpointConfig Signed-off-by: Evan Cordell --- server/server.go | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/server/server.go b/server/server.go index 3a5505f04..f4075412e 100644 --- a/server/server.go +++ b/server/server.go @@ -116,19 +116,7 @@ func filterImagePrefixes(requiredPrefixes []string, err error, handler http.Hand }) } -// EndpointConfig stores settings used to create a ServerHandler -type EndpointConfig struct { - OperationName string - ServerHandler utils.ContextHandler - ErrorIfGUNInvalid error - IncludeCacheHeaders bool - CacheControlConfig utils.CacheControlConfig - PermissionsRequired []string - AuthWrapper utils.AuthWrapper - RepoPrefixes []string -} - -// CreateHandler creates a server handler from an EndpointConfig +// CreateHandler creates a server handler, wrapping with auth, caching, and monitoring func CreateHandler(operationName string, serverHandler utils.ContextHandler, errorIfGUNInvalid error, includeCacheHeaders bool, cacheControlConfig utils.CacheControlConfig, permissionsRequired []string, authWrapper utils.AuthWrapper, repoPrefixes []string) http.Handler { var wrapped http.Handler wrapped = authWrapper(serverHandler, permissionsRequired...) From e7749ff708edd4ab047c79cda9cec00c4327dc1b Mon Sep 17 00:00:00 2001 From: Evan Cordell Date: Thu, 23 Feb 2017 12:56:32 -0500 Subject: [PATCH 6/6] const table names Signed-off-by: Evan Cordell --- server/storage/sql_models.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/storage/sql_models.go b/server/storage/sql_models.go index 4e891099e..5dfb62e9c 100644 --- a/server/storage/sql_models.go +++ b/server/storage/sql_models.go @@ -12,10 +12,10 @@ const ( ) // TUFFileTableName returns the name used for the tuf file table -var TUFFileTableName = "tuf_files" +const TUFFileTableName = "tuf_files" // ChangefeedTableName returns the name used for the changefeed table -var ChangefeedTableName = "changefeed" +const ChangefeedTableName = "changefeed" // TUFFile represents a TUF file in the database type TUFFile struct {