From ff806fb4926948df9de90ff264801b86fd7c9f09 Mon Sep 17 00:00:00 2001 From: Johannes Batzill Date: Mon, 5 Sep 2022 18:45:16 -0700 Subject: [PATCH] block 'api' as root space name, minor improvements --- internal/api/guard/guard.go | 6 +- internal/api/guard/guard.repo.go | 4 +- internal/api/guard/guard.space.go | 5 +- internal/api/handler/repo/create.go | 2 +- internal/api/handler/space/create.go | 4 +- internal/api/handler/space/delete.go | 1 - internal/api/handler/space/list.go | 6 +- internal/api/handler/space/listRepos.go | 6 +- internal/api/middleware/authn/authn.go | 6 +- internal/api/middleware/encode/encode.go | 14 +- internal/api/request/context.go | 2 +- internal/api/request/util.go | 4 +- internal/auth/authn/token.go | 6 - internal/router/api.go | 12 +- internal/router/git.go | 20 +- internal/router/router.go | 9 +- internal/router/web.go | 2 +- internal/store/database/repo.go | 2 +- internal/store/database/space.go | 4 +- internal/store/database/testdata/repos.json | 36 +++ internal/store/database/testdata/spaces.json | 26 ++ internal/store/database/testdata/users.json | 4 +- mocks/mock.go | 2 +- mocks/mock_store.go | 252 ++++++++++++++++++- types/check/repo.go | 2 +- types/check/space.go | 15 +- 26 files changed, 389 insertions(+), 63 deletions(-) create mode 100644 internal/store/database/testdata/repos.json create mode 100644 internal/store/database/testdata/spaces.json diff --git a/internal/api/guard/guard.go b/internal/api/guard/guard.go index d6c75dcc9..b42c1e1d8 100644 --- a/internal/api/guard/guard.go +++ b/internal/api/guard/guard.go @@ -12,18 +12,16 @@ import ( "github.com/harness/gitness/internal/api/render" "github.com/harness/gitness/internal/api/request" "github.com/harness/gitness/internal/auth/authz" - "github.com/harness/gitness/internal/store" "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" ) type Guard struct { authorizer authz.Authorizer - spaces store.SpaceStore } -func New(spaces store.SpaceStore, authorizer authz.Authorizer) *Guard { - return &Guard{authorizer: authorizer, spaces: spaces} +func New(authorizer authz.Authorizer) *Guard { + return &Guard{authorizer: authorizer} } /* diff --git a/internal/api/guard/guard.repo.go b/internal/api/guard/guard.repo.go index ff05eae08..134072d9b 100644 --- a/internal/api/guard/guard.repo.go +++ b/internal/api/guard/guard.repo.go @@ -42,8 +42,8 @@ func (g *Guard) Repo(permission enum.Permission, orPublic bool, guarded http.Han return } - // Enforce permission - if (!orPublic || !rep.IsPublic) && !g.EnforceRepo(w, r, permission, rep.Fqn) { + // Enforce permission (renders error) + if !(orPublic && rep.IsPublic) && !g.EnforceRepo(w, r, permission, rep.Fqn) { return } diff --git a/internal/api/guard/guard.space.go b/internal/api/guard/guard.space.go index e9360913c..568265d8e 100644 --- a/internal/api/guard/guard.space.go +++ b/internal/api/guard/guard.space.go @@ -6,7 +6,6 @@ package guard import ( "errors" - "fmt" "net/http" "github.com/harness/gitness/internal/api/render" @@ -43,10 +42,8 @@ func (g *Guard) Space(permission enum.Permission, orPublic bool, guarded http.Ha return } - fmt.Printf("check for space '%s'", s.Fqn) - // Enforce permission (renders error) - if (!orPublic || !s.IsPublic) && !g.EnforceSpace(w, r, permission, s.Fqn) { + if !(orPublic && s.IsPublic) && !g.EnforceSpace(w, r, permission, s.Fqn) { return } diff --git a/internal/api/handler/repo/create.go b/internal/api/handler/repo/create.go index 6698fcb8a..9da7504ff 100644 --- a/internal/api/handler/repo/create.go +++ b/internal/api/handler/repo/create.go @@ -72,7 +72,7 @@ func HandleCreate(guard *guard.Guard, spaces store.SpaceStore, repos store.RepoS /* * AUTHORIZATION - has to be done on parent space! */ - if !guard.EnforceSpace(w, r, enum.PermissionRepoCreate, parentFqn) { + if !guard.EnforceRepo(w, r, enum.PermissionRepoCreate, parentFqn) { return } diff --git a/internal/api/handler/space/create.go b/internal/api/handler/space/create.go index 9b3da3a74..103bf6717 100644 --- a/internal/api/handler/space/create.go +++ b/internal/api/handler/space/create.go @@ -35,7 +35,7 @@ func HandleCreate(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc ctx := r.Context() log := hlog.FromRequest(r) - // get fqn (requires parent if child space) + // get fqn (requires parent of child space) sref, err := request.GetSpaceRef(r) if err != nil { render.BadRequest(w, err) @@ -81,7 +81,7 @@ func HandleCreate(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc } // get parentId if needed - parentId := int64(-1) + parentId := int64(0) if parentFqn != "" { parentSpace, err := spaces.FindFqn(ctx, parentFqn) if err != nil { diff --git a/internal/api/handler/space/delete.go b/internal/api/handler/space/delete.go index fb0b86806..a5eb089fa 100644 --- a/internal/api/handler/space/delete.go +++ b/internal/api/handler/space/delete.go @@ -32,7 +32,6 @@ func HandleDelete(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc if err != nil { render.InternalError(w, err) log.Error().Err(err). - Int64("space_id", s.ID). Str("space_fqn", s.Fqn). Msg("Failed to delete space.") return diff --git a/internal/api/handler/space/list.go b/internal/api/handler/space/list.go index 348ad8843..2fec52ead 100644 --- a/internal/api/handler/space/list.go +++ b/internal/api/handler/space/list.go @@ -36,7 +36,8 @@ func HandleList(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc { if err != nil { render.InternalError(w, err) log.Error().Err(err). - Msgf("Failed to retrieve count of spaces under '%s'.", s.Fqn) + Str("space_fqn", s.Fqn). + Msg("Failed to retrieve count of child spaces.") return } @@ -44,7 +45,8 @@ func HandleList(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc { if err != nil { render.InternalError(w, err) log.Error().Err(err). - Msgf("Failed to retrieve list of spaces under '%s'.", s.Fqn) + Str("space_fqn", s.Fqn). + Msg("Failed to retrieve list of child spaces.") return } diff --git a/internal/api/handler/space/listRepos.go b/internal/api/handler/space/listRepos.go index 4de535605..94611cc01 100644 --- a/internal/api/handler/space/listRepos.go +++ b/internal/api/handler/space/listRepos.go @@ -36,7 +36,8 @@ func HandleListRepos(guard *guard.Guard, repos store.RepoStore) http.HandlerFunc if err != nil { render.InternalError(w, err) log.Error().Err(err). - Msgf("Failed to retrieve count of repos under '%s'.", s.Fqn) + Str("space_fqn", s.Fqn). + Msg("Failed to retrieve count of repos.") return } @@ -44,7 +45,8 @@ func HandleListRepos(guard *guard.Guard, repos store.RepoStore) http.HandlerFunc if err != nil { render.InternalError(w, err) log.Error().Err(err). - Msgf("Failed to retrieve list of repos under '%s'.", s.Fqn) + Str("space_fqn", s.Fqn). + Msg("Failed to retrieve list of repos.") return } diff --git a/internal/api/middleware/authn/authn.go b/internal/api/middleware/authn/authn.go index e47363a49..a451a48d8 100644 --- a/internal/api/middleware/authn/authn.go +++ b/internal/api/middleware/authn/authn.go @@ -26,11 +26,15 @@ func Attempt(authenticator authn.Authenticator) func(http.Handler) http.Handler if err != nil { render.Unauthorized(w, err) return - } else if user == nil { + } + + // if there was no auth info - continue as is + if user == nil { next.ServeHTTP(w, r) return } + // otherwise update the logging context and inject user in context ctx := r.Context() log.Ctx(ctx).UpdateContext(func(c zerolog.Context) zerolog.Context { return c.Str("session_email", user.Email).Bool("session_admin", user.Admin) diff --git a/internal/api/middleware/encode/encode.go b/internal/api/middleware/encode/encode.go index 2a66ce4f0..6e3b65632 100644 --- a/internal/api/middleware/encode/encode.go +++ b/internal/api/middleware/encode/encode.go @@ -14,7 +14,7 @@ import ( */ func GitFqnBefore(h http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - r, _ = encodeFQNWithCustomMarker(r, "", ".git", false) + r, _ = encodeFQNWithMarker(r, "", ".git", false) h.ServeHTTP(w, r) } } @@ -27,8 +27,9 @@ func GitFqnBefore(h http.HandlerFunc) http.HandlerFunc { func TerminatedFqnBefore(prefixes []string, h http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { for _, p := range prefixes { + // IMPORTANT: define changed separately to avoid overshadowing r changed := false - if r, changed = encodeFQNWithCustomMarker(r, p, "/+", false); changed { + if r, changed = encodeFQNWithMarker(r, p, "/+", false); changed { break } } @@ -44,10 +45,10 @@ func TerminatedFqnBefore(prefixes []string, h http.HandlerFunc) http.HandlerFunc * * Examples: * Prefix: "" Path: "/space1/space2/+" => "/space1%2Fspace2" - * Prefix: "" Path: "/space1/space2.git" => "/space1%2Fspace2.git" + * Prefix: "" Path: "/space1/space2.git" => "/space1%2Fspace2" * Prefix: "/spaces" Path: "/spaces/space1/space2/+/authToken" => "/spaces/space1%2Fspace2/authToken" */ -func encodeFQNWithCustomMarker(r *http.Request, prefix string, marker string, keepMarker bool) (*http.Request, bool) { +func encodeFQNWithMarker(r *http.Request, prefix string, marker string, keepMarker bool) (*http.Request, bool) { // In case path doesn't start with prefix - nothing to encode if len(r.URL.Path) < len(prefix) || r.URL.Path[0:len(prefix)] != prefix { return r, false @@ -58,11 +59,12 @@ func encodeFQNWithCustomMarker(r *http.Request, prefix string, marker string, ke // If we don't find a marker - nothing to encode if !found { + fmt.Println("what") return r, false } - // if marker was found - convert to escaped version - escapedFqn := "/" + strings.Replace(fqn[1:], "/", "%2F", -1) + // if marker was found - convert to escaped version (skip first character in case path starts with '/') + escapedFqn := fqn[0:1] + strings.Replace(fqn[1:], "/", "%2F", -1) if keepMarker { escapedFqn += marker } diff --git a/internal/api/request/context.go b/internal/api/request/context.go index 9c7ca36e7..5912e7485 100644 --- a/internal/api/request/context.go +++ b/internal/api/request/context.go @@ -31,7 +31,7 @@ func WithUser(parent context.Context, v *types.User) context.Context { // context. func UserFrom(ctx context.Context) (*types.User, bool) { v, ok := ctx.Value(userKey).(*types.User) - return v, ok + return v, ok && v != nil } // WithSpace returns a copy of parent in which the space value is set diff --git a/internal/api/request/util.go b/internal/api/request/util.go index 2edb1cf7f..ca902b4d0 100644 --- a/internal/api/request/util.go +++ b/internal/api/request/util.go @@ -87,7 +87,7 @@ func ParseUserFilter(r *http.Request) types.UserFilter { } } -// ParseSpaceFilter extracts the user query parameter from the url. +// ParseSpaceFilter extracts the space query parameter from the url. func ParseSpaceFilter(r *http.Request) types.SpaceFilter { return types.SpaceFilter{ Order: ParseOrder(r), @@ -97,7 +97,7 @@ func ParseSpaceFilter(r *http.Request) types.SpaceFilter { } } -// ParseRepoFilter extracts the user query parameter from the url. +// ParseRepoFilter extracts the repository query parameter from the url. func ParseRepoFilter(r *http.Request) types.RepoFilter { return types.RepoFilter{ Order: ParseOrder(r), diff --git a/internal/auth/authn/token.go b/internal/auth/authn/token.go index 9a7f35053..6b4abbc41 100644 --- a/internal/auth/authn/token.go +++ b/internal/auth/authn/token.go @@ -16,9 +16,7 @@ import ( "github.com/harness/gitness/internal/store" "github.com/harness/gitness/internal/token" "github.com/harness/gitness/types" - "github.com/rs/zerolog" "github.com/rs/zerolog/hlog" - "github.com/rs/zerolog/log" ) var _ Authenticator = (*TokenAuthenticator)(nil) @@ -81,10 +79,6 @@ func (a *TokenAuthenticator) Authenticate(r *http.Request) (*types.User, error) } } - log.Ctx(ctx).UpdateContext(func(c zerolog.Context) zerolog.Context { - return c.Str("session_email", user.Email).Bool("session_admin", user.Admin) - }) - return user, nil } diff --git a/internal/router/api.go b/internal/router/api.go index 0a5b8caa1..04f875930 100644 --- a/internal/router/api.go +++ b/internal/router/api.go @@ -27,7 +27,7 @@ import ( ) /* - * Mounts the Rest API Router under mountPath. + * Mounts the Rest API Router under mountPath (path has to end with ). * The handler is wrapped within a layer that handles encoding terminated FQNs. */ func newApiHandler( @@ -40,8 +40,9 @@ func newApiHandler( authorizer authz.Authorizer) (http.Handler, error) { config := systemStore.Config(nocontext) + guard := guard.New(authorizer) - // User go-chi router for inner routing (restricted to mountPath!) + // Use go-chi router for inner routing (restricted to mountPath!) r := chi.NewRouter() r.Route(mountPath, func(r chi.Router) { @@ -72,7 +73,7 @@ func newApiHandler( r.Use(middleware_authn.Attempt(authenticator)) r.Route("/v1", func(r chi.Router) { - setupRoutesV1(r, systemStore, userStore, spaceStore, repoStore, authenticator, authorizer) + setupRoutesV1(r, systemStore, userStore, spaceStore, repoStore, authenticator, guard) }) }) @@ -92,10 +93,7 @@ func setupRoutesV1( spaceStore store.SpaceStore, repoStore store.RepoStore, authenticator authn.Authenticator, - authorizer authz.Authorizer) { - - // Create singleton middlewares for later usage - guard := guard.New(spaceStore, authorizer) + guard *guard.Guard) { // SPACES r.Route("/spaces", func(r chi.Router) { diff --git a/internal/router/git.go b/internal/router/git.go index 8343760c9..167c2e5d0 100644 --- a/internal/router/git.go +++ b/internal/router/git.go @@ -12,6 +12,7 @@ import ( "github.com/harness/gitness/internal/auth/authn" "github.com/harness/gitness/internal/auth/authz" "github.com/harness/gitness/internal/store" + "github.com/harness/gitness/types/enum" "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" @@ -32,13 +33,12 @@ func newGitHandler( authenticator authn.Authenticator, authorizer authz.Authorizer) (http.Handler, error) { - // User go-chi router for inner routing (restricted to mountPath!) + guard := guard.New(authorizer) + + // Use go-chi router for inner routing (restricted to mountPath!) r := chi.NewRouter() r.Route(mountPath, func(r chi.Router) { - // Create singleton middlewares for later usage - guard := guard.New(spaceStore, authorizer) - // Apply common api middleware r.Use(middleware.NoCache) r.Use(middleware.Recoverer) @@ -56,15 +56,19 @@ func newGitHandler( // resolves the repo and stores in the context r.Use(repo.Required(repoStore)) - // Operations that require auth (write) + // Write operations (need auth) r.Group(func(r chi.Router) { - r.Use(guard.EnforceAuthenticated) + // TODO: specific permission for pushing code? + r.Use(guard.ForRepo(enum.PermissionRepoEdit, false)) r.Handle("/git-upload-pack", http.HandlerFunc(stubGitHandler)) }) - // Operations that not always require auth (read) + // Read operations (only need of it not public) r.Group(func(r chi.Router) { + + r.Use(guard.ForRepo(enum.PermissionRepoView, true)) + r.Post("/git-receive-pack", stubGitHandler) r.Get("/info/refs", stubGitHandler) r.Get("/HEAD", stubGitHandler) @@ -88,7 +92,7 @@ func stubGitHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusForbidden) w.Write([]byte(fmt.Sprintf( - "Work in progress ... \n"+ + "Oooops, seems you hit a major construction site ... \n"+ " Repo: '%s' (%s)\n"+ " Method: '%s'\n"+ " Path: '%s'\n"+ diff --git a/internal/router/router.go b/internal/router/router.go index 2b803de5c..801188665 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -17,7 +17,8 @@ import ( ) const ( - restMount = "/api/" + restMount = "/api" + gitUserAgentPrefix = "git/" ) // empty context @@ -39,7 +40,7 @@ func New( authenticator authn.Authenticator, authorizer authz.Authorizer, ) (http.Handler, error) { - api, err := newApiHandler("/api", systemStore, userStore, spaceStore, repoStore, authenticator, authorizer) + api, err := newApiHandler(restMount, systemStore, userStore, spaceStore, repoStore, authenticator, authorizer) if err != nil { return nil, err } @@ -68,8 +69,8 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { * This can collide with other API endpoints and thus has to be checked first. * To avoid any false positives, we use the user-agent header to identify git agents. */ - a := req.Header.Get("user-agent") - if strings.HasPrefix(a, "git/") { + ua := req.Header.Get("user-agent") + if strings.HasPrefix(ua, gitUserAgentPrefix) { r.git.ServeHTTP(w, req) return } diff --git a/internal/router/web.go b/internal/router/web.go index b6f43a5bb..f4163848c 100644 --- a/internal/router/web.go +++ b/internal/router/web.go @@ -22,7 +22,7 @@ func newWebHandler( config := systemStore.Config(nocontext) - // User go-chi router for inner routing (restricted to mountPath!) + // Use go-chi router for inner routing (restricted to mountPath!) r := chi.NewRouter() r.Route(mountPath, func(r chi.Router) { diff --git a/internal/store/database/repo.go b/internal/store/database/repo.go index df17f46d0..53a9def87 100644 --- a/internal/store/database/repo.go +++ b/internal/store/database/repo.go @@ -199,7 +199,7 @@ INSERT INTO repositories ( ,:repo_numPulls ,:repo_numClosedPulls ,:repo_numOpenPulls - )RETURNING repo_id +) RETURNING repo_id ` const repoUpdate = ` diff --git a/internal/store/database/space.go b/internal/store/database/space.go index 936246bd1..004749c6d 100644 --- a/internal/store/database/space.go +++ b/internal/store/database/space.go @@ -176,7 +176,7 @@ WHERE space_id = $1 const spaceInsert = ` INSERT INTO spaces ( - space_name + space_name ,space_fqn ,space_parentId ,space_displayName @@ -195,7 +195,7 @@ INSERT INTO spaces ( ,:space_createdBy ,:space_created ,:space_updated - )RETURNING space_id +) RETURNING space_id ` const spaceUpdate = ` diff --git a/internal/store/database/testdata/repos.json b/internal/store/database/testdata/repos.json new file mode 100644 index 000000000..adca542f7 --- /dev/null +++ b/internal/store/database/testdata/repos.json @@ -0,0 +1,36 @@ +[ + { + "id": 1, + "name": "repo1", + "spaceId": 1, + "fqn": "space1/repo1", + "displayName": "Repository 1", + "description": "Some repository.", + "isPublic": true, + "createdBy": 1, + "created": 1662427496787, + "updated": 1662427496787, + "forkId": 0, + "numForks": 0, + "numPulls": 0, + "numClosedPulls": 0, + "numOpenPulls": 0 + }, + { + "id": 2, + "name": "repo2", + "spaceId": 2, + "fqn": "space1/space2/repo2", + "displayName": "Repository 2", + "description": "Some other repository.", + "isPublic": true, + "createdBy": 1, + "created": 1662427602488, + "updated": 1662427602488, + "forkId": 0, + "numForks": 0, + "numPulls": 0, + "numClosedPulls": 0, + "numOpenPulls": 0 + } +] \ No newline at end of file diff --git a/internal/store/database/testdata/spaces.json b/internal/store/database/testdata/spaces.json new file mode 100644 index 000000000..42e90ac67 --- /dev/null +++ b/internal/store/database/testdata/spaces.json @@ -0,0 +1,26 @@ +[ + { + "id": 1, + "name": "space1", + "fqn": "space1", + "parentId": 0, + "displayName": "Space 1", + "description": "Some space.", + "isPublic": true, + "createdBy": 1, + "created": 1662427417128, + "updated": 1662427417128 + }, + { + "id": 2, + "name": "space2", + "fqn": "space1/space2", + "parentId": 1, + "displayName": "Space 2", + "description": "Some subspace.", + "isPublic": true, + "createdBy": 1, + "created": 1662427428536, + "updated": 1662427428536 + } +] \ No newline at end of file diff --git a/internal/store/database/testdata/users.json b/internal/store/database/testdata/users.json index e2b20bd77..3a54c80b9 100644 --- a/internal/store/database/testdata/users.json +++ b/internal/store/database/testdata/users.json @@ -1,6 +1,6 @@ [ { - "id": 0, + "id": 1, "email": "jane@example.com", "name": "jane", "company": "acme", @@ -11,7 +11,7 @@ "authed": 0 }, { - "id": 0, + "id": 2, "email": "john@example.com", "name": "john", "company": "acme", diff --git a/mocks/mock.go b/mocks/mock.go index ac30ad271..a3c08c6cf 100644 --- a/mocks/mock.go +++ b/mocks/mock.go @@ -5,5 +5,5 @@ // Package mocks provides mock interfaces. package mocks -//go:generate mockgen -package=mocks -destination=mock_store.go github.com/harness/gitness/internal/store SystemStore,UserStore +//go:generate mockgen -package=mocks -destination=mock_store.go github.com/harness/gitness/internal/store SystemStore,UserStore,SpaceStore,RepoStore //go:generate mockgen -package=mocks -destination=mock_client.go github.com/harness/gitness/client Client diff --git a/mocks/mock_store.go b/mocks/mock_store.go index f6f624fe4..07e966202 100644 --- a/mocks/mock_store.go +++ b/mocks/mock_store.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/harness/gitness/internal/store (interfaces: SystemStore,UserStore) +// Source: github.com/harness/gitness/internal/store (interfaces: SystemStore,UserStore,SpaceStore,RepoStore) // Package mocks is a generated GoMock package. package mocks @@ -188,3 +188,253 @@ func (mr *MockUserStoreMockRecorder) Update(arg0, arg1 interface{}) *gomock.Call mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockUserStore)(nil).Update), arg0, arg1) } + +// MockSpaceStore is a mock of SpaceStore interface. +type MockSpaceStore struct { + ctrl *gomock.Controller + recorder *MockSpaceStoreMockRecorder +} + +// MockSpaceStoreMockRecorder is the mock recorder for MockSpaceStore. +type MockSpaceStoreMockRecorder struct { + mock *MockSpaceStore +} + +// NewMockSpaceStore creates a new mock instance. +func NewMockSpaceStore(ctrl *gomock.Controller) *MockSpaceStore { + mock := &MockSpaceStore{ctrl: ctrl} + mock.recorder = &MockSpaceStoreMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSpaceStore) EXPECT() *MockSpaceStoreMockRecorder { + return m.recorder +} + +// Count mocks base method. +func (m *MockSpaceStore) Count(arg0 context.Context, arg1 int64) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Count", arg0, arg1) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Count indicates an expected call of Count. +func (mr *MockSpaceStoreMockRecorder) Count(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Count", reflect.TypeOf((*MockSpaceStore)(nil).Count), arg0, arg1) +} + +// Create mocks base method. +func (m *MockSpaceStore) Create(arg0 context.Context, arg1 *types.Space) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create. +func (mr *MockSpaceStoreMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockSpaceStore)(nil).Create), arg0, arg1) +} + +// Delete mocks base method. +func (m *MockSpaceStore) Delete(arg0 context.Context, arg1 int64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockSpaceStoreMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockSpaceStore)(nil).Delete), arg0, arg1) +} + +// Find mocks base method. +func (m *MockSpaceStore) Find(arg0 context.Context, arg1 int64) (*types.Space, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Find", arg0, arg1) + ret0, _ := ret[0].(*types.Space) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Find indicates an expected call of Find. +func (mr *MockSpaceStoreMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockSpaceStore)(nil).Find), arg0, arg1) +} + +// FindFqn mocks base method. +func (m *MockSpaceStore) FindFqn(arg0 context.Context, arg1 string) (*types.Space, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindFqn", arg0, arg1) + ret0, _ := ret[0].(*types.Space) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindFqn indicates an expected call of FindFqn. +func (mr *MockSpaceStoreMockRecorder) FindFqn(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindFqn", reflect.TypeOf((*MockSpaceStore)(nil).FindFqn), arg0, arg1) +} + +// List mocks base method. +func (m *MockSpaceStore) List(arg0 context.Context, arg1 int64, arg2 types.SpaceFilter) ([]*types.Space, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", arg0, arg1, arg2) + ret0, _ := ret[0].([]*types.Space) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockSpaceStoreMockRecorder) List(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockSpaceStore)(nil).List), arg0, arg1, arg2) +} + +// Update mocks base method. +func (m *MockSpaceStore) Update(arg0 context.Context, arg1 *types.Space) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update. +func (mr *MockSpaceStoreMockRecorder) Update(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockSpaceStore)(nil).Update), arg0, arg1) +} + +// MockRepoStore is a mock of RepoStore interface. +type MockRepoStore struct { + ctrl *gomock.Controller + recorder *MockRepoStoreMockRecorder +} + +// MockRepoStoreMockRecorder is the mock recorder for MockRepoStore. +type MockRepoStoreMockRecorder struct { + mock *MockRepoStore +} + +// NewMockRepoStore creates a new mock instance. +func NewMockRepoStore(ctrl *gomock.Controller) *MockRepoStore { + mock := &MockRepoStore{ctrl: ctrl} + mock.recorder = &MockRepoStoreMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRepoStore) EXPECT() *MockRepoStoreMockRecorder { + return m.recorder +} + +// Count mocks base method. +func (m *MockRepoStore) Count(arg0 context.Context, arg1 int64) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Count", arg0, arg1) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Count indicates an expected call of Count. +func (mr *MockRepoStoreMockRecorder) Count(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Count", reflect.TypeOf((*MockRepoStore)(nil).Count), arg0, arg1) +} + +// Create mocks base method. +func (m *MockRepoStore) Create(arg0 context.Context, arg1 *types.Repository) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create. +func (mr *MockRepoStoreMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockRepoStore)(nil).Create), arg0, arg1) +} + +// Delete mocks base method. +func (m *MockRepoStore) Delete(arg0 context.Context, arg1 int64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockRepoStoreMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockRepoStore)(nil).Delete), arg0, arg1) +} + +// Find mocks base method. +func (m *MockRepoStore) Find(arg0 context.Context, arg1 int64) (*types.Repository, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Find", arg0, arg1) + ret0, _ := ret[0].(*types.Repository) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Find indicates an expected call of Find. +func (mr *MockRepoStoreMockRecorder) Find(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockRepoStore)(nil).Find), arg0, arg1) +} + +// FindFqn mocks base method. +func (m *MockRepoStore) FindFqn(arg0 context.Context, arg1 string) (*types.Repository, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindFqn", arg0, arg1) + ret0, _ := ret[0].(*types.Repository) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindFqn indicates an expected call of FindFqn. +func (mr *MockRepoStoreMockRecorder) FindFqn(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindFqn", reflect.TypeOf((*MockRepoStore)(nil).FindFqn), arg0, arg1) +} + +// List mocks base method. +func (m *MockRepoStore) List(arg0 context.Context, arg1 int64, arg2 types.RepoFilter) ([]*types.Repository, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", arg0, arg1, arg2) + ret0, _ := ret[0].([]*types.Repository) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockRepoStoreMockRecorder) List(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockRepoStore)(nil).List), arg0, arg1, arg2) +} + +// Update mocks base method. +func (m *MockRepoStore) Update(arg0 context.Context, arg1 *types.Repository) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update. +func (mr *MockRepoStoreMockRecorder) Update(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockRepoStore)(nil).Update), arg0, arg1) +} diff --git a/types/check/repo.go b/types/check/repo.go index 43567d2f1..2d8a388ae 100644 --- a/types/check/repo.go +++ b/types/check/repo.go @@ -26,7 +26,7 @@ var ( ErrRepoNameLength = errors.New(fmt.Sprintf("Repository name has to be between %d and %d in length.", minRepoNameLength, maxRepoNameLength)) ErrRepoNameRegex = errors.New("Repository name has start with a letter and only contain the following [a-z0-9-_].") - ErrRepoDisplayNameLength = errors.New(fmt.Sprintf("Repository name has to be between %d and %d in length.", minRepoDisplayNameLength, maxRepoDisplayNameLength)) + ErrRepoDisplayNameLength = errors.New(fmt.Sprintf("Repository display name has to be between %d and %d in length.", minRepoDisplayNameLength, maxRepoDisplayNameLength)) ErrRepoDisplayNameRegex = errors.New("Repository display name has start with a letter and only contain the following [a-zA-Z0-9-_ ].") ) diff --git a/types/check/space.go b/types/check/space.go index d4f0edbd5..c18cbb7ab 100644 --- a/types/check/space.go +++ b/types/check/space.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "regexp" + "strings" "github.com/harness/gitness/types" ) @@ -26,8 +27,11 @@ var ( ErrSpaceNameLength = errors.New(fmt.Sprintf("Space name has to be between %d and %d in length.", minSpaceNameLength, maxSpaceNameLength)) ErrSpaceNameRegex = errors.New("Space name has start with a letter and only contain the following [a-z0-9-_].") - ErrSpaceDisplayNameLength = errors.New(fmt.Sprintf("Space name has to be between %d and %d in length.", minSpaceDisplayNameLength, maxSpaceDisplayNameLength)) + ErrSpaceDisplayNameLength = errors.New(fmt.Sprintf("Space display name has to be between %d and %d in length.", minSpaceDisplayNameLength, maxSpaceDisplayNameLength)) ErrSpaceDisplayNameRegex = errors.New("Space display name has start with a letter and only contain the following [a-zA-Z0-9-_ ].") + + illegalRootSpaceNames = []string{"api"} + ErrRootSpaceNameNotAllowed = errors.New(fmt.Sprintf("The following names are not allowed for a root space: %v", illegalRootSpaceNames)) ) // User returns true if the User if valid. @@ -50,5 +54,14 @@ func Space(space *types.Space) (bool, error) { return false, ErrSpaceDisplayNameRegex } + // root space specific validations + if space.ParentId <= 0 { + for _, p := range illegalRootSpaceNames { + if strings.HasPrefix(space.Name, p) { + return false, ErrRootSpaceNameNotAllowed + } + } + } + return true, nil }