mirror of https://github.com/harness/drone.git
[Embedded] Harness Router, Inline Space Creation, Bootstrap, Harness/Admin User Setup (#28)
Adds the basic for harness embedded mode: - Harness dedicated router with custom APIHandler - Inline Space Creation - Client for Account/Org/Project - Bootstrap (Allows for automated creation of admin user and gitness service (used for all platform required ops)) - Inline harness service principal creation - Ignore flag for ACL.jobatzil/rename
parent
c07dc4c779
commit
5786ad2409
36
.harness.env
36
.harness.env
|
@ -1,18 +1,28 @@
|
|||
# Gitness values
|
||||
GITNESS_TRACE=true
|
||||
GITNESS_ADMIN_NAME=Administrator
|
||||
GITNESS_ADMIN_EMAIL=gitness@harness.io
|
||||
GITNESS_ADMIN_PASSWORD=changeit
|
||||
|
||||
# Harness specifc values
|
||||
HARNESS_JWT_IDENTITY="gitness"
|
||||
HARNESS_JWT_SECRET="IC04LYMBf1lDP5oeY4hupxd4HJhLmN6azUku3xEbeE3SUx5G3ZYzhbiwVtK4i7AmqyU9OZkwB4v8E9qM"
|
||||
HARNESS_JWT_VALIDINMIN=1440
|
||||
HARNESS_JWT_BEARER_IDENTITY="Bearer"
|
||||
HARNESS_JWT_BEARER_SECRET="dOkdsVqdRPPRJG31XU0qY4MPqmBBMk0PTAGIKM6O7TGqhjyxScIdJe80mwh5Yb5zF3KxYBHw6B3Lfzlq"
|
||||
HARNESS_JWT_IDENTITY_SERVICE_IDENTITY="IdentityService"
|
||||
HARNESS_JWT_IDENTITY_SERVICE_SECRET="HVSKUYqD4e5Rxu12hFDdCJKGM64sxgEynvdDhaOHaTHhwwn0K4Ttr0uoOxSsEVYNrUU"
|
||||
HARNESS_JWT_MANAGER_IDENTITY="Manager"
|
||||
HARNESS_JWT_MANAGER_SECRET="dOkdsVqdRPPRJG31XU0qY4MPqmBBMk0PTAGIKM6O7TGqhjyxScIdJe80mwh5Yb5zF3KxYBHw6B3Lfzlq"
|
||||
HARNESS_JWT_NGMANAGER_IDENTITY="NextGenManager"
|
||||
HARNESS_JWT_NGMANAGER_SECRET="IC04LYMBf1lDP5oeY4hupxd4HJhLmN6azUku3xEbeE3SUx5G3ZYzhbiwVtK4i7AmqyU9OZkwB4v8E9qM"
|
||||
HARNESS_CLIENTS_ACL_SECURE=false
|
||||
HARNESS_CLIENTS_ACL_BASEURL="http://localhost:9006/api"
|
||||
HARNESS_CLIENTS_MANAGER_SECURE=false
|
||||
HARNESS_CLIENTS_MANAGER_BASEURL="http://localhost:3457/api"
|
||||
HARNESS_CLIENTS_NGMANAGER_SECURE=false
|
||||
HARNESS_CLIENTS_NGMANAGER_BASEURL="http://localhost:7457"
|
||||
|
||||
HARNESS_SERVICES_IDENTITY_JWT_IDENTITY="IdentityService"
|
||||
HARNESS_SERVICES_IDENTITY_JWT_SECRET="HVSKUYqD4e5Rxu12hFDdCJKGM64sxgEynvdDhaOHaTHhwwn0K4Ttr0uoOxSsEVYNrUU"
|
||||
|
||||
HARNESS_SERVICES_ACL_IGNORE=true
|
||||
HARNESS_SERVICES_ACL_CLIENT_SECURE=false
|
||||
HARNESS_SERVICES_ACL_CLIENT_BASEURL="http://localhost:9006/api"
|
||||
|
||||
HARNESS_SERVICES_MANAGER_CLIENT_SECURE=false
|
||||
HARNESS_SERVICES_MANAGER_CLIENT_BASEURL="http://localhost:3457/api"
|
||||
HARNESS_SERVICES_MANAGER_JWT_IDENTITY="Manager"
|
||||
HARNESS_SERVICES_MANAGER_JWT_SECRET="dOkdsVqdRPPRJG31XU0qY4MPqmBBMk0PTAGIKM6O7TGqhjyxScIdJe80mwh5Yb5zF3KxYBHw6B3Lfzlq"
|
||||
|
||||
HARNESS_SERVICES_NGMANAGER_CLIENT_SECURE=false
|
||||
HARNESS_SERVICES_NGMANAGER_CLIENT_BASEURL="http://localhost:7457"
|
||||
HARNESS_SERVICES_NGMANAGER_JWT_IDENTITY="NextGenManager"
|
||||
HARNESS_SERVICES_NGMANAGER_JWT_SECRET="IC04LYMBf1lDP5oeY4hupxd4HJhLmN6azUku3xEbeE3SUx5G3ZYzhbiwVtK4i7AmqyU9OZkwB4v8E9qM"
|
||||
|
|
|
@ -1 +1,5 @@
|
|||
GITNESS_TRACE=true
|
||||
GITNESS_TRACE=true
|
||||
GITNESS_ADMIN_UID=admin
|
||||
GITNESS_ADMIN_NAME=Administrator
|
||||
GITNESS_ADMIN_EMAIL=admin@gitness.io
|
||||
GITNESS_ADMIN_PASSWORD=changeit
|
|
@ -10,37 +10,39 @@ package server
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/harness"
|
||||
"github.com/harness/gitness/harness/auth/authn"
|
||||
"github.com/harness/gitness/harness/auth/authz"
|
||||
"github.com/harness/gitness/harness/bootstrap"
|
||||
"github.com/harness/gitness/harness/client"
|
||||
"github.com/harness/gitness/harness/router"
|
||||
"github.com/harness/gitness/harness/types"
|
||||
"github.com/harness/gitness/internal/api/controller/repo"
|
||||
"github.com/harness/gitness/internal/api/controller/serviceaccount"
|
||||
"github.com/harness/gitness/internal/api/controller/service"
|
||||
"github.com/harness/gitness/internal/api/controller/space"
|
||||
"github.com/harness/gitness/internal/api/controller/user"
|
||||
"github.com/harness/gitness/internal/cron"
|
||||
"github.com/harness/gitness/internal/router"
|
||||
"github.com/harness/gitness/internal/server"
|
||||
"github.com/harness/gitness/internal/store/database"
|
||||
"github.com/harness/gitness/internal/store/memory"
|
||||
"github.com/harness/gitness/types"
|
||||
gitnessTypes "github.com/harness/gitness/types"
|
||||
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
func initSystem(ctx context.Context, config *types.Config) (*system, error) {
|
||||
func initSystem(ctx context.Context, config *gitnessTypes.Config) (*system, error) {
|
||||
wire.Build(
|
||||
newSystem,
|
||||
bootstrap.WireSet,
|
||||
database.WireSet,
|
||||
memory.WireSet,
|
||||
router.WireSet,
|
||||
server.WireSet,
|
||||
cron.WireSet,
|
||||
repo.WireSet,
|
||||
serviceaccount.WireSet,
|
||||
space.WireSet,
|
||||
user.WireSet,
|
||||
harness.LoadConfig,
|
||||
service.WireSet,
|
||||
types.LoadConfig,
|
||||
router.WireSet,
|
||||
authn.WireSet,
|
||||
authz.WireSet,
|
||||
client.WireSet,
|
||||
|
|
|
@ -8,16 +8,18 @@ package server
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/harness"
|
||||
"github.com/harness/gitness/harness/auth/authn"
|
||||
"github.com/harness/gitness/harness/auth/authz"
|
||||
"github.com/harness/gitness/harness/bootstrap"
|
||||
"github.com/harness/gitness/harness/client"
|
||||
"github.com/harness/gitness/harness/router"
|
||||
types2 "github.com/harness/gitness/harness/types"
|
||||
"github.com/harness/gitness/internal/api/controller/repo"
|
||||
"github.com/harness/gitness/internal/api/controller/serviceaccount"
|
||||
"github.com/harness/gitness/internal/api/controller/service"
|
||||
"github.com/harness/gitness/internal/api/controller/space"
|
||||
"github.com/harness/gitness/internal/api/controller/user"
|
||||
"github.com/harness/gitness/internal/cron"
|
||||
"github.com/harness/gitness/internal/router"
|
||||
router2 "github.com/harness/gitness/internal/router"
|
||||
"github.com/harness/gitness/internal/server"
|
||||
"github.com/harness/gitness/internal/store/database"
|
||||
"github.com/harness/gitness/internal/store/memory"
|
||||
|
@ -27,55 +29,61 @@ import (
|
|||
// Injectors from harness.wire.go:
|
||||
|
||||
func initSystem(ctx context.Context, config *types.Config) (*system, error) {
|
||||
systemStore := memory.New(config)
|
||||
typesConfig, err := types2.LoadConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serviceJWTProvider, err := client.ProvideServiceJWTProvider(typesConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aclClient, err := client.ProvideACLClient(serviceJWTProvider, typesConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorizer := authz.ProvideAuthorizer(typesConfig, aclClient)
|
||||
db, err := database.ProvideDatabase(ctx, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userStore := database.ProvideUserStore(db)
|
||||
harnessConfig, err := harness.LoadConfig()
|
||||
tokenStore := database.ProvideTokenStore(db)
|
||||
controller := user.NewController(authorizer, userStore, tokenStore)
|
||||
serviceStore := database.ProvideServiceStore(db)
|
||||
serviceController := service.NewController(authorizer, serviceStore)
|
||||
bootstrapBootstrap := bootstrap.ProvideBootstrap(config, controller, serviceController)
|
||||
systemStore := memory.New(config)
|
||||
tokenClient, err := client.ProvideTokenClient(serviceJWTProvider, typesConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serviceJWTProvider, err := client.ProvideServiceJWTProvider(harnessConfig)
|
||||
userClient, err := client.ProvideUserClient(serviceJWTProvider, typesConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenClient, err := client.ProvideTokenClient(serviceJWTProvider, harnessConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userClient, err := client.ProvideUserClient(serviceJWTProvider, harnessConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serviceAccountClient, err := client.ProvideServiceAccountClient(serviceJWTProvider, harnessConfig)
|
||||
serviceAccountClient, err := client.ProvideServiceAccountClient(serviceJWTProvider, typesConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serviceAccountStore := database.ProvideServiceAccountStore(db)
|
||||
authenticator, err := authn.ProvideAuthenticator(userStore, tokenClient, userClient, harnessConfig, serviceAccountClient, serviceAccountStore)
|
||||
authenticator, err := authn.ProvideAuthenticator(userStore, tokenClient, userClient, typesConfig, serviceAccountClient, serviceAccountStore, serviceStore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aclClient, err := client.ProvideACLClient(serviceJWTProvider, harnessConfig)
|
||||
accountClient, err := client.ProvideAccountClient(serviceJWTProvider, typesConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorizer := authz.ProvideAuthorizer(aclClient)
|
||||
spaceStore := database.ProvideSpaceStore(db)
|
||||
repoStore := database.ProvideRepoStore(db)
|
||||
controller := repo.NewController(authorizer, spaceStore, repoStore, serviceAccountStore)
|
||||
spaceController := space.NewController(authorizer, spaceStore, repoStore, serviceAccountStore)
|
||||
tokenStore := database.ProvideTokenStore(db)
|
||||
serviceaccountController := serviceaccount.NewController(authorizer, serviceAccountStore, spaceStore, repoStore, tokenStore)
|
||||
userController := user.NewController(authorizer, userStore, tokenStore)
|
||||
apiHandler := router.ProvideAPIHandler(systemStore, authenticator, controller, spaceController, serviceaccountController, userController)
|
||||
gitHandler := router.ProvideGitHandler(repoStore, authenticator)
|
||||
webHandler := router.ProvideWebHandler(systemStore)
|
||||
routerRouter := router.ProvideRouter(apiHandler, gitHandler, webHandler)
|
||||
repoController := repo.NewController(authorizer, spaceStore, repoStore, serviceAccountStore)
|
||||
apiHandler := router.ProvideAPIHandler(systemStore, authenticator, accountClient, spaceController, repoController)
|
||||
gitHandler := router2.ProvideGitHandler(repoStore, authenticator)
|
||||
webHandler := router2.ProvideWebHandler(systemStore)
|
||||
routerRouter := router2.ProvideRouter(apiHandler, gitHandler, webHandler)
|
||||
serverServer := server.ProvideServer(config, routerRouter)
|
||||
nightly := cron.NewNightly()
|
||||
serverSystem := newSystem(serverServer, nightly)
|
||||
serverSystem := newSystem(bootstrapBootstrap, serverServer, nightly)
|
||||
return serverSystem, nil
|
||||
}
|
||||
|
|
|
@ -54,10 +54,20 @@ func (c *command) run(*kingpin.ParseContext) error {
|
|||
// configure the log level
|
||||
setupLogger(config)
|
||||
|
||||
// add logger to context
|
||||
log := log.Logger.With().Logger()
|
||||
ctx = log.WithContext(ctx)
|
||||
|
||||
// initialize system
|
||||
system, err := initSystem(ctx, config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encountered an error while initializing the system: %w", err)
|
||||
return fmt.Errorf("encountered an error while wiring the system: %w", err)
|
||||
}
|
||||
|
||||
// bootstrap the system
|
||||
err = system.bootstrap(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encountered an error while bootstrapping the system: %w", err)
|
||||
}
|
||||
|
||||
// collects all go routines - gCTX cancels if any go routine encounters an error
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/harness/gitness/internal/api/controller/user"
|
||||
"github.com/harness/gitness/internal/auth/authn"
|
||||
"github.com/harness/gitness/internal/auth/authz"
|
||||
"github.com/harness/gitness/internal/bootstrap"
|
||||
"github.com/harness/gitness/internal/cron"
|
||||
"github.com/harness/gitness/internal/router"
|
||||
"github.com/harness/gitness/internal/server"
|
||||
|
@ -29,6 +30,7 @@ import (
|
|||
func initSystem(ctx context.Context, config *types.Config) (*system, error) {
|
||||
wire.Build(
|
||||
newSystem,
|
||||
bootstrap.WireSet,
|
||||
database.WireSet,
|
||||
memory.WireSet,
|
||||
router.WireSet,
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/harness/gitness/internal/api/controller/user"
|
||||
"github.com/harness/gitness/internal/auth/authn"
|
||||
"github.com/harness/gitness/internal/auth/authz"
|
||||
"github.com/harness/gitness/internal/bootstrap"
|
||||
"github.com/harness/gitness/internal/cron"
|
||||
"github.com/harness/gitness/internal/router"
|
||||
"github.com/harness/gitness/internal/server"
|
||||
|
@ -25,28 +26,29 @@ import (
|
|||
// Injectors from standalone.wire.go:
|
||||
|
||||
func initSystem(ctx context.Context, config *types.Config) (*system, error) {
|
||||
systemStore := memory.New(config)
|
||||
authorizer := authz.ProvideAuthorizer()
|
||||
db, err := database.ProvideDatabase(ctx, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userStore := database.ProvideUserStore(db)
|
||||
serviceAccountStore := database.ProvideServiceAccountStore(db)
|
||||
tokenStore := database.ProvideTokenStore(db)
|
||||
controller := user.NewController(authorizer, userStore, tokenStore)
|
||||
bootstrapBootstrap := bootstrap.ProvideBootstrap(config, controller)
|
||||
systemStore := memory.New(config)
|
||||
serviceAccountStore := database.ProvideServiceAccountStore(db)
|
||||
authenticator := authn.ProvideAuthenticator(userStore, serviceAccountStore, tokenStore)
|
||||
authorizer := authz.ProvideAuthorizer()
|
||||
spaceStore := database.ProvideSpaceStore(db)
|
||||
repoStore := database.ProvideRepoStore(db)
|
||||
controller := repo.NewController(authorizer, spaceStore, repoStore, serviceAccountStore)
|
||||
repoController := repo.NewController(authorizer, spaceStore, repoStore, serviceAccountStore)
|
||||
spaceController := space.NewController(authorizer, spaceStore, repoStore, serviceAccountStore)
|
||||
serviceaccountController := serviceaccount.NewController(authorizer, serviceAccountStore, spaceStore, repoStore, tokenStore)
|
||||
userController := user.NewController(authorizer, userStore, tokenStore)
|
||||
apiHandler := router.ProvideAPIHandler(systemStore, authenticator, controller, spaceController, serviceaccountController, userController)
|
||||
apiHandler := router.ProvideAPIHandler(systemStore, authenticator, repoController, spaceController, serviceaccountController, controller)
|
||||
gitHandler := router.ProvideGitHandler(repoStore, authenticator)
|
||||
webHandler := router.ProvideWebHandler(systemStore)
|
||||
routerRouter := router.ProvideRouter(apiHandler, gitHandler, webHandler)
|
||||
serverServer := server.ProvideServer(config, routerRouter)
|
||||
nightly := cron.NewNightly()
|
||||
serverSystem := newSystem(serverServer, nightly)
|
||||
serverSystem := newSystem(bootstrapBootstrap, serverServer, nightly)
|
||||
return serverSystem, nil
|
||||
}
|
||||
|
|
|
@ -5,20 +5,23 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"github.com/harness/gitness/internal/bootstrap"
|
||||
"github.com/harness/gitness/internal/cron"
|
||||
"github.com/harness/gitness/internal/server"
|
||||
)
|
||||
|
||||
// system stores high level system sub-routines.
|
||||
type system struct {
|
||||
server *server.Server
|
||||
nightly *cron.Nightly
|
||||
bootstrap bootstrap.Bootstrap
|
||||
server *server.Server
|
||||
nightly *cron.Nightly
|
||||
}
|
||||
|
||||
// newSystem returns a new system structure.
|
||||
func newSystem(server *server.Server, nightly *cron.Nightly) *system {
|
||||
func newSystem(bootstrap bootstrap.Bootstrap, server *server.Server, nightly *cron.Nightly) *system {
|
||||
return &system{
|
||||
server: server,
|
||||
nightly: nightly,
|
||||
bootstrap: bootstrap,
|
||||
server: server,
|
||||
nightly: nightly,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/internal/auth"
|
||||
"github.com/harness/gitness/internal/auth/authz"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
/*
|
||||
* CheckService checks if a service specific permission is granted for the current auth session.
|
||||
* Returns nil if the permission is granted, otherwise returns an error.
|
||||
* NotAuthenticated, NotAuthorized, or any unerlaying error.
|
||||
*/
|
||||
func CheckService(ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
|
||||
svc *types.Service, permission enum.Permission) error {
|
||||
// a service exists outside of any scope
|
||||
scope := &types.Scope{}
|
||||
resource := &types.Resource{
|
||||
Type: enum.ResourceTypeService,
|
||||
Name: svc.UID,
|
||||
}
|
||||
|
||||
return Check(ctx, authorizer, session, scope, resource, permission)
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/harness/gitness/internal/auth/authz"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
authorizer authz.Authorizer
|
||||
serviceStore store.ServiceStore
|
||||
}
|
||||
|
||||
func NewController(authorizer authz.Authorizer, serviceStore store.ServiceStore) *Controller {
|
||||
return &Controller{
|
||||
authorizer: authorizer,
|
||||
serviceStore: serviceStore,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/dchest/uniuri"
|
||||
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||
"github.com/harness/gitness/internal/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/check"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// CreateInput is the input used for create operations.
|
||||
type CreateInput struct {
|
||||
UID string
|
||||
Name string
|
||||
}
|
||||
|
||||
/*
|
||||
* Create creates a new service.
|
||||
*/
|
||||
func (c *Controller) Create(ctx context.Context, session *auth.Session, in *CreateInput) (*types.Service, error) {
|
||||
// Ensure principal has required permissions (service is global, no explicit resource)
|
||||
scope := &types.Scope{}
|
||||
resource := &types.Resource{
|
||||
Type: enum.ResourceTypeService,
|
||||
}
|
||||
if err := apiauth.Check(ctx, c.authorizer, session, scope, resource, enum.PermissionServiceCreate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.CreateNoAuth(ctx, in, false)
|
||||
}
|
||||
|
||||
/*
|
||||
* CreateNoAuth creates a new service without auth checks.
|
||||
* WARNING: Never call as part of user flow.
|
||||
*
|
||||
* Note: take admin separately to avoid potential vulnerabilities for user calls.
|
||||
*/
|
||||
func (c *Controller) CreateNoAuth(ctx context.Context, in *CreateInput, admin bool) (*types.Service, error) {
|
||||
svc := &types.Service{
|
||||
UID: in.UID,
|
||||
Name: in.Name,
|
||||
Admin: admin,
|
||||
Salt: uniuri.NewLen(uniuri.UUIDLen),
|
||||
Created: time.Now().UnixMilli(),
|
||||
Updated: time.Now().UnixMilli(),
|
||||
}
|
||||
|
||||
// validate service
|
||||
if err := check.Service(svc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err := c.serviceStore.Create(ctx, svc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return svc, nil
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||
"github.com/harness/gitness/internal/auth"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
/*
|
||||
* Delete deletes a service.
|
||||
*/
|
||||
func (c *Controller) Delete(ctx context.Context, session *auth.Session,
|
||||
serviceUID string) error {
|
||||
svc, err := findServiceFromUID(ctx, c.serviceStore, serviceUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure principal has required permissions on parent
|
||||
if err = apiauth.CheckService(ctx, c.authorizer, session, svc, enum.PermissionServiceDelete); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.serviceStore.Delete(ctx, svc.ID)
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||
"github.com/harness/gitness/internal/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
/*
|
||||
* Find tries to find the provided service.
|
||||
*/
|
||||
func (c *Controller) Find(ctx context.Context, session *auth.Session,
|
||||
serviceUID string) (*types.Service, error) {
|
||||
svc, err := c.FindNoAuth(ctx, serviceUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ensure principal has required permissions on parent.
|
||||
if err = apiauth.CheckService(ctx, c.authorizer, session, svc, enum.PermissionServiceView); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
/*
|
||||
* FindNoAuth finds a service without auth checks.
|
||||
* WARNING: Never call as part of user flow.
|
||||
*/
|
||||
func (c *Controller) FindNoAuth(ctx context.Context, serviceUID string) (*types.Service, error) {
|
||||
return findServiceFromUID(ctx, c.serviceStore, serviceUID)
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
func findServiceFromUID(ctx context.Context,
|
||||
serviceStore store.ServiceStore, serviceUID string) (*types.Service, error) {
|
||||
return serviceStore.FindUID(ctx, serviceUID)
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||
"github.com/harness/gitness/internal/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
/*
|
||||
* List lists all services of the system.
|
||||
*/
|
||||
func (c *Controller) List(ctx context.Context, session *auth.Session) (int64, []*types.Service, error) {
|
||||
// Ensure principal has required permissions (service is global, no explicit resource)
|
||||
scope := &types.Scope{}
|
||||
resource := &types.Resource{
|
||||
Type: enum.ResourceTypeService,
|
||||
}
|
||||
if err := apiauth.Check(ctx, c.authorizer, session, scope, resource, enum.PermissionServiceView); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
count, err := c.serviceStore.Count(ctx)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("failed to count services: %w", err)
|
||||
}
|
||||
|
||||
repos, err := c.serviceStore.List(ctx)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("failed to list services: %w", err)
|
||||
}
|
||||
|
||||
return count, repos, nil
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/gotidy/ptr"
|
||||
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||
"github.com/harness/gitness/internal/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/check"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// UpdateInput store infos to update an existing service.
|
||||
type UpdateInput struct {
|
||||
Name *string `json:"name"`
|
||||
}
|
||||
|
||||
/*
|
||||
* Update updates the provided service.
|
||||
*/
|
||||
func (c *Controller) Update(ctx context.Context, session *auth.Session,
|
||||
serviceUID string, in *UpdateInput) (*types.Service, error) {
|
||||
svc, err := findServiceFromUID(ctx, c.serviceStore, serviceUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ensure principal has required permissions on parent.
|
||||
if err = apiauth.CheckService(ctx, c.authorizer, session, svc, enum.PermissionServiceEdit); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if in.Name != nil {
|
||||
svc.Name = ptr.ToString(in.Name)
|
||||
}
|
||||
svc.Updated = time.Now().UnixMilli()
|
||||
|
||||
// validate service
|
||||
if err = check.Service(svc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = c.serviceStore.Update(ctx, svc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return svc, nil
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
apiauth "github.com/harness/gitness/internal/api/auth"
|
||||
"github.com/harness/gitness/internal/auth"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
/*
|
||||
* UpdateAdmin updates the admin state of a service.
|
||||
*/
|
||||
func (c *Controller) UpdateAdmin(ctx context.Context, session *auth.Session,
|
||||
serviceUID string, admin bool) (*types.Service, error) {
|
||||
sbc, err := findServiceFromUID(ctx, c.serviceStore, serviceUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ensure principal has required permissions on parent.
|
||||
if err = apiauth.CheckService(ctx, c.authorizer, session, sbc, enum.PermissionServiceEditAdmin); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sbc.Admin = admin
|
||||
sbc.Updated = time.Now().UnixMilli()
|
||||
|
||||
err = c.serviceStore.Update(ctx, sbc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sbc, nil
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
"github.com/harness/gitness/internal/auth/authz"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
)
|
||||
|
||||
// WireSet provides a wire set for this package.
|
||||
var WireSet = wire.NewSet(
|
||||
NewController,
|
||||
)
|
||||
|
||||
func ProvideController(authorizer authz.Authorizer, serviceStore store.ServiceStore) *Controller {
|
||||
return NewController(authorizer, serviceStore)
|
||||
}
|
|
@ -24,7 +24,7 @@ func (c *Controller) ListRepositories(ctx context.Context, session *auth.Session
|
|||
return 0, nil, err
|
||||
}
|
||||
|
||||
if err = apiauth.CheckSpace(ctx, c.authorizer, session, space, enum.PermissionSpaceView, true); err != nil {
|
||||
if err = apiauth.CheckSpace(ctx, c.authorizer, session, space, enum.PermissionRepoView, true); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ import (
|
|||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// CreateInput is the input used for create operations.
|
||||
// On purpose don't expose admin, has to be enabled explicitly.
|
||||
type CreateInput struct {
|
||||
UID string `json:"uid"`
|
||||
Name string `json:"name"`
|
||||
|
@ -38,13 +40,21 @@ func (c *Controller) Create(ctx context.Context, session *auth.Session, in *Crea
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return c.createNoAuth(ctx, in)
|
||||
return c.CreateNoAuth(ctx, in, false)
|
||||
}
|
||||
|
||||
/*
|
||||
* createNoAuth creates a new user without auth checks.
|
||||
* CreateNoAuth creates a new user without auth checks.
|
||||
* WARNING: Never call as part of user flow.
|
||||
*
|
||||
* Note: take admin separately to avoid potential vulnerabilities for user calls.
|
||||
*/
|
||||
func (c *Controller) createNoAuth(ctx context.Context, in *CreateInput) (*types.User, error) {
|
||||
func (c *Controller) CreateNoAuth(ctx context.Context, in *CreateInput, admin bool) (*types.User, error) {
|
||||
// validate password before hashing
|
||||
if err := check.Password(in.Password); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hash, err := hashPassword([]byte(in.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create hash: %w", err)
|
||||
|
@ -58,6 +68,7 @@ func (c *Controller) createNoAuth(ctx context.Context, in *CreateInput) (*types.
|
|||
Salt: uniuri.NewLen(uniuri.UUIDLen),
|
||||
Created: time.Now().UnixMilli(),
|
||||
Updated: time.Now().UnixMilli(),
|
||||
Admin: admin,
|
||||
}
|
||||
|
||||
// validate user
|
||||
|
@ -71,6 +82,7 @@ func (c *Controller) createNoAuth(ctx context.Context, in *CreateInput) (*types.
|
|||
}
|
||||
|
||||
// first user will be admin by default.
|
||||
// TODO: move responsibility somewhere else.
|
||||
if user.ID == 1 {
|
||||
user.Admin = true
|
||||
err = c.userStore.Update(ctx, user)
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
*/
|
||||
func (c *Controller) Find(ctx context.Context, session *auth.Session,
|
||||
userUID string) (*types.User, error) {
|
||||
user, err := findUserFromUID(ctx, c.userStore, userUID)
|
||||
user, err := c.FindNoAuth(ctx, userUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -30,3 +30,11 @@ func (c *Controller) Find(ctx context.Context, session *auth.Session,
|
|||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
/*
|
||||
* FindNoAuth finds a user without auth checks.
|
||||
* WARNING: Never call as part of user flow.
|
||||
*/
|
||||
func (c *Controller) FindNoAuth(ctx context.Context, userUID string) (*types.User, error) {
|
||||
return findUserFromUID(ctx, c.userStore, userUID)
|
||||
}
|
||||
|
|
|
@ -18,9 +18,9 @@ import (
|
|||
* functionalities (unable to create admin user for example).
|
||||
*/
|
||||
func (c *Controller) Register(ctx context.Context, in *CreateInput) (*types.TokenResponse, error) {
|
||||
// TODO: allows to configure if open register is allowed.
|
||||
// TODO: allow to configure if open register is allowed.
|
||||
|
||||
user, err := c.createNoAuth(ctx, in)
|
||||
user, err := c.CreateNoAuth(ctx, in, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create user: %w", err)
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ func HandleCreatePath(repoCtrl *repo.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
repoRef, err := request.GetRepoRef(r)
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -19,7 +19,7 @@ func HandleDelete(repoCtrl *repo.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
repoRef, err := request.GetRepoRef(r)
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -19,12 +19,12 @@ func HandleDeletePath(repoCtrl *repo.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
repoRef, err := request.GetRepoRef(r)
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
pathID, err := request.GetPathID(r)
|
||||
pathID, err := request.GetPathIDFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -19,7 +19,7 @@ func HandleFind(repoCtrl *repo.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
repoRef, err := request.GetRepoRef(r)
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -20,7 +20,7 @@ func HandleListPaths(repoCtrl *repo.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
repoRef, err := request.GetRepoRef(r)
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -19,7 +19,7 @@ func HandleListServiceAccounts(repoCtrl *repo.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
repoRef, err := request.GetRepoRef(r)
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -18,7 +18,7 @@ func HandleMove(repoCtrl *repo.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
repoRef, err := request.GetRepoRef(r)
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -20,7 +20,7 @@ func HandleUpdate(repoCtrl *repo.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
repoRef, err := request.GetRepoRef(r)
|
||||
repoRef, err := request.GetRepoRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -19,7 +19,7 @@ func HandleCreateToken(saCrl *serviceaccount.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
saUID, err := request.GetServiceAccountUID(r)
|
||||
saUID, err := request.GetServiceAccountUIDFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -19,7 +19,7 @@ func HandleDelete(saCrl *serviceaccount.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
saUID, err := request.GetServiceAccountUID(r)
|
||||
saUID, err := request.GetServiceAccountUIDFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -18,12 +18,12 @@ func HandleDeleteToken(saCrl *serviceaccount.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
saUID, err := request.GetServiceAccountUID(r)
|
||||
saUID, err := request.GetServiceAccountUIDFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
tokenID, err := request.GetTokenID(r)
|
||||
tokenID, err := request.GetTokenIDFromPath(r)
|
||||
if err != nil {
|
||||
render.BadRequest(w)
|
||||
return
|
||||
|
|
|
@ -18,7 +18,7 @@ func HandleFind(saCrl *serviceaccount.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
saUID, err := request.GetServiceAccountUID(r)
|
||||
saUID, err := request.GetServiceAccountUIDFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -18,7 +18,7 @@ func HandleListTokens(saCrl *serviceaccount.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
saUID, err := request.GetServiceAccountUID(r)
|
||||
saUID, err := request.GetServiceAccountUIDFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -20,7 +20,7 @@ func HandleCreatePath(spaceCtrl *space.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
spaceRef, err := request.GetSpaceRef(r)
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -19,7 +19,7 @@ func HandleDelete(spaceCtrl *space.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
spaceRef, err := request.GetSpaceRef(r)
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -19,13 +19,13 @@ func HandleDeletePath(spaceCtrl *space.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
spaceRef, err := request.GetSpaceRef(r)
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
pathID, err := request.GetPathID(r)
|
||||
pathID, err := request.GetPathIDFromPath(r)
|
||||
if err != nil {
|
||||
render.BadRequest(w)
|
||||
return
|
||||
|
|
|
@ -19,7 +19,7 @@ func HandleFind(spaceCtrl *space.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
spaceRef, err := request.GetSpaceRef(r)
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -18,7 +18,7 @@ func HandleListSpaces(spaceCtrl *space.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
spaceRef, err := request.GetSpaceRef(r)
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -20,7 +20,7 @@ func HandleListPaths(spaceCtrl *space.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
spaceRef, err := request.GetSpaceRef(r)
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -18,7 +18,7 @@ func HandleListRepos(spaceCtrl *space.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
spaceRef, err := request.GetSpaceRef(r)
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -19,7 +19,7 @@ func HandleListServiceAccounts(spaceCtrl *space.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
spaceRef, err := request.GetSpaceRef(r)
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -18,7 +18,7 @@ func HandleMove(spaceCtrl *space.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
spaceRef, err := request.GetSpaceRef(r)
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -20,7 +20,7 @@ func HandleUpdate(spaceCtrl *space.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
spaceRef, err := request.GetSpaceRef(r)
|
||||
spaceRef, err := request.GetSpaceRefFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -21,7 +21,7 @@ func HandleDeleteToken(userCtrl *user.Controller, tokenType enum.TokenType) http
|
|||
session, _ := request.AuthSessionFrom(ctx)
|
||||
userUID := session.Principal.UID
|
||||
|
||||
tokenID, err := request.GetTokenID(r)
|
||||
tokenID, err := request.GetTokenIDFromPath(r)
|
||||
if err != nil {
|
||||
render.BadRequest(w)
|
||||
return
|
||||
|
|
|
@ -18,7 +18,7 @@ func HandleDelete(userCtrl *user.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
userUID, err := request.GetUserUID(r)
|
||||
userUID, err := request.GetUserUIDFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -18,7 +18,7 @@ func HandleFind(userCtrl *user.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
userUID, err := request.GetUserUID(r)
|
||||
userUID, err := request.GetUserUIDFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -19,7 +19,7 @@ func HandleUpdate(userCtrl *user.Controller) http.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
userUID, err := request.GetUserUID(r)
|
||||
userUID, err := request.GetUserUIDFromPath(r)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(w, err)
|
||||
return
|
||||
|
|
|
@ -19,6 +19,19 @@ import (
|
|||
// Attempt returns an http.HandlerFunc middleware that authenticates
|
||||
// the http.Request if authentication payload is available.
|
||||
func Attempt(authenticator authn.Authenticator) func(http.Handler) http.Handler {
|
||||
return performAuthentication(authenticator, false)
|
||||
}
|
||||
|
||||
// Required returns an http.HandlerFunc middleware that authenticates
|
||||
// the http.Request and fails the request if no auth data was available.
|
||||
func Required(authenticator authn.Authenticator) func(http.Handler) http.Handler {
|
||||
return performAuthentication(authenticator, true)
|
||||
}
|
||||
|
||||
// performAuthentication returns an http.HandlerFunc middleware that authenticates
|
||||
// the http.Request if authentication payload is available.
|
||||
// Depending on whether it is required or not, the request will be failed.
|
||||
func performAuthentication(authenticator authn.Authenticator, required bool) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
@ -27,6 +40,11 @@ func Attempt(authenticator authn.Authenticator) func(http.Handler) http.Handler
|
|||
session, err := authenticator.Authenticate(r)
|
||||
|
||||
if errors.Is(err, authn.ErrNoAuthData) {
|
||||
if required {
|
||||
render.Unauthorized(w)
|
||||
return
|
||||
}
|
||||
|
||||
// if there was no auth data in the request - continue as is
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
|
|
|
@ -8,6 +8,6 @@ const (
|
|||
PathParamPathID = "pathID"
|
||||
)
|
||||
|
||||
func GetPathID(r *http.Request) (int64, error) {
|
||||
return ParseAsInt64(r, PathParamPathID)
|
||||
func GetPathIDFromPath(r *http.Request) (int64, error) {
|
||||
return PathParamAsInt64(r, PathParamPathID)
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@ const (
|
|||
PathParamServiceAccountUID = "saUID"
|
||||
)
|
||||
|
||||
func GetUserUID(r *http.Request) (string, error) {
|
||||
return ParamOrError(r, PathParamUserUID)
|
||||
func GetUserUIDFromPath(r *http.Request) (string, error) {
|
||||
return PathParamOrError(r, PathParamUserUID)
|
||||
}
|
||||
|
||||
func GetServiceAccountUID(r *http.Request) (string, error) {
|
||||
return ParamOrError(r, PathParamServiceAccountUID)
|
||||
func GetServiceAccountUIDFromPath(r *http.Request) (string, error) {
|
||||
return PathParamOrError(r, PathParamServiceAccountUID)
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ const (
|
|||
PathParamRepoRef = "repositoryRef"
|
||||
)
|
||||
|
||||
func GetRepoRef(r *http.Request) (string, error) {
|
||||
rawRef, err := ParamOrError(r, PathParamRepoRef)
|
||||
func GetRepoRefFromPath(r *http.Request) (string, error) {
|
||||
rawRef, err := PathParamOrError(r, PathParamRepoRef)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ const (
|
|||
PathParamSpaceRef = "spaceRef"
|
||||
)
|
||||
|
||||
func GetSpaceRef(r *http.Request) (string, error) {
|
||||
rawRef, err := ParamOrError(r, PathParamSpaceRef)
|
||||
func GetSpaceRefFromPath(r *http.Request) (string, error) {
|
||||
rawRef, err := PathParamOrError(r, PathParamSpaceRef)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -8,6 +8,6 @@ const (
|
|||
PathParamTokenID = "tokenID"
|
||||
)
|
||||
|
||||
func GetTokenID(r *http.Request) (int64, error) {
|
||||
return ParseAsInt64(r, PathParamTokenID)
|
||||
func GetTokenIDFromPath(r *http.Request) (int64, error) {
|
||||
return PathParamAsInt64(r, PathParamTokenID)
|
||||
}
|
||||
|
|
|
@ -15,9 +15,9 @@ import (
|
|||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
// ParamOrError tries to retrieve the parameter from the request and
|
||||
// PathParamOrError tries to retrieve the parameter from the request and
|
||||
// returns the parameter if it exists and is not empty, otherwise returns an error.
|
||||
func ParamOrError(r *http.Request, paramName string) (string, error) {
|
||||
func PathParamOrError(r *http.Request, paramName string) (string, error) {
|
||||
value := chi.URLParam(r, paramName)
|
||||
if value == "" {
|
||||
return "", usererror.BadRequest(fmt.Sprintf("Parameter '%s' not found in request path.", paramName))
|
||||
|
@ -26,9 +26,9 @@ func ParamOrError(r *http.Request, paramName string) (string, error) {
|
|||
return value, nil
|
||||
}
|
||||
|
||||
// ParseAsInt64 tries to retrieve the parameter from the request and parse it to in64.
|
||||
func ParseAsInt64(r *http.Request, paramName string) (int64, error) {
|
||||
rawID, err := ParamOrError(r, paramName)
|
||||
// PathParamAsInt64 tries to retrieve the parameter from the request and parse it to in64.
|
||||
func PathParamAsInt64(r *http.Request, paramName string) (int64, error) {
|
||||
rawID, err := PathParamOrError(r, paramName)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ func Translate(err error) *Error {
|
|||
|
||||
// unknown error
|
||||
default:
|
||||
log.Err(err).Msgf("Unable to translate error: %s", err)
|
||||
log.Warn().Msgf("Unable to translate error: %s", err)
|
||||
return ErrInternal
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/harness/gitness/internal/api/controller/user"
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
adminUID = "admin"
|
||||
)
|
||||
|
||||
// Bootstrap is an abstraction of a function that bootstraps a system.
|
||||
type Bootstrap func(context.Context) error
|
||||
|
||||
func System(config *types.Config, userCtrl *user.Controller) func(context.Context) error {
|
||||
return func(ctx context.Context) error {
|
||||
return Admin(ctx, config, userCtrl)
|
||||
}
|
||||
}
|
||||
|
||||
// Admin sets up the admin user based on the config (if provided)
|
||||
//
|
||||
// NOTE: We could just call update and ignore any duplicate error
|
||||
// but then the duplicte might be due to email or name, not uid.
|
||||
// Futhermore, it would create unnecesary error logs.
|
||||
func Admin(ctx context.Context, config *types.Config, userCtrl *user.Controller) error {
|
||||
if config.Admin.Name == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := userCtrl.FindNoAuth(ctx, adminUID)
|
||||
if err == nil || !errors.Is(err, store.ErrResourceNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
in := &user.CreateInput{
|
||||
UID: adminUID,
|
||||
Name: config.Admin.Name,
|
||||
Email: config.Admin.Email,
|
||||
Password: config.Admin.Password,
|
||||
}
|
||||
|
||||
// create user as admin
|
||||
usr, err := userCtrl.CreateNoAuth(ctx, in, true)
|
||||
if errors.Is(err, store.ErrDuplicate) {
|
||||
// user might've been created by another instance
|
||||
// in which case we should find the user now.
|
||||
_, err2 := userCtrl.FindNoAuth(ctx, adminUID)
|
||||
if err2 != nil {
|
||||
// return original error.
|
||||
return err
|
||||
}
|
||||
|
||||
// user exists - perfect
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create admin user: %w", err)
|
||||
}
|
||||
|
||||
log.Ctx(ctx).Info().Msgf("Created admin user (id: %d).", usr.ID)
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
"github.com/harness/gitness/internal/api/controller/user"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
// WireSet provides a wire set for this package.
|
||||
var WireSet = wire.NewSet(ProvideBootstrap)
|
||||
|
||||
func ProvideBootstrap(config *types.Config, userCtrl *user.Controller) Bootstrap {
|
||||
return System(config, userCtrl)
|
||||
}
|
|
@ -76,7 +76,7 @@ func stubGitHandler(repoStore store.RepoStore) http.HandlerFunc {
|
|||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusTeapot)
|
||||
|
||||
repoPath, _ := request.GetRepoRef(r)
|
||||
repoPath, _ := request.GetRepoRefFromPath(r)
|
||||
repo, err := repoStore.FindByPath(r.Context(), repoPath)
|
||||
if err != nil {
|
||||
_, _ = w.Write([]byte(fmt.Sprintf("Repo '%s' not found.", repoPath)))
|
||||
|
|
|
@ -5,7 +5,7 @@ CREATE TABLE IF NOT EXISTS paths (
|
|||
,path_targetType TEXT
|
||||
,path_targetId INTEGER
|
||||
,path_createdBy INTEGER
|
||||
,path_created INTEGER
|
||||
,path_updated INTEGER
|
||||
,path_created BIGINT
|
||||
,path_updated BIGINT
|
||||
,UNIQUE(path_value)
|
||||
);
|
|
@ -6,8 +6,8 @@ principal_id SERIAL PRIMARY KEY
|
|||
,principal_admin BOOLEAN
|
||||
,principal_blocked BOOLEAN
|
||||
,principal_salt TEXT
|
||||
,principal_created INTEGER
|
||||
,principal_updated INTEGER
|
||||
,principal_created BIGINT
|
||||
,principal_updated BIGINT
|
||||
|
||||
,principal_user_email CITEXT
|
||||
,principal_user_password TEXT
|
||||
|
|
|
@ -6,8 +6,8 @@ CREATE TABLE IF NOT EXISTS repositories (
|
|||
,repo_description TEXT
|
||||
,repo_isPublic BOOLEAN
|
||||
,repo_createdBy INTEGER
|
||||
,repo_created INTEGER
|
||||
,repo_updated INTEGER
|
||||
,repo_created BIGINT
|
||||
,repo_updated BIGINT
|
||||
,repo_forkId INTEGER
|
||||
,repo_numForks INTEGER
|
||||
,repo_numPulls INTEGER
|
||||
|
|
|
@ -6,6 +6,6 @@ CREATE TABLE IF NOT EXISTS spaces (
|
|||
,space_description TEXT
|
||||
,space_isPublic BOOLEAN
|
||||
,space_createdBy INTEGER
|
||||
,space_created INTEGER
|
||||
,space_updated INTEGER
|
||||
,space_created BIGINT
|
||||
,space_updated BIGINT
|
||||
);
|
|
@ -3,8 +3,8 @@ CREATE TABLE IF NOT EXISTS tokens (
|
|||
,token_type TEXT
|
||||
,token_name TEXT
|
||||
,token_principalId INTEGER
|
||||
,token_expiresAt INTEGER
|
||||
,token_grants INTEGER
|
||||
,token_issuedAt INTEGER
|
||||
,token_expiresAt BIGINT
|
||||
,token_grants BIGINT
|
||||
,token_issuedAt BIGINT
|
||||
,token_createdBy INTEGER
|
||||
);
|
||||
|
|
|
@ -5,7 +5,7 @@ CREATE TABLE IF NOT EXISTS paths (
|
|||
,path_targetType TEXT
|
||||
,path_targetId INTEGER
|
||||
,path_createdBy INTEGER
|
||||
,path_created INTEGER
|
||||
,path_updated INTEGER
|
||||
,path_created BIGINT
|
||||
,path_updated BIGINT
|
||||
,UNIQUE(path_value COLLATE NOCASE)
|
||||
);
|
|
@ -6,8 +6,8 @@ principal_id INTEGER PRIMARY KEY AUTOINCREMENT
|
|||
,principal_admin BOOLEAN
|
||||
,principal_blocked BOOLEAN
|
||||
,principal_salt TEXT
|
||||
,principal_created INTEGER
|
||||
,principal_updated INTEGER
|
||||
,principal_created BIGINT
|
||||
,principal_updated BIGINT
|
||||
|
||||
,principal_user_email TEXT
|
||||
,principal_user_password TEXT
|
||||
|
|
|
@ -6,8 +6,8 @@ CREATE TABLE IF NOT EXISTS repositories (
|
|||
,repo_description TEXT
|
||||
,repo_isPublic BOOLEAN
|
||||
,repo_createdBy INTEGER
|
||||
,repo_created INTEGER
|
||||
,repo_updated INTEGER
|
||||
,repo_created BIGINT
|
||||
,repo_updated BIGINT
|
||||
,repo_forkId INTEGER
|
||||
,repo_numForks INTEGER
|
||||
,repo_numPulls INTEGER
|
||||
|
|
|
@ -6,6 +6,6 @@ CREATE TABLE IF NOT EXISTS spaces (
|
|||
,space_description TEXT
|
||||
,space_isPublic BOOLEAN
|
||||
,space_createdBy INTEGER
|
||||
,space_created INTEGER
|
||||
,space_updated INTEGER
|
||||
,space_created BIGINT
|
||||
,space_updated BIGINT
|
||||
);
|
|
@ -3,8 +3,8 @@ CREATE TABLE IF NOT EXISTS tokens (
|
|||
,token_type TEXT COLLATE NOCASE
|
||||
,token_name TEXT
|
||||
,token_principalId INTEGER
|
||||
,token_expiresAt INTEGER
|
||||
,token_grants INTEGER
|
||||
,token_issuedAt INTEGER
|
||||
,token_expiresAt BIGINT
|
||||
,token_grants BIGINT
|
||||
,token_issuedAt BIGINT
|
||||
,token_createdBy INTEGER
|
||||
);
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
var _ store.ServiceStore = (*ServiceStore)(nil)
|
||||
|
||||
// NewServiceStore returns a new ServiceStore.
|
||||
func NewServiceStore(db *sqlx.DB) *ServiceStore {
|
||||
return &ServiceStore{db}
|
||||
}
|
||||
|
||||
// ServiceStore implements a ServiceStore backed by a relational
|
||||
// database.
|
||||
type ServiceStore struct {
|
||||
db *sqlx.DB
|
||||
}
|
||||
|
||||
// Find finds the service by id.
|
||||
func (s *ServiceStore) Find(ctx context.Context, id int64) (*types.Service, error) {
|
||||
dst := new(types.Service)
|
||||
if err := s.db.GetContext(ctx, dst, serviceSelectID, id); err != nil {
|
||||
return nil, processSQLErrorf(err, "Select by id query failed")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// FindUID finds the service by uid.
|
||||
func (s *ServiceStore) FindUID(ctx context.Context, uid string) (*types.Service, error) {
|
||||
dst := new(types.Service)
|
||||
if err := s.db.GetContext(ctx, dst, serviceSelectUID, uid); err != nil {
|
||||
return nil, processSQLErrorf(err, "Select by uid query failed")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// Create saves the service.
|
||||
func (s *ServiceStore) Create(ctx context.Context, sa *types.Service) error {
|
||||
query, arg, err := s.db.BindNamed(serviceInsert, sa)
|
||||
if err != nil {
|
||||
return processSQLErrorf(err, "Failed to bind service object")
|
||||
}
|
||||
|
||||
if err = s.db.QueryRowContext(ctx, query, arg...).Scan(&sa.ID); err != nil {
|
||||
return processSQLErrorf(err, "Insert query failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update updates the service.
|
||||
func (s *ServiceStore) Update(ctx context.Context, sa *types.Service) error {
|
||||
query, arg, err := s.db.BindNamed(serviceUpdate, sa)
|
||||
if err != nil {
|
||||
return processSQLErrorf(err, "Failed to bind service object")
|
||||
}
|
||||
|
||||
if _, err = s.db.ExecContext(ctx, query, arg...); err != nil {
|
||||
return processSQLErrorf(err, "Update query failed")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete deletes the service.
|
||||
func (s *ServiceStore) Delete(ctx context.Context, id int64) error {
|
||||
tx, err := s.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return processSQLErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer func(tx *sql.Tx) {
|
||||
_ = tx.Rollback()
|
||||
}(tx)
|
||||
// delete the service
|
||||
if _, err = tx.ExecContext(ctx, serviceDelete, id); err != nil {
|
||||
return processSQLErrorf(err, "The delete query failed")
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
// List returns a list of service for a specific parent.
|
||||
func (s *ServiceStore) List(ctx context.Context) ([]*types.Service, error) {
|
||||
dst := []*types.Service{}
|
||||
|
||||
err := s.db.SelectContext(ctx, &dst, serviceSelect)
|
||||
if err != nil {
|
||||
return nil, processSQLErrorf(err, "Failed executing default list query")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// Count returns a count of service for a specific parent.
|
||||
func (s *ServiceStore) Count(ctx context.Context) (int64, error) {
|
||||
var count int64
|
||||
err := s.db.QueryRowContext(ctx, serviceCount).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, processSQLErrorf(err, "Failed executing count query")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
const serviceCount = `
|
||||
SELECT count(*)
|
||||
FROM principals
|
||||
WHERE principal_type = "service"
|
||||
`
|
||||
|
||||
const serviceBase = `
|
||||
SELECT
|
||||
principal_id
|
||||
,principal_uid
|
||||
,principal_name
|
||||
,principal_blocked
|
||||
,principal_salt
|
||||
,principal_created
|
||||
,principal_updated
|
||||
FROM principals
|
||||
`
|
||||
|
||||
const serviceSelect = serviceBase + `
|
||||
WHERE principal_type = "service"
|
||||
ORDER BY principal_name ASC
|
||||
`
|
||||
|
||||
const serviceSelectID = serviceBase + `
|
||||
WHERE principal_type = "service" AND principal_id = $1
|
||||
`
|
||||
|
||||
const serviceSelectUID = serviceBase + `
|
||||
WHERE principal_type = "service" AND principal_uid = $1
|
||||
`
|
||||
|
||||
const serviceDelete = `
|
||||
DELETE FROM principals
|
||||
WHERE principal_type = "service" AND principal_id = $1
|
||||
`
|
||||
|
||||
const serviceInsert = `
|
||||
INSERT INTO principals (
|
||||
principal_type
|
||||
,principal_uid
|
||||
,principal_name
|
||||
,principal_admin
|
||||
,principal_blocked
|
||||
,principal_salt
|
||||
,principal_created
|
||||
,principal_updated
|
||||
) values (
|
||||
"service"
|
||||
,:principal_uid
|
||||
,:principal_name
|
||||
,:principal_admin
|
||||
,:principal_blocked
|
||||
,:principal_salt
|
||||
,:principal_created
|
||||
,:principal_updated
|
||||
) RETURNING principal_id
|
||||
`
|
||||
|
||||
const serviceUpdate = `
|
||||
UPDATE principals
|
||||
SET
|
||||
principal_name = :principal_name
|
||||
,principal_admin = :principal_admin
|
||||
,principal_blocked = :principal_blocked
|
||||
,principal_updated = :principal_updated
|
||||
WHERE principal_type = "service" AND principal_id = :principal_id
|
||||
`
|
|
@ -181,8 +181,8 @@ const serviceAccountUpdate = `
|
|||
UPDATE principals
|
||||
SET
|
||||
principal_name = :principal_name
|
||||
,:principal_blocked = :principal_blocked
|
||||
,:principal_salt = :principal_salt
|
||||
,:principal_updated = :principal_updated
|
||||
,principal_blocked = :principal_blocked
|
||||
,principal_salt = :principal_salt
|
||||
,principal_updated = :principal_updated
|
||||
WHERE principal_type = "serviceaccount" AND principal_id = :principal_id
|
||||
`
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/internal/store/database/mutex"
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
var _ store.ServiceStore = (*ServiceStoreSync)(nil)
|
||||
|
||||
// NewServiceStoreSync returns a new ServiceStoreSync.
|
||||
func NewServiceStoreSync(base *ServiceStore) *ServiceStoreSync {
|
||||
return &ServiceStoreSync{base}
|
||||
}
|
||||
|
||||
// ServiceStoreSync synchronizes read and write access to the
|
||||
// service store. This prevents race conditions when the database
|
||||
// type is sqlite3.
|
||||
type ServiceStoreSync struct {
|
||||
base *ServiceStore
|
||||
}
|
||||
|
||||
// Find finds the service by id.
|
||||
func (s *ServiceStoreSync) Find(ctx context.Context, id int64) (*types.Service, error) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.Find(ctx, id)
|
||||
}
|
||||
|
||||
// FindUID finds the service by uid.
|
||||
func (s *ServiceStoreSync) FindUID(ctx context.Context, uid string) (*types.Service, error) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.FindUID(ctx, uid)
|
||||
}
|
||||
|
||||
// Create saves the service.
|
||||
func (s *ServiceStoreSync) Create(ctx context.Context, sa *types.Service) error {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.Create(ctx, sa)
|
||||
}
|
||||
|
||||
// Update updates the service.
|
||||
func (s *ServiceStoreSync) Update(ctx context.Context, sa *types.Service) error {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.Update(ctx, sa)
|
||||
}
|
||||
|
||||
// Delete deletes the service.
|
||||
func (s *ServiceStoreSync) Delete(ctx context.Context, id int64) error {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.Delete(ctx, id)
|
||||
}
|
||||
|
||||
// List returns a list of service for a specific parent.
|
||||
func (s *ServiceStoreSync) List(ctx context.Context) ([]*types.Service, error) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.List(ctx)
|
||||
}
|
||||
|
||||
// Count returns a count of service for a specific parent.
|
||||
func (s *ServiceStoreSync) Count(ctx context.Context) (int64, error) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.Count(ctx)
|
||||
}
|
|
@ -23,6 +23,7 @@ var WireSet = wire.NewSet(
|
|||
ProvideDatabase,
|
||||
ProvideUserStore,
|
||||
ProvideServiceAccountStore,
|
||||
ProvideServiceStore,
|
||||
ProvideSpaceStore,
|
||||
ProvideRepoStore,
|
||||
ProvideTokenStore,
|
||||
|
@ -61,6 +62,18 @@ func ProvideServiceAccountStore(db *sqlx.DB) store.ServiceAccountStore {
|
|||
}
|
||||
}
|
||||
|
||||
// ProvideServiceStore provides a service store.
|
||||
func ProvideServiceStore(db *sqlx.DB) store.ServiceStore {
|
||||
switch db.DriverName() {
|
||||
case postgres:
|
||||
return NewServiceStore(db)
|
||||
default:
|
||||
return NewServiceStoreSync(
|
||||
NewServiceStore(db),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ProvideSpaceStore provides a space store.
|
||||
func ProvideSpaceStore(db *sqlx.DB) store.SpaceStore {
|
||||
switch db.DriverName() {
|
||||
|
|
|
@ -64,6 +64,30 @@ type (
|
|||
Count(ctx context.Context, parentType enum.ParentResourceType, parentID int64) (int64, error)
|
||||
}
|
||||
|
||||
// ServiceStore defines the service data storage.
|
||||
ServiceStore interface {
|
||||
// Find finds the service by id.
|
||||
Find(ctx context.Context, id int64) (*types.Service, error)
|
||||
|
||||
// FindUID finds the service by uid.
|
||||
FindUID(ctx context.Context, uid string) (*types.Service, error)
|
||||
|
||||
// Create saves the service.
|
||||
Create(ctx context.Context, sa *types.Service) error
|
||||
|
||||
// Update updates the service details.
|
||||
Update(ctx context.Context, sa *types.Service) error
|
||||
|
||||
// Delete deletes the service.
|
||||
Delete(ctx context.Context, id int64) error
|
||||
|
||||
// List returns a list of all services.
|
||||
List(ctx context.Context) ([]*types.Service, error)
|
||||
|
||||
// Count returns a count of all services.
|
||||
Count(ctx context.Context) (int64, error)
|
||||
}
|
||||
|
||||
// SpaceStore defines the space data storage.
|
||||
SpaceStore interface {
|
||||
// Find the space by id.
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package check
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
minPasswordLength = 1
|
||||
maxPasswordLength = 128
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrPasswordLength is returned when the password
|
||||
// is outside of the allowed length.
|
||||
ErrPasswordLength = &ValidationError{
|
||||
fmt.Sprintf("Password has to be within %d and %d characters", minPasswordLength, maxPasswordLength),
|
||||
}
|
||||
)
|
||||
|
||||
// Password returns true if the Password is valid.
|
||||
// TODO: add proper password checks.
|
||||
func Password(pw string) error {
|
||||
// validate length
|
||||
l := len(pw)
|
||||
if l < minPasswordLength || l > maxPasswordLength {
|
||||
return ErrPasswordLength
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2021 Harness Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Polyform Free Trial License
|
||||
// that can be found in the LICENSE.md file for this repository.
|
||||
|
||||
package check
|
||||
|
||||
import (
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
// Service returns true if the Service if valid.
|
||||
func Service(sa *types.Service) error {
|
||||
// validate UID
|
||||
if err := UID(sa.UID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// validate name
|
||||
if err := Name(sa.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -22,12 +22,12 @@ func ServiceAccount(sa *types.ServiceAccount) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// verify name
|
||||
// validate name
|
||||
if err := Name(sa.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// verify parentType is valid
|
||||
// validate parentType
|
||||
if sa.ParentType != enum.ParentResourceTypeRepo && sa.ParentType != enum.ParentResourceTypeSpace {
|
||||
return ErrServiceAccountParentTypeIsInvalid
|
||||
}
|
||||
|
|
|
@ -17,13 +17,13 @@ const (
|
|||
|
||||
var (
|
||||
// ErrEmailLen is returned when the email address
|
||||
// exceeds the maximum number of characters.
|
||||
// exceeds the range of allowed number of characters.
|
||||
ErrEmailLen = &ValidationError{
|
||||
fmt.Sprintf("Email address has to be within %d and %d characters", minEmailLength, maxEmailLength),
|
||||
}
|
||||
)
|
||||
|
||||
// User returns true if the User if valid.
|
||||
// User returns true if the User is valid.
|
||||
func User(user *types.User) error {
|
||||
// validate UID
|
||||
if err := UID(user.UID); err != nil {
|
||||
|
|
|
@ -64,4 +64,11 @@ type Config struct {
|
|||
ContentSecurityPolicy string `envconfig:"GITNESS_HTTP_CONTENT_SECURITY_POLICY"`
|
||||
ReferrerPolicy string `envconfig:"GITNESS_HTTP_REFERRER_POLICY"`
|
||||
}
|
||||
|
||||
// Admin defines admin user params (no admin setup if either is empty)
|
||||
Admin struct {
|
||||
Name string `envconfig:"GITNESS_ADMIN_NAME"`
|
||||
Email string `envconfig:"GITNESS_ADMIN_EMAIL"`
|
||||
Password string `envconfig:"GITNESS_ADMIN_PASSWORD"`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ const (
|
|||
ResourceTypeRepo ResourceType = "REPOSITORY"
|
||||
ResourceTypeUser ResourceType = "USER"
|
||||
ResourceTypeServiceAccount ResourceType = "SERVICEACCOUNT"
|
||||
ResourceTypeService ResourceType = "SERVICE"
|
||||
// ResourceType_Branch ResourceType = "BRANCH"
|
||||
)
|
||||
|
||||
|
@ -51,10 +52,21 @@ const (
|
|||
|
||||
const (
|
||||
/*
|
||||
----- REPOSITORY -----
|
||||
----- SERVICE ACCOUNT -----
|
||||
*/
|
||||
PermissionServiceAccountCreate Permission = "serviceaccount_create"
|
||||
PermissionServiceAccountView Permission = "serviceaccount_view"
|
||||
PermissionServiceAccountEdit Permission = "serviceaccount_edit"
|
||||
PermissionServiceAccountDelete Permission = "serviceaccount_delete"
|
||||
)
|
||||
|
||||
const (
|
||||
/*
|
||||
----- SERVICE -----
|
||||
*/
|
||||
PermissionServiceCreate Permission = "service_create"
|
||||
PermissionServiceView Permission = "service_view"
|
||||
PermissionServiceEdit Permission = "service_edit"
|
||||
PermissionServiceDelete Permission = "service_delete"
|
||||
PermissionServiceEditAdmin Permission = "service_editadmin"
|
||||
)
|
||||
|
|
|
@ -14,6 +14,7 @@ const (
|
|||
|
||||
// Represents a path to a resource (e.g. space) that can be used to address the resource.
|
||||
type Path struct {
|
||||
// TODO: int64 ID doesn't match DB
|
||||
ID int64 `db:"path_id" json:"id"`
|
||||
Value string `db:"path_value" json:"value"`
|
||||
IsAlias bool `db:"path_isAlias" json:"isAlias"`
|
||||
|
|
|
@ -10,7 +10,7 @@ import "github.com/harness/gitness/types/enum"
|
|||
type (
|
||||
// Represents the identity of an acting entity (User, ServiceAccount, Service).
|
||||
Principal struct {
|
||||
// ID is the internal identifier of a principal (primary key)
|
||||
// TODO: int64 ID doesn't match DB
|
||||
ID int64 `db:"principal_id" json:"-"`
|
||||
UID string `db:"principal_uid" json:"uid"`
|
||||
Type enum.PrincipalType `db:"principal_type" json:"type"`
|
||||
|
@ -61,7 +61,7 @@ func PrincipalFromService(s *Service) *Principal {
|
|||
UID: s.UID,
|
||||
Type: enum.PrincipalTypeService,
|
||||
Name: s.Name,
|
||||
Admin: true,
|
||||
Admin: s.Admin,
|
||||
Blocked: s.Blocked,
|
||||
Salt: s.Salt,
|
||||
Created: s.Created,
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
|
||||
// Repository represents a code repository.
|
||||
type Repository struct {
|
||||
// Core properties
|
||||
// TODO: int64 ID doesn't match DB
|
||||
ID int64 `db:"repo_id" json:"id"`
|
||||
SpaceID int64 `db:"repo_spaceId" json:"spaceId"`
|
||||
PathName string `db:"repo_pathName" json:"pathName"`
|
||||
|
|
|
@ -5,12 +5,10 @@
|
|||
// Package types defines common data structures.
|
||||
package types
|
||||
|
||||
import "github.com/harness/gitness/types/enum"
|
||||
|
||||
type (
|
||||
// Service is a principal representing a different internal service that runs alongside gitness.
|
||||
Service struct {
|
||||
// Fields from Principal (without admin, as it's always admin for now)
|
||||
// Fields from Principal
|
||||
ID int64 `db:"principal_id" json:"-"`
|
||||
UID string `db:"principal_uid" json:"uid"`
|
||||
Name string `db:"principal_name" json:"name"`
|
||||
|
@ -20,12 +18,4 @@ type (
|
|||
Created int64 `db:"principal_created" json:"created"`
|
||||
Updated int64 `db:"principal_updated" json:"updated"`
|
||||
}
|
||||
|
||||
// ServiceAccountInput store details used to
|
||||
// create or update a service account.
|
||||
ServiceInput struct {
|
||||
Name *string `json:"name"`
|
||||
ParentType *enum.ParentResourceType `json:"parentType"`
|
||||
ParentID *int64 `json:"parentId"`
|
||||
}
|
||||
)
|
||||
|
|
|
@ -21,6 +21,7 @@ https://stackoverflow.com/questions/4048151/what-are-the-options-for-storing-hie
|
|||
https://www.slideshare.net/billkarwin/models-for-hierarchical-data
|
||||
*/
|
||||
type Space struct {
|
||||
// TODO: int64 ID doesn't match DB
|
||||
ID int64 `db:"space_id" json:"id"`
|
||||
ParentID int64 `db:"space_parentId" json:"parentId"`
|
||||
PathName string `db:"space_pathName" json:"pathName"`
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
// Represents server side infos stored for tokens we distribute.
|
||||
type Token struct {
|
||||
// TODO: int64 ID doesn't match DB
|
||||
ID int64 `db:"token_id" json:"id"`
|
||||
Type enum.TokenType `db:"token_type" json:"type"`
|
||||
Name string `db:"token_name" json:"name"`
|
||||
|
|
Loading…
Reference in New Issue