mirror of https://github.com/harness/drone.git
[MAINT] initial config for ci linter (#17)
* initial config for ci linter * more linter work * linter errors fix * linter errors fix * linter conf minor changesjobatzil/rename
parent
11f3aa532c
commit
f03528e862
|
@ -25,6 +25,7 @@ fi
|
|||
# The files are then git-added
|
||||
FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep .go | sed 's| |\\ |g')
|
||||
if [ -n "$FILES" ]; then
|
||||
make format
|
||||
make lint
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error running make check - please fix before committing"
|
||||
|
|
|
@ -0,0 +1,266 @@
|
|||
## Golden config for golangci-lint v1.49.0
|
||||
|
||||
run:
|
||||
# Timeout for analysis, e.g. 30s, 5m.
|
||||
# Default: 1m
|
||||
timeout: 3m
|
||||
|
||||
|
||||
# This file contains only configs which differ from defaults.
|
||||
# All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml
|
||||
linters-settings:
|
||||
cyclop:
|
||||
# The maximal code complexity to report.
|
||||
# Default: 10
|
||||
max-complexity: 30
|
||||
# The maximal average package complexity.
|
||||
# If it's higher than 0.0 (float) the check is enabled
|
||||
# Default: 0.0
|
||||
package-average: 10.0
|
||||
|
||||
errcheck:
|
||||
# Report about not checking of errors in type assertions: `a := b.(MyStruct)`.
|
||||
# Such cases aren't reported by default.
|
||||
# Default: false
|
||||
check-type-assertions: true
|
||||
|
||||
funlen:
|
||||
# Checks the number of lines in a function.
|
||||
# If lower than 0, disable the check.
|
||||
# Default: 60
|
||||
lines: 100
|
||||
# Checks the number of statements in a function.
|
||||
# If lower than 0, disable the check.
|
||||
# Default: 40
|
||||
statements: 50
|
||||
|
||||
gocognit:
|
||||
# Minimal code complexity to report
|
||||
# Default: 30 (but we recommend 10-20)
|
||||
min-complexity: 20
|
||||
|
||||
gocritic:
|
||||
# Settings passed to gocritic.
|
||||
# The settings key is the name of a supported gocritic checker.
|
||||
# The list of supported checkers can be find in https://go-critic.github.io/overview.
|
||||
settings:
|
||||
captLocal:
|
||||
# Whether to restrict checker to params only.
|
||||
# Default: true
|
||||
paramsOnly: false
|
||||
underef:
|
||||
# Whether to skip (*x).method() calls where x is a pointer receiver.
|
||||
# Default: true
|
||||
skipRecvDeref: false
|
||||
|
||||
gomnd:
|
||||
# List of function patterns to exclude from analysis.
|
||||
# Values always ignored: `time.Date`
|
||||
# Default: []
|
||||
ignored-functions:
|
||||
- os.Chmod
|
||||
- os.Mkdir
|
||||
- os.MkdirAll
|
||||
- os.OpenFile
|
||||
- os.WriteFile
|
||||
- prometheus.ExponentialBuckets
|
||||
- prometheus.ExponentialBucketsRange
|
||||
- prometheus.LinearBuckets
|
||||
- strconv.FormatFloat
|
||||
- strconv.FormatInt
|
||||
- strconv.FormatUint
|
||||
- strconv.ParseFloat
|
||||
- strconv.ParseInt
|
||||
- strconv.ParseUint
|
||||
|
||||
gomodguard:
|
||||
blocked:
|
||||
# List of blocked modules.
|
||||
# Default: []
|
||||
modules:
|
||||
- github.com/golang/protobuf:
|
||||
recommendations:
|
||||
- google.golang.org/protobuf
|
||||
reason: "see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules"
|
||||
- github.com/satori/go.uuid:
|
||||
recommendations:
|
||||
- github.com/google/uuid
|
||||
reason: "satori's package is not maintained"
|
||||
- github.com/gofrs/uuid:
|
||||
recommendations:
|
||||
- github.com/google/uuid
|
||||
reason: "see recommendation from dev-infra team: https://confluence.gtforge.com/x/gQI6Aw"
|
||||
|
||||
govet:
|
||||
# Enable all analyzers.
|
||||
# Default: false
|
||||
enable-all: true
|
||||
# Disable analyzers by name.
|
||||
# Run `go tool vet help` to see all analyzers.
|
||||
# Default: []
|
||||
disable:
|
||||
- fieldalignment # too strict
|
||||
# Settings per analyzer.
|
||||
settings:
|
||||
shadow:
|
||||
# Whether to be strict about shadowing; can be noisy.
|
||||
# Default: false
|
||||
strict: true
|
||||
|
||||
nakedret:
|
||||
# Make an issue if func has more lines of code than this setting, and it has naked returns.
|
||||
# Default: 30
|
||||
max-func-lines: 0
|
||||
|
||||
nolintlint:
|
||||
# Exclude following linters from requiring an explanation.
|
||||
# Default: []
|
||||
allow-no-explanation: [ funlen, gocognit, lll ]
|
||||
# Enable to require an explanation of nonzero length after each nolint directive.
|
||||
# Default: false
|
||||
require-explanation: true
|
||||
# Enable to require nolint directives to mention the specific linter being suppressed.
|
||||
# Default: false
|
||||
require-specific: true
|
||||
|
||||
rowserrcheck:
|
||||
# database/sql is always checked
|
||||
# Default: []
|
||||
packages:
|
||||
- github.com/jmoiron/sqlx
|
||||
|
||||
tenv:
|
||||
# The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.
|
||||
# Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked.
|
||||
# Default: false
|
||||
all: true
|
||||
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
## enabled by default
|
||||
- errcheck # checking for unchecked errors, these unchecked errors can be critical bugs in some cases
|
||||
- gosimple # specializes in simplifying a code
|
||||
- govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
|
||||
- ineffassign # detects when assignments to existing variables are not used
|
||||
- staticcheck # is a go vet on steroids, applying a ton of static analysis checks
|
||||
- typecheck # like the front-end of a Go compiler, parses and type-checks Go code
|
||||
- unused # checks for unused constants, variables, functions and types
|
||||
## disabled by default
|
||||
- asasalint # checks for pass []any as any in variadic func(...any)
|
||||
- asciicheck # checks that your code does not contain non-ASCII identifiers
|
||||
- bidichk # checks for dangerous unicode character sequences
|
||||
- bodyclose # checks whether HTTP response body is closed successfully
|
||||
#- contextcheck # checks the function whether use a non-inherited context # TODO: enable after golangci-lint uses https://github.com/sylvia7788/contextcheck/releases/tag/v1.0.7
|
||||
- cyclop # checks function and package cyclomatic complexity
|
||||
# - dupl # tool for code clone detection
|
||||
- durationcheck # checks for two durations multiplied together
|
||||
- errname # checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error
|
||||
- errorlint # finds code that will cause problems with the error wrapping scheme introduced in Go 1.13
|
||||
- execinquery # checks query string in Query function which reads your Go src files and warning it finds
|
||||
- exhaustive # checks exhaustiveness of enum switch statements
|
||||
- exportloopref # checks for pointers to enclosing loop variables
|
||||
- forbidigo # forbids identifiers
|
||||
- funlen # tool for detection of long functions
|
||||
#- gochecknoglobals # checks that no global variables exist
|
||||
#- gochecknoinits # checks that no init functions are present in Go code
|
||||
- gocognit # computes and checks the cognitive complexity of functions
|
||||
- goconst # finds repeated strings that could be replaced by a constant
|
||||
- gocritic # provides diagnostics that check for bugs, performance and style issues
|
||||
- gocyclo # computes and checks the cyclomatic complexity of functions
|
||||
- godot # checks if comments end in a period
|
||||
- goimports # in addition to fixing imports, goimports also formats your code in the same style as gofmt
|
||||
- gomnd # detects magic numbers
|
||||
- gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod
|
||||
- gomodguard # allow and block lists linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations
|
||||
- goprintffuncname # checks that printf-like functions are named with f at the end
|
||||
- gosec # inspects source code for security problems
|
||||
- lll # reports long lines
|
||||
- makezero # finds slice declarations with non-zero initial length
|
||||
- nakedret # finds naked returns in functions greater than a specified function length
|
||||
- nestif # reports deeply nested if statements
|
||||
- nilerr # finds the code that returns nil even if it checks that the error is not nil
|
||||
- nilnil # checks that there is no simultaneous return of nil error and an invalid value
|
||||
- noctx # finds sending http request without context.Context
|
||||
- nolintlint # reports ill-formed or insufficient nolint directives
|
||||
- nonamedreturns # reports all named returns
|
||||
- nosprintfhostport # checks for misuse of Sprintf to construct a host with port in a URL
|
||||
- predeclared # finds code that shadows one of Go's predeclared identifiers
|
||||
- promlinter # checks Prometheus metrics naming via promlint
|
||||
- reassign # checks that package variables are not reassigned
|
||||
- revive # fast, configurable, extensible, flexible, and beautiful linter for Go, drop-in replacement of golint
|
||||
- rowserrcheck # checks whether Err of rows is checked successfully
|
||||
- sqlclosecheck # checks that sql.Rows and sql.Stmt are closed
|
||||
- stylecheck # is a replacement for golint
|
||||
- tenv # detects using os.Setenv instead of t.Setenv since Go1.17
|
||||
#- testpackage # makes you use a separate _test package
|
||||
- tparallel # detects inappropriate usage of t.Parallel() method in your Go test codes
|
||||
- unconvert # removes unnecessary type conversions
|
||||
- unparam # reports unused function parameters
|
||||
- usestdlibvars # detects the possibility to use variables/constants from the Go standard library
|
||||
- wastedassign # finds wasted assignment statements
|
||||
- whitespace # detects leading and trailing whitespace
|
||||
|
||||
## you may want to enable
|
||||
#- decorder # checks declaration order and count of types, constants, variables and functions
|
||||
#- exhaustruct # checks if all structure fields are initialized
|
||||
#- gci # controls golang package import order and makes it always deterministic
|
||||
#- godox # detects FIXME, TODO and other comment keywords
|
||||
#- goheader # checks is file header matches to pattern
|
||||
#- interfacebloat # checks the number of methods inside an interface
|
||||
#- ireturn # accept interfaces, return concrete types
|
||||
#- prealloc # [premature optimization, but can be used in some cases] finds slice declarations that could potentially be preallocated
|
||||
#- varnamelen # [great idea, but too many false positives] checks that the length of a variable's name matches its scope
|
||||
#- wrapcheck # checks that errors returned from external packages are wrapped
|
||||
|
||||
## disabled
|
||||
#- containedctx # detects struct contained context.Context field
|
||||
#- depguard # [replaced by gomodguard] checks if package imports are in a list of acceptable packages
|
||||
#- dogsled # checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
|
||||
#- errchkjson # [don't see profit + I'm against of omitting errors like in the first example https://github.com/breml/errchkjson] checks types passed to the json encoding functions. Reports unsupported types and optionally reports occasions, where the check for the returned error can be omitted
|
||||
#- forcetypeassert # [replaced by errcheck] finds forced type assertions
|
||||
#- goerr113 # [too strict] checks the errors handling expressions
|
||||
#- gofmt # [replaced by goimports] checks whether code was gofmt-ed
|
||||
#- gofumpt # [replaced by goimports, gofumports is not available yet] checks whether code was gofumpt-ed
|
||||
#- grouper # analyzes expression groups
|
||||
#- importas # enforces consistent import aliases
|
||||
#- logrlint # [owner archived repository] checks logr arguments
|
||||
#- maintidx # measures the maintainability index of each function
|
||||
#- misspell # [useless] finds commonly misspelled English words in comments
|
||||
#- nlreturn # [too strict and mostly code is not more readable] checks for a new line before return and branch statements to increase code clarity
|
||||
#- paralleltest # [too many false positives] detects missing usage of t.Parallel() method in your Go test
|
||||
#- tagliatelle # checks the struct tags
|
||||
#- thelper # detects golang test helpers without t.Helper() call and checks the consistency of test helpers
|
||||
#- wsl # [too strict and mostly code is not more readable] whitespace linter forces you to use empty lines
|
||||
|
||||
|
||||
issues:
|
||||
# Maximum count of issues with the same text.
|
||||
# Set to 0 to disable.
|
||||
# Default: 3
|
||||
max-same-issues: 50
|
||||
|
||||
exclude-rules:
|
||||
- source: "^//\\s*go:generate\\s"
|
||||
linters: [ lll ]
|
||||
- source: "(noinspection|TODO)"
|
||||
linters: [ godot ]
|
||||
- source: "//noinspection"
|
||||
linters: [ gocritic ]
|
||||
- source: "^\\s+if _, ok := err\\.\\([^.]+\\.InternalError\\); ok {"
|
||||
linters: [ errorlint ]
|
||||
- path: "^cli/"
|
||||
linters: [forbidigo]
|
||||
- text: "mnd: Magic number: \\d"
|
||||
linters:
|
||||
- gomnd
|
||||
- path: "_test\\.go"
|
||||
linters:
|
||||
- bodyclose
|
||||
- dupl
|
||||
- funlen
|
||||
- goconst
|
||||
- gosec
|
||||
- noctx
|
||||
- wrapcheck
|
|
@ -16,11 +16,10 @@ import (
|
|||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
)
|
||||
|
||||
// application name
|
||||
var application = "gitness"
|
||||
|
||||
// application description
|
||||
var description = "description goes here" // TODO edit this application description
|
||||
const (
|
||||
application = "gitness"
|
||||
description = "description goes here" // TODO edit this application description
|
||||
)
|
||||
|
||||
// Command parses the command line arguments and then executes a
|
||||
// subcommand program.
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package cli
|
||||
|
||||
const (
|
||||
OwnerReadWrite = 0600
|
||||
)
|
10
cli/login.go
10
cli/login.go
|
@ -5,8 +5,10 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/cli/util"
|
||||
"github.com/harness/gitness/client"
|
||||
|
@ -20,8 +22,10 @@ type loginCommand struct {
|
|||
|
||||
func (c *loginCommand) run(*kingpin.ParseContext) error {
|
||||
username, password := util.Credentials()
|
||||
client := client.New(c.server)
|
||||
token, err := client.Login(username, password)
|
||||
httpClient := client.New(c.server)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
token, err := httpClient.Login(ctx, username, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -34,7 +38,7 @@ func (c *loginCommand) run(*kingpin.ParseContext) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(path, data, 0600)
|
||||
return ioutil.WriteFile(path, data, OwnerReadWrite)
|
||||
}
|
||||
|
||||
// helper function to register the logout command.
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/cli/util"
|
||||
"github.com/harness/gitness/client"
|
||||
|
@ -19,8 +21,10 @@ type registerCommand struct {
|
|||
|
||||
func (c *registerCommand) run(*kingpin.ParseContext) error {
|
||||
username, password := util.Credentials()
|
||||
client := client.New(c.server)
|
||||
token, err := client.Register(username, password)
|
||||
httpClient := client.New(c.server)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
token, err := httpClient.Register(ctx, username, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -33,7 +37,7 @@ func (c *registerCommand) run(*kingpin.ParseContext) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(path, data, 0600)
|
||||
return ioutil.WriteFile(path, data, OwnerReadWrite)
|
||||
}
|
||||
|
||||
// helper function to register the register command.
|
||||
|
|
|
@ -21,7 +21,6 @@ var legacy = map[string]string{
|
|||
// load returns the system configuration from the
|
||||
// host environment.
|
||||
func load() (*types.Config, error) {
|
||||
|
||||
// loop through legacy environment variable and, if set
|
||||
// rewrite to the new variable name.
|
||||
for k, v := range legacy {
|
||||
|
|
|
@ -23,7 +23,7 @@ func (c *swaggerCommand) run(*kingpin.ParseContext) error {
|
|||
os.Stdout.Write(data)
|
||||
return nil
|
||||
}
|
||||
return ioutil.WriteFile(c.path, data, 0600)
|
||||
return ioutil.WriteFile(c.path, data, OwnerReadWrite)
|
||||
}
|
||||
|
||||
// helper function to register the swagger command.
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
package token
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/cli/util"
|
||||
|
||||
|
@ -22,7 +24,9 @@ func (c *command) run(*kingpin.ParseContext) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
token, err := client.Token()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
token, err := client.Token(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -44,5 +48,4 @@ func Register(app *kingpin.Application) {
|
|||
|
||||
cmd.Flag("json", "json encode the output").
|
||||
BoolVar(&c.json)
|
||||
|
||||
}
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/cli/util"
|
||||
|
||||
|
@ -30,7 +32,9 @@ func (c *command) run(*kingpin.ParseContext) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user, err := client.Self()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
user, err := client.Self(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
package users
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/cli/util"
|
||||
"github.com/harness/gitness/types"
|
||||
|
@ -33,7 +35,9 @@ func (c *createCommand) run(*kingpin.ParseContext) error {
|
|||
Email: c.email,
|
||||
Password: util.Password(),
|
||||
}
|
||||
user, err := client.UserCreate(in)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
user, err := client.UserCreate(ctx, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -49,7 +53,7 @@ func (c *createCommand) run(*kingpin.ParseContext) error {
|
|||
return tmpl.Execute(os.Stdout, user)
|
||||
}
|
||||
|
||||
// helper function registers the user create command
|
||||
// helper function registers the user create command.
|
||||
func registerCreate(app *kingpin.CmdClause) {
|
||||
c := new(createCommand)
|
||||
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
package users
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/cli/util"
|
||||
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
|
@ -19,10 +22,12 @@ func (c *deleteCommand) run(*kingpin.ParseContext) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return client.UserDelete(c.email)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
return client.UserDelete(ctx, c.email)
|
||||
}
|
||||
|
||||
// helper function registers the user delete command
|
||||
// helper function registers the user delete command.
|
||||
func registerDelete(app *kingpin.CmdClause) {
|
||||
c := new(deleteCommand)
|
||||
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
package users
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/cli/util"
|
||||
|
||||
|
@ -26,7 +28,9 @@ func (c *findCommand) run(*kingpin.ParseContext) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user, err := client.User(c.email)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
user, err := client.User(ctx, c.email)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -42,7 +46,7 @@ func (c *findCommand) run(*kingpin.ParseContext) error {
|
|||
return tmpl.Execute(os.Stdout, user)
|
||||
}
|
||||
|
||||
// helper function registers the user find command
|
||||
// helper function registers the user find command.
|
||||
func registerFind(app *kingpin.CmdClause) {
|
||||
c := new(findCommand)
|
||||
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
package users
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/drone/funcmap"
|
||||
"github.com/harness/gitness/cli/util"
|
||||
|
@ -34,7 +36,9 @@ func (c *listCommand) run(*kingpin.ParseContext) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
list, err := client.UserList(types.Params{
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
list, err := client.UserList(ctx, types.Params{
|
||||
Size: c.size,
|
||||
Page: c.page,
|
||||
})
|
||||
|
@ -58,7 +62,7 @@ func (c *listCommand) run(*kingpin.ParseContext) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// helper function registers the user list command
|
||||
// helper function registers the user list command.
|
||||
func registerList(app *kingpin.CmdClause) {
|
||||
c := new(listCommand)
|
||||
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
package users
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/harness/gitness/cli/util"
|
||||
"github.com/harness/gitness/types"
|
||||
|
@ -50,12 +52,16 @@ func (c *updateCommand) run(*kingpin.ParseContext) error {
|
|||
in.Admin = ptr.Bool(false)
|
||||
}
|
||||
if c.passgen {
|
||||
v := uniuri.NewLen(8)
|
||||
const maxRandomChars = 8
|
||||
v := uniuri.NewLen(maxRandomChars)
|
||||
in.Password = ptr.String(v)
|
||||
fmt.Printf("generated temporary password: %s\n", v)
|
||||
}
|
||||
|
||||
user, err := client.UserUpdate(c.id, in)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
|
||||
user, err := client.UserUpdate(ctx, c.id, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -71,7 +77,7 @@ func (c *updateCommand) run(*kingpin.ParseContext) error {
|
|||
return tmpl.Execute(os.Stdout, user)
|
||||
}
|
||||
|
||||
// helper function registers the user update command
|
||||
// helper function registers the user update command.
|
||||
func registerUpdate(app *kingpin.CmdClause) {
|
||||
c := new(updateCommand)
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/term"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -17,6 +16,8 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/term"
|
||||
|
||||
"github.com/harness/gitness/client"
|
||||
"github.com/harness/gitness/types"
|
||||
|
||||
|
@ -34,7 +35,7 @@ func Client() (*client.HTTPClient, error) {
|
|||
return nil, err
|
||||
}
|
||||
token := new(types.Token)
|
||||
if err := json.Unmarshal(data, token); err != nil {
|
||||
if err = json.Unmarshal(data, token); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if time.Now().Unix() > token.Expires.Unix() {
|
||||
|
@ -72,7 +73,7 @@ func Username() string {
|
|||
// Password returns the password from stdin.
|
||||
func Password() string {
|
||||
fmt.Print("Enter Password: ")
|
||||
passwordb, _ := term.ReadPassword(int(syscall.Stdin))
|
||||
passwordb, _ := term.ReadPassword(syscall.Stdin)
|
||||
password := string(passwordb)
|
||||
|
||||
return strings.TrimSpace(password)
|
||||
|
|
|
@ -6,14 +6,16 @@ package client
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/rs/zerolog/log"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/version"
|
||||
)
|
||||
|
@ -56,24 +58,24 @@ func (c *HTTPClient) SetDebug(debug bool) {
|
|||
}
|
||||
|
||||
// Login authenticates the user and returns a JWT token.
|
||||
func (c *HTTPClient) Login(username, password string) (*types.Token, error) {
|
||||
func (c *HTTPClient) Login(ctx context.Context, username, password string) (*types.Token, error) {
|
||||
form := &url.Values{}
|
||||
form.Add("username", username)
|
||||
form.Add("password", password)
|
||||
out := new(types.UserToken)
|
||||
uri := fmt.Sprintf("%s/api/v1/login?return_user=true", c.base)
|
||||
err := c.post(uri, form, out)
|
||||
err := c.post(ctx, uri, form, out)
|
||||
return out.Token, err
|
||||
}
|
||||
|
||||
// Register registers a new user and returns a JWT token.
|
||||
func (c *HTTPClient) Register(username, password string) (*types.Token, error) {
|
||||
func (c *HTTPClient) Register(ctx context.Context, username, password string) (*types.Token, error) {
|
||||
form := &url.Values{}
|
||||
form.Add("username", username)
|
||||
form.Add("password", password)
|
||||
out := new(types.UserToken)
|
||||
uri := fmt.Sprintf("%s/api/v1/register?return_user=true", c.base)
|
||||
err := c.post(uri, form, out)
|
||||
err := c.post(ctx, uri, form, out)
|
||||
return out.Token, err
|
||||
}
|
||||
|
||||
|
@ -82,58 +84,58 @@ func (c *HTTPClient) Register(username, password string) (*types.Token, error) {
|
|||
//
|
||||
|
||||
// Self returns the currently authenticated user.
|
||||
func (c *HTTPClient) Self() (*types.User, error) {
|
||||
func (c *HTTPClient) Self(ctx context.Context) (*types.User, error) {
|
||||
out := new(types.User)
|
||||
uri := fmt.Sprintf("%s/api/v1/user", c.base)
|
||||
err := c.get(uri, out)
|
||||
err := c.get(ctx, uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// Token returns an oauth2 bearer token for the currently
|
||||
// authenticated user.
|
||||
func (c *HTTPClient) Token() (*types.Token, error) {
|
||||
func (c *HTTPClient) Token(ctx context.Context) (*types.Token, error) {
|
||||
out := new(types.Token)
|
||||
uri := fmt.Sprintf("%s/api/v1/user/token", c.base)
|
||||
err := c.post(uri, nil, out)
|
||||
err := c.post(ctx, uri, nil, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// User returns a user by ID or email.
|
||||
func (c *HTTPClient) User(key string) (*types.User, error) {
|
||||
func (c *HTTPClient) User(ctx context.Context, key string) (*types.User, error) {
|
||||
out := new(types.User)
|
||||
uri := fmt.Sprintf("%s/api/v1/users/%s", c.base, key)
|
||||
err := c.get(uri, out)
|
||||
err := c.get(ctx, uri, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// UserList returns a list of all registered users.
|
||||
func (c *HTTPClient) UserList(params types.Params) ([]*types.User, error) {
|
||||
out := []*types.User{}
|
||||
func (c *HTTPClient) UserList(ctx context.Context, params types.Params) ([]types.User, error) {
|
||||
out := []types.User{}
|
||||
uri := fmt.Sprintf("%s/api/v1/users?page=%d&per_page=%d", c.base, params.Page, params.Size)
|
||||
err := c.get(uri, &out)
|
||||
err := c.get(ctx, uri, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// UserCreate creates a new user account.
|
||||
func (c *HTTPClient) UserCreate(user *types.User) (*types.User, error) {
|
||||
func (c *HTTPClient) UserCreate(ctx context.Context, user *types.User) (*types.User, error) {
|
||||
out := new(types.User)
|
||||
uri := fmt.Sprintf("%s/api/v1/users", c.base)
|
||||
err := c.post(uri, user, out)
|
||||
err := c.post(ctx, uri, user, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// UserUpdate updates a user account by ID or email.
|
||||
func (c *HTTPClient) UserUpdate(key string, user *types.UserInput) (*types.User, error) {
|
||||
func (c *HTTPClient) UserUpdate(ctx context.Context, key string, user *types.UserInput) (*types.User, error) {
|
||||
out := new(types.User)
|
||||
uri := fmt.Sprintf("%s/api/v1/users/%s", c.base, key)
|
||||
err := c.patch(uri, user, out)
|
||||
err := c.patch(ctx, uri, user, out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// UserDelete deletes a user account by ID or email.
|
||||
func (c *HTTPClient) UserDelete(key string) error {
|
||||
func (c *HTTPClient) UserDelete(ctx context.Context, key string) error {
|
||||
uri := fmt.Sprintf("%s/api/v1/users/%s", c.base, key)
|
||||
err := c.delete(uri)
|
||||
err := c.delete(ctx, uri)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -142,32 +144,34 @@ func (c *HTTPClient) UserDelete(key string) error {
|
|||
//
|
||||
|
||||
// helper function for making an http GET request.
|
||||
func (c *HTTPClient) get(rawurl string, out interface{}) error {
|
||||
return c.do(rawurl, "GET", nil, out)
|
||||
func (c *HTTPClient) get(ctx context.Context, rawurl string, out interface{}) error {
|
||||
return c.do(ctx, rawurl, "GET", nil, out)
|
||||
}
|
||||
|
||||
// helper function for making an http POST request.
|
||||
func (c *HTTPClient) post(rawurl string, in, out interface{}) error {
|
||||
return c.do(rawurl, "POST", in, out)
|
||||
func (c *HTTPClient) post(ctx context.Context, rawurl string, in, out interface{}) error {
|
||||
return c.do(ctx, rawurl, "POST", in, out)
|
||||
}
|
||||
|
||||
// helper function for making an http PATCH request.
|
||||
func (c *HTTPClient) patch(rawurl string, in, out interface{}) error {
|
||||
return c.do(rawurl, "PATCH", in, out)
|
||||
func (c *HTTPClient) patch(ctx context.Context, rawurl string, in, out interface{}) error {
|
||||
return c.do(ctx, rawurl, "PATCH", in, out)
|
||||
}
|
||||
|
||||
// helper function for making an http DELETE request.
|
||||
func (c *HTTPClient) delete(rawurl string) error {
|
||||
return c.do(rawurl, "DELETE", nil, nil)
|
||||
func (c *HTTPClient) delete(ctx context.Context, rawurl string) error {
|
||||
return c.do(ctx, rawurl, "DELETE", nil, nil)
|
||||
}
|
||||
|
||||
// helper function to make an http request
|
||||
func (c *HTTPClient) do(rawurl, method string, in, out interface{}) error {
|
||||
// helper function to make an http request.
|
||||
func (c *HTTPClient) do(ctx context.Context, rawurl, method string, in, out interface{}) error {
|
||||
// executes the http request and returns the body as
|
||||
// and io.ReadCloser
|
||||
body, err := c.stream(rawurl, method, in, out)
|
||||
body, err := c.stream(ctx, rawurl, method, in, out)
|
||||
if body != nil {
|
||||
defer body.Close()
|
||||
defer func(body io.ReadCloser) {
|
||||
_ = body.Close()
|
||||
}(body)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -181,8 +185,8 @@ func (c *HTTPClient) do(rawurl, method string, in, out interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// helper function to stream an http request
|
||||
func (c *HTTPClient) stream(rawurl, method string, in, out interface{}) (io.ReadCloser, error) {
|
||||
// helper function to stream a http request.
|
||||
func (c *HTTPClient) stream(ctx context.Context, rawurl, method string, in, _ interface{}) (io.ReadCloser, error) {
|
||||
uri, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -192,21 +196,19 @@ func (c *HTTPClient) stream(rawurl, method string, in, out interface{}) (io.Read
|
|||
// write it to the body of the request.
|
||||
var buf io.ReadWriter
|
||||
if in != nil {
|
||||
buf = new(bytes.Buffer)
|
||||
buf = &bytes.Buffer{}
|
||||
// if posting form data, encode the form values.
|
||||
if form, ok := in.(*url.Values); ok {
|
||||
if _, err := io.WriteString(buf, form.Encode()); err != nil {
|
||||
if _, err = io.WriteString(buf, form.Encode()); err != nil {
|
||||
log.Err(err).Msg("in stream method")
|
||||
}
|
||||
} else {
|
||||
if err := json.NewEncoder(buf).Encode(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if err = json.NewEncoder(buf).Encode(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// creates a new http request.
|
||||
req, err := http.NewRequest(method, uri.String(), buf)
|
||||
req, err := http.NewRequestWithContext(ctx, method, uri.String(), buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -231,12 +233,14 @@ func (c *HTTPClient) stream(rawurl, method string, in, out interface{}) (io.Read
|
|||
}
|
||||
if c.debug {
|
||||
dump, _ := httputil.DumpResponse(resp, true)
|
||||
fmt.Println(method, rawurl)
|
||||
fmt.Println(string(dump))
|
||||
log.Debug().Msgf("method %s, url %s", method, rawurl)
|
||||
log.Debug().Msg(string(dump))
|
||||
}
|
||||
if resp.StatusCode > 299 {
|
||||
defer resp.Body.Close()
|
||||
err := new(remoteError)
|
||||
if resp.StatusCode >= http.StatusMultipleChoices {
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
err = &remoteError{}
|
||||
if decodeErr := json.NewDecoder(resp.Body).Decode(err); decodeErr != nil {
|
||||
return nil, decodeErr
|
||||
}
|
||||
|
|
|
@ -4,37 +4,41 @@
|
|||
|
||||
package client
|
||||
|
||||
import "github.com/harness/gitness/types"
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
// Client to access the remote APIs.
|
||||
type Client interface {
|
||||
// Login authenticates the user and returns a JWT token.
|
||||
Login(username, password string) (*types.Token, error)
|
||||
Login(ctx context.Context, username, password string) (*types.Token, error)
|
||||
|
||||
// Register registers a new user and returns a JWT token.
|
||||
Register(username, password string) (*types.Token, error)
|
||||
Register(ctx context.Context, username, password string) (*types.Token, error)
|
||||
|
||||
// Self returns the currently authenticated user.
|
||||
Self() (*types.User, error)
|
||||
Self(ctx context.Context) (*types.User, error)
|
||||
|
||||
// Token returns an oauth2 bearer token for the currently
|
||||
// authenticated user.
|
||||
Token() (*types.Token, error)
|
||||
Token(ctx context.Context) (*types.Token, error)
|
||||
|
||||
// User returns a user by ID or email.
|
||||
User(key string) (*types.User, error)
|
||||
User(ctx context.Context, key string) (*types.User, error)
|
||||
|
||||
// UserList returns a list of all registered users.
|
||||
UserList(params types.Params) ([]*types.User, error)
|
||||
UserList(ctx context.Context, params types.Params) ([]types.User, error)
|
||||
|
||||
// UserCreate creates a new user account.
|
||||
UserCreate(user *types.User) (*types.User, error)
|
||||
UserCreate(ctx context.Context, user *types.User) (*types.User, error)
|
||||
|
||||
// UserUpdate updates a user account by ID or email.
|
||||
UserUpdate(key string, input *types.UserInput) (*types.User, error)
|
||||
UserUpdate(ctx context.Context, key string, input *types.UserInput) (*types.User, error)
|
||||
|
||||
// UserDelete deletes a user account by ID or email.
|
||||
UserDelete(key string) error
|
||||
UserDelete(ctx context.Context, key string) error
|
||||
}
|
||||
|
||||
// remoteError store the error payload returned
|
||||
|
|
|
@ -8,18 +8,19 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/rs/zerolog/hlog"
|
||||
|
||||
"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/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotAuthenticated = errors.New("Not authenticated.")
|
||||
ErrNotAuthorized = errors.New("Not authorized.")
|
||||
ErrNotAuthenticated = errors.New("not authenticated")
|
||||
ErrNotAuthorized = errors.New("not authorized")
|
||||
)
|
||||
|
||||
type Guard struct {
|
||||
|
@ -30,9 +31,7 @@ func New(authorizer authz.Authorizer) *Guard {
|
|||
return &Guard{authorizer: authorizer}
|
||||
}
|
||||
|
||||
/*
|
||||
* EnforceAdmin is a middleware that enforces that the user is authenticated and an admin.
|
||||
*/
|
||||
// EnforceAdmin is a middleware that enforces that the user is authenticated and an admin.
|
||||
func (g *Guard) EnforceAdmin(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
@ -51,9 +50,7 @@ func (g *Guard) EnforceAdmin(next http.Handler) http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* EnforceAuthenticated is a middleware that enforces that the user is authenticated.
|
||||
*/
|
||||
// EnforceAuthenticated is a middleware that enforces that the user is authenticated.
|
||||
func (g *Guard) EnforceAuthenticated(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
@ -67,18 +64,16 @@ func (g *Guard) EnforceAuthenticated(next http.Handler) http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Enforces that the executing principal has requested permission on the resource.
|
||||
* returns true if it's the case, otherwise renders the appropriate error and returns false.
|
||||
*/
|
||||
func (g *Guard) Enforce(w http.ResponseWriter, r *http.Request, scope *types.Scope, resource *types.Resource, permission enum.Permission) bool {
|
||||
|
||||
// Enforce that the executing principal has requested permission on the resource
|
||||
// returns true if it's the case, otherwise renders the appropriate error and returns false.
|
||||
func (g *Guard) Enforce(w http.ResponseWriter, r *http.Request, scope *types.Scope, resource *types.Resource,
|
||||
permission enum.Permission) bool {
|
||||
err := g.Check(r, scope, resource, permission)
|
||||
|
||||
// render error if needed
|
||||
if errors.Is(err, ErrNotAuthenticated) {
|
||||
switch {
|
||||
case errors.Is(err, ErrNotAuthenticated):
|
||||
render.ErrorObject(w, http.StatusUnauthorized, render.ErrUnauthorized)
|
||||
} else if errors.Is(err, ErrNotAuthorized) {
|
||||
case errors.Is(err, ErrNotAuthorized):
|
||||
// log error for debugging.
|
||||
hlog.FromRequest(r).Debug().Msgf("User not authorized to perform %s on resource %v in scope %v",
|
||||
permission,
|
||||
|
@ -86,21 +81,17 @@ func (g *Guard) Enforce(w http.ResponseWriter, r *http.Request, scope *types.Sco
|
|||
scope)
|
||||
|
||||
render.Forbidden(w)
|
||||
} else if err != nil {
|
||||
case err != nil:
|
||||
// log err for debugging
|
||||
hlog.FromRequest(r).Err(err).Msg("Encountered unexpected error while enforcing permission.")
|
||||
|
||||
render.InternalError(w)
|
||||
}
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks whether the principal executing the request has the requested permission on the resource.
|
||||
* Returns nil if the user is confirmed to be permitted to execute the action, otherwise returns errors
|
||||
* NotAuthenticated, NotAuthorized, or any unerlaying error.
|
||||
*/
|
||||
// Check whether the principal executing the request has the requested permission on the resource.
|
||||
// Returns nil if the user is confirmed to be permitted to execute the action, otherwise returns errors
|
||||
// NotAuthenticated, NotAuthorized, or any unerlaying error.
|
||||
func (g *Guard) Check(r *http.Request, scope *types.Scope, resource *types.Resource, permission enum.Permission) error {
|
||||
u, present := request.UserFrom(r.Context())
|
||||
if !present {
|
||||
|
@ -109,6 +100,7 @@ func (g *Guard) Check(r *http.Request, scope *types.Scope, resource *types.Resou
|
|||
|
||||
// TODO: don't hardcode principal type USER
|
||||
authorized, err := g.authorizer.Check(
|
||||
r.Context(),
|
||||
enum.PrincipalTypeUser,
|
||||
fmt.Sprint(u.ID),
|
||||
scope,
|
||||
|
|
|
@ -23,9 +23,9 @@ import (
|
|||
*
|
||||
* Assumes the repository is already available in the request context.
|
||||
*/
|
||||
func (e *Guard) ForRepo(requiredPermission enum.Permission, orPublic bool) func(http.Handler) http.Handler {
|
||||
func (g *Guard) ForRepo(requiredPermission enum.Permission, orPublic bool) func(http.Handler) http.Handler {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return e.Repo(requiredPermission, orPublic, h.ServeHTTP)
|
||||
return g.Repo(requiredPermission, orPublic, h.ServeHTTP)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,9 +24,9 @@ import (
|
|||
*
|
||||
* Assumes the space is already available in the request context.
|
||||
*/
|
||||
func (e *Guard) ForSpace(requiredPermission enum.Permission, orPublic bool) func(http.Handler) http.Handler {
|
||||
func (g *Guard) ForSpace(requiredPermission enum.Permission, orPublic bool) func(http.Handler) http.Handler {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return e.Space(requiredPermission, orPublic, h.ServeHTTP)
|
||||
return g.Space(requiredPermission, orPublic, h.ServeHTTP)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ func HandleLogin(users store.UserStore, system store.SystemStore) http.HandlerFu
|
|||
}
|
||||
|
||||
expires := time.Now().Add(system.Config(ctx).Token.Expire)
|
||||
token_, err := token.GenerateExp(user, expires.Unix(), user.Salt)
|
||||
token, err := token.GenerateExp(user, expires.Unix(), user.Salt)
|
||||
if err != nil {
|
||||
log.Err(err).
|
||||
Str("user", username).
|
||||
|
@ -68,13 +68,13 @@ func HandleLogin(users store.UserStore, system store.SystemStore) http.HandlerFu
|
|||
&types.UserToken{
|
||||
User: user,
|
||||
Token: &types.Token{
|
||||
Value: token_,
|
||||
Value: token,
|
||||
Expires: expires.UTC(),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
// else return the token only.
|
||||
render.JSON(w, http.StatusOK, &types.Token{Value: token_})
|
||||
render.JSON(w, http.StatusOK, &types.Token{Value: token})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ func HandleRegister(users store.UserStore, system store.SystemStore) http.Handle
|
|||
Updated: time.Now().UnixMilli(),
|
||||
}
|
||||
|
||||
if ok, err := check.User(user); !ok {
|
||||
if err = check.User(user); err != nil {
|
||||
log.Debug().Err(err).
|
||||
Str("email", username).
|
||||
Msg("invalid user input")
|
||||
|
@ -58,7 +58,7 @@ func HandleRegister(users store.UserStore, system store.SystemStore) http.Handle
|
|||
return
|
||||
}
|
||||
|
||||
if err := users.Create(ctx, user); err != nil {
|
||||
if err = users.Create(ctx, user); err != nil {
|
||||
log.Err(err).
|
||||
Str("email", username).
|
||||
Msg("Failed to create user")
|
||||
|
@ -72,7 +72,7 @@ func HandleRegister(users store.UserStore, system store.SystemStore) http.Handle
|
|||
// user system admin access.
|
||||
if user.ID == 1 {
|
||||
user.Admin = true
|
||||
if err := users.Update(ctx, user); err != nil {
|
||||
if err = users.Update(ctx, user); err != nil {
|
||||
log.Err(err).
|
||||
Str("email", username).
|
||||
Int64("user_id", user.ID).
|
||||
|
@ -84,7 +84,7 @@ func HandleRegister(users store.UserStore, system store.SystemStore) http.Handle
|
|||
}
|
||||
|
||||
expires := time.Now().Add(system.Config(ctx).Token.Expire)
|
||||
token_, err := token.GenerateExp(user, expires.Unix(), user.Salt)
|
||||
token, err := token.GenerateExp(user, expires.Unix(), user.Salt)
|
||||
if err != nil {
|
||||
log.Err(err).
|
||||
Str("email", username).
|
||||
|
@ -101,13 +101,13 @@ func HandleRegister(users store.UserStore, system store.SystemStore) http.Handle
|
|||
render.JSON(w, http.StatusOK, &types.UserToken{
|
||||
User: user,
|
||||
Token: &types.Token{
|
||||
Value: token_,
|
||||
Value: token,
|
||||
Expires: expires.UTC(),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
// else return the token only.
|
||||
render.JSON(w, http.StatusOK, &types.Token{Value: token_})
|
||||
render.JSON(w, http.StatusOK, &types.Token{Value: token})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
package common
|
||||
|
||||
// Used for path creation apis
|
||||
// CreatePathRequest used for path creation apis.
|
||||
type CreatePathRequest struct {
|
||||
Path string
|
||||
}
|
||||
|
|
|
@ -22,11 +22,11 @@ import (
|
|||
|
||||
type repoCreateInput struct {
|
||||
Name string `json:"name"`
|
||||
SpaceId int64 `json:"spaceId"`
|
||||
SpaceID int64 `json:"spaceId"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Description string `json:"description"`
|
||||
IsPublic bool `json:"isPublic"`
|
||||
ForkId int64 `json:"forkId"`
|
||||
ForkID int64 `json:"forkId"`
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -48,14 +48,14 @@ func HandleCreate(guard *guard.Guard, spaces store.SpaceStore, repos store.RepoS
|
|||
}
|
||||
|
||||
// ensure we reference a space
|
||||
if in.SpaceId <= 0 {
|
||||
if in.SpaceID <= 0 {
|
||||
render.BadRequestf(w, "A repository can only be created within a space.")
|
||||
return
|
||||
}
|
||||
|
||||
parentSpace, err := spaces.Find(ctx, in.SpaceId)
|
||||
parentSpace, err := spaces.Find(ctx, in.SpaceID)
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to get space with id '%d'.", in.SpaceId)
|
||||
log.Err(err).Msgf("Failed to get space with id '%d'.", in.SpaceID)
|
||||
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
|
@ -83,18 +83,18 @@ func HandleCreate(guard *guard.Guard, spaces store.SpaceStore, repos store.RepoS
|
|||
// create new repo object
|
||||
repo := &types.Repository{
|
||||
Name: strings.ToLower(in.Name),
|
||||
SpaceId: in.SpaceId,
|
||||
SpaceID: in.SpaceID,
|
||||
DisplayName: in.DisplayName,
|
||||
Description: in.Description,
|
||||
IsPublic: in.IsPublic,
|
||||
CreatedBy: usr.ID,
|
||||
Created: time.Now().UnixMilli(),
|
||||
Updated: time.Now().UnixMilli(),
|
||||
ForkId: in.ForkId,
|
||||
ForkID: in.ForkID,
|
||||
}
|
||||
|
||||
// validate repo
|
||||
if err := check.Repo(repo); err != nil {
|
||||
if err = check.Repo(repo); err != nil {
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -27,15 +27,15 @@ func HandleDeletePath(guard *guard.Guard, repos store.RepoStore) http.HandlerFun
|
|||
log := hlog.FromRequest(r)
|
||||
repo, _ := request.RepoFrom(ctx)
|
||||
|
||||
pathId, err := request.GetPathId(r)
|
||||
pathID, err := request.GetPathID(r)
|
||||
if err != nil {
|
||||
render.BadRequest(w)
|
||||
return
|
||||
}
|
||||
|
||||
err = repos.DeletePath(ctx, repo.ID, pathId)
|
||||
err = repos.DeletePath(ctx, repo.ID, pathID)
|
||||
if err != nil {
|
||||
log.Err(err).Int64("path_id", pathId).
|
||||
log.Err(err).Int64("path_id", pathID).
|
||||
Msgf("Failed to delete repo path.")
|
||||
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
|
|
|
@ -21,13 +21,11 @@ import (
|
|||
|
||||
type repoMoveRequest struct {
|
||||
Name *string `json:"name"`
|
||||
SpaceId *int64 `json:"spaceId"`
|
||||
SpaceID *int64 `json:"spaceId"`
|
||||
KeepAsAlias bool `json:"keepAsAlias"`
|
||||
}
|
||||
|
||||
/*
|
||||
* Moves an existing repo.
|
||||
*/
|
||||
// HandleMove moves an existing repo.
|
||||
func HandleMove(guard *guard.Guard, repos store.RepoStore, spaces store.SpaceStore) http.HandlerFunc {
|
||||
return guard.Repo(
|
||||
enum.PermissionRepoEdit,
|
||||
|
@ -49,8 +47,8 @@ func HandleMove(guard *guard.Guard, repos store.RepoStore, spaces store.SpaceSto
|
|||
if in.Name == nil {
|
||||
in.Name = &repo.Name
|
||||
}
|
||||
if in.SpaceId == nil {
|
||||
in.SpaceId = &repo.SpaceId
|
||||
if in.SpaceID == nil {
|
||||
in.SpaceID = &repo.SpaceID
|
||||
}
|
||||
|
||||
// convert name to lower case for easy of api use
|
||||
|
@ -60,19 +58,22 @@ func HandleMove(guard *guard.Guard, repos store.RepoStore, spaces store.SpaceSto
|
|||
if err = check.Name(*in.Name); err != nil {
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
} else if *in.SpaceId == repo.SpaceId && *in.Name == repo.Name {
|
||||
}
|
||||
if *in.SpaceID == repo.SpaceID && *in.Name == repo.Name {
|
||||
render.BadRequestError(w, render.ErrNoChange)
|
||||
return
|
||||
} else if *in.SpaceId <= 0 {
|
||||
render.UserfiedErrorOrInternal(w, check.ErrRepositoryRequiresSpaceId)
|
||||
}
|
||||
if *in.SpaceID <= 0 {
|
||||
render.UserfiedErrorOrInternal(w, check.ErrRepositoryRequiresSpaceID)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure we have access to the target space (if its a space move)
|
||||
if *in.SpaceId != repo.SpaceId {
|
||||
newSpace, err := spaces.Find(ctx, *in.SpaceId)
|
||||
if *in.SpaceID != repo.SpaceID {
|
||||
var newSpace *types.Space
|
||||
newSpace, err = spaces.Find(ctx, *in.SpaceID)
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to get target space with id %d for the move.", *in.SpaceId)
|
||||
log.Err(err).Msgf("Failed to get target space with id %d for the move.", *in.SpaceID)
|
||||
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
|
@ -89,7 +90,7 @@ func HandleMove(guard *guard.Guard, repos store.RepoStore, spaces store.SpaceSto
|
|||
}
|
||||
}
|
||||
|
||||
res, err := repos.Move(ctx, usr.ID, repo.ID, *in.SpaceId, *in.Name, in.KeepAsAlias)
|
||||
res, err := repos.Move(ctx, usr.ID, repo.ID, *in.SpaceID, *in.Name, in.KeepAsAlias)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to move the repository.")
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ func HandleUpdate(guard *guard.Guard, repos store.RepoStore) http.HandlerFunc {
|
|||
repo.Updated = time.Now().UnixMilli()
|
||||
|
||||
// ensure provided values are valid
|
||||
if err := check.Repo(repo); err != nil {
|
||||
if err = check.Repo(repo); err != nil {
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
|
||||
type spaceCreateRequest struct {
|
||||
Name string `json:"name"`
|
||||
ParentId int64 `json:"parentId"`
|
||||
ParentID int64 `json:"parentId"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Description string `json:"description"`
|
||||
IsPublic bool `json:"isPublic"`
|
||||
|
@ -54,7 +54,7 @@ func HandleCreate(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc
|
|||
* AUTHORIZATION
|
||||
* Can only be done once we know the parent space
|
||||
*/
|
||||
if in.ParentId <= 0 {
|
||||
if in.ParentID <= 0 {
|
||||
// TODO: Restrict top level space creation.
|
||||
if usr == nil {
|
||||
render.Unauthorized(w)
|
||||
|
@ -62,9 +62,10 @@ func HandleCreate(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc
|
|||
}
|
||||
} else {
|
||||
// Create is a special case - we need the parent path
|
||||
parent, err := spaces.Find(ctx, in.ParentId)
|
||||
var parent *types.Space
|
||||
parent, err = spaces.Find(ctx, in.ParentID)
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to get space with id '%d'.", in.ParentId)
|
||||
log.Err(err).Msgf("Failed to get space with id '%d'.", in.ParentID)
|
||||
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
|
@ -85,7 +86,7 @@ func HandleCreate(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc
|
|||
// create new space object
|
||||
space := &types.Space{
|
||||
Name: strings.ToLower(in.Name),
|
||||
ParentId: in.ParentId,
|
||||
ParentID: in.ParentID,
|
||||
DisplayName: in.DisplayName,
|
||||
Description: in.Description,
|
||||
IsPublic: in.IsPublic,
|
||||
|
@ -95,12 +96,13 @@ func HandleCreate(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc
|
|||
}
|
||||
|
||||
// validate space
|
||||
if err := check.Space(space); err != nil {
|
||||
if err = check.Space(space); err != nil {
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate path length (Due to racing conditions we can't be 100% sure on the path here only best effort to have a quick failure)
|
||||
// Validate path length (Due to racing conditions we can't be 100% sure on the path here only best effort
|
||||
// to have a quick failure)
|
||||
path := paths.Concatinate(parentPath, space.Name)
|
||||
if err = check.Path(path, true); err != nil {
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
|
|
|
@ -27,15 +27,15 @@ func HandleDeletePath(guard *guard.Guard, spaces store.SpaceStore) http.HandlerF
|
|||
log := hlog.FromRequest(r)
|
||||
space, _ := request.SpaceFrom(ctx)
|
||||
|
||||
pathId, err := request.GetPathId(r)
|
||||
pathID, err := request.GetPathID(r)
|
||||
if err != nil {
|
||||
render.BadRequest(w)
|
||||
return
|
||||
}
|
||||
|
||||
err = spaces.DeletePath(ctx, space.ID, pathId)
|
||||
err = spaces.DeletePath(ctx, space.ID, pathID)
|
||||
if err != nil {
|
||||
log.Err(err).Int64("path_id", pathId).
|
||||
log.Err(err).Int64("path_id", pathID).
|
||||
Msgf("Failed to delete space path.")
|
||||
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
|
|
|
@ -16,9 +16,7 @@ import (
|
|||
"github.com/rs/zerolog/hlog"
|
||||
)
|
||||
|
||||
/*
|
||||
* Writes json-encoded list of child spaces in the request body.
|
||||
*/
|
||||
// HandleList writes json-encoded list of child spaces in the request body.
|
||||
func HandleList(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc {
|
||||
return guard.Space(
|
||||
enum.PermissionSpaceView,
|
||||
|
@ -59,7 +57,7 @@ func HandleList(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc {
|
|||
result := make([]*types.Space, 0, len(allSpaces))
|
||||
for _, cs := range allSpaces {
|
||||
if !cs.IsPublic {
|
||||
err := guard.CheckSpace(r, enum.PermissionSpaceView, cs.Path)
|
||||
err = guard.CheckSpace(r, enum.PermissionSpaceView, cs.Path)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).
|
||||
Msgf("Skip space '%s' in output.", cs.Path)
|
||||
|
|
|
@ -16,9 +16,7 @@ import (
|
|||
"github.com/rs/zerolog/hlog"
|
||||
)
|
||||
|
||||
/*
|
||||
* Writes json-encoded list of repos in the request body.
|
||||
*/
|
||||
// HandleListRepos writes json-encoded list of repos in the request body.
|
||||
func HandleListRepos(guard *guard.Guard, repos store.RepoStore) http.HandlerFunc {
|
||||
return guard.Space(
|
||||
enum.PermissionSpaceView,
|
||||
|
@ -59,7 +57,7 @@ func HandleListRepos(guard *guard.Guard, repos store.RepoStore) http.HandlerFunc
|
|||
result := make([]*types.Repository, 0, len(allRepos))
|
||||
for _, rep := range allRepos {
|
||||
if !rep.IsPublic {
|
||||
err := guard.CheckRepo(r, enum.PermissionRepoView, rep.Path)
|
||||
err = guard.CheckRepo(r, enum.PermissionRepoView, rep.Path)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).
|
||||
Msgf("Skip repo '%s' in output.", rep.Path)
|
||||
|
|
|
@ -22,13 +22,11 @@ import (
|
|||
|
||||
type spaceMoveRequest struct {
|
||||
Name *string `json:"name"`
|
||||
ParentId *int64 `json:"parentId"`
|
||||
ParentID *int64 `json:"parentId"`
|
||||
KeepAsAlias bool `json:"keepAsAlias"`
|
||||
}
|
||||
|
||||
/*
|
||||
* Moves an existing space.
|
||||
*/
|
||||
// HandleMove moves an existing space.
|
||||
func HandleMove(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc {
|
||||
return guard.Space(
|
||||
enum.PermissionSpaceEdit,
|
||||
|
@ -50,8 +48,8 @@ func HandleMove(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc {
|
|||
if in.Name == nil {
|
||||
in.Name = &space.Name
|
||||
}
|
||||
if in.ParentId == nil {
|
||||
in.ParentId = &space.ParentId
|
||||
if in.ParentID == nil {
|
||||
in.ParentID = &space.ParentID
|
||||
}
|
||||
|
||||
// convert name to lower case for easy of api use
|
||||
|
@ -61,18 +59,19 @@ func HandleMove(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc {
|
|||
if err = check.Name(*in.Name); err != nil {
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
} else if *in.ParentId == space.ParentId && *in.Name == space.Name {
|
||||
} else if *in.ParentID == space.ParentID && *in.Name == space.Name {
|
||||
render.BadRequestError(w, render.ErrNoChange)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: restrict top level move
|
||||
// Ensure we can create spaces within the target space (using parent space as scope, similar to create)
|
||||
if *in.ParentId > 0 && *in.ParentId != space.ParentId {
|
||||
newParent, err := spaces.Find(ctx, *in.ParentId)
|
||||
if *in.ParentID > 0 && *in.ParentID != space.ParentID {
|
||||
var newParent *types.Space
|
||||
newParent, err = spaces.Find(ctx, *in.ParentID)
|
||||
if err != nil {
|
||||
log.Err(err).
|
||||
Msgf("Failed to get target space with id %d for the move.", *in.ParentId)
|
||||
Msgf("Failed to get target space with id %d for the move.", *in.ParentID)
|
||||
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
|
@ -88,7 +87,8 @@ func HandleMove(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc {
|
|||
}
|
||||
|
||||
/*
|
||||
* Validate path length (Due to racing conditions we can't be 100% sure on the path here only best effort to avoid big transaction failure)
|
||||
* Validate path length (Due to racing conditions we can't be 100% sure on the path here only best
|
||||
* effort to avoid big transaction failure)
|
||||
* Only needed if we actually change the parent (and can skip top level, as we already validate the name)
|
||||
*/
|
||||
path := paths.Concatinate(newParent.Path, *in.Name)
|
||||
|
@ -98,7 +98,7 @@ func HandleMove(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
res, err := spaces.Move(ctx, usr.ID, space.ID, *in.ParentId, *in.Name, in.KeepAsAlias)
|
||||
res, err := spaces.Move(ctx, usr.ID, space.ID, *in.ParentID, *in.Name, in.KeepAsAlias)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to move the space.")
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ func HandleUpdate(guard *guard.Guard, spaces store.SpaceStore) http.HandlerFunc
|
|||
space.Updated = time.Now().UnixMilli()
|
||||
|
||||
// ensure provided values are valid
|
||||
if err := check.Space(space); err != nil {
|
||||
if err = check.Space(space); err != nil {
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -38,7 +38,8 @@ func HandleUpdate(users store.UserStore) http.HandlerFunc {
|
|||
}
|
||||
|
||||
if in.Password != nil {
|
||||
hash, err := hashPassword([]byte(ptr.ToString(in.Password)), bcrypt.DefaultCost)
|
||||
var hash []byte
|
||||
hash, err = hashPassword([]byte(ptr.ToString(in.Password)), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to hash password.")
|
||||
|
||||
|
|
|
@ -57,8 +57,7 @@ func HandleCreate(users store.UserStore) http.HandlerFunc {
|
|||
Created: time.Now().UnixMilli(),
|
||||
Updated: time.Now().UnixMilli(),
|
||||
}
|
||||
|
||||
if ok, err := check.User(user); !ok {
|
||||
if err = check.User(user); err != nil {
|
||||
log.Debug().Err(err).
|
||||
Str("email", user.Email).
|
||||
Msg("invalid user input")
|
||||
|
|
|
@ -39,7 +39,6 @@ func HandleDelete(users store.UserStore) http.HandlerFunc {
|
|||
|
||||
render.UserfiedErrorOrInternal(w, err)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
// password at the given cost.
|
||||
var hashPassword = bcrypt.GenerateFromPassword
|
||||
|
||||
// HandleUpdate returns an http.HandlerFunc that processes an http.Request
|
||||
// HandleUpdate returns a http.HandlerFunc that processes an http.Request
|
||||
// to update a user account.
|
||||
func HandleUpdate(users store.UserStore) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -41,13 +41,14 @@ func HandleUpdate(users store.UserStore) http.HandlerFunc {
|
|||
}
|
||||
|
||||
in := new(types.UserInput)
|
||||
if err := json.NewDecoder(r.Body).Decode(in); err != nil {
|
||||
if err = json.NewDecoder(r.Body).Decode(in); err != nil {
|
||||
render.BadRequestf(w, "Invalid request body: %s.", err)
|
||||
return
|
||||
}
|
||||
|
||||
if in.Password != nil {
|
||||
hash, err := hashPassword([]byte(ptr.ToString(in.Password)), bcrypt.DefaultCost)
|
||||
var hash []byte
|
||||
hash, err = hashPassword([]byte(ptr.ToString(in.Password)), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
log.Err(err).
|
||||
Int64("user_id", user.ID).
|
||||
|
@ -74,7 +75,8 @@ func HandleUpdate(users store.UserStore) http.HandlerFunc {
|
|||
|
||||
// TODO: why are we overwriting the password twice?
|
||||
if in.Password != nil {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(ptr.ToString(in.Password)), bcrypt.DefaultCost)
|
||||
var hash []byte
|
||||
hash, err = bcrypt.GenerateFromPassword([]byte(ptr.ToString(in.Password)), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
log.Err(err).
|
||||
Int64("user_id", user.ID).
|
||||
|
@ -86,8 +88,7 @@ func HandleUpdate(users store.UserStore) http.HandlerFunc {
|
|||
}
|
||||
user.Password = string(hash)
|
||||
}
|
||||
|
||||
if ok, err := check.User(user); !ok {
|
||||
if err = check.User(user); err != nil {
|
||||
log.Debug().Err(err).
|
||||
Int64("user_id", user.ID).
|
||||
Str("user_email", user.Email).
|
||||
|
|
|
@ -43,15 +43,16 @@ func Handler(scheme, host string) func(http.Handler) http.Handler {
|
|||
// using the X-Forwarded-Proto, if the original request was HTTPS
|
||||
// and routed through a reverse proxy with SSL termination.
|
||||
func resolveScheme(r *http.Request) string {
|
||||
const https = "https"
|
||||
switch {
|
||||
case r.URL.Scheme == "https":
|
||||
return "https"
|
||||
case r.URL.Scheme == https:
|
||||
return https
|
||||
case r.TLS != nil:
|
||||
return "https"
|
||||
return https
|
||||
case strings.HasPrefix(r.Proto, "HTTPS"):
|
||||
return "https"
|
||||
case r.Header.Get("X-Forwarded-Proto") == "https":
|
||||
return "https"
|
||||
return https
|
||||
case r.Header.Get("X-Forwarded-Proto") == https:
|
||||
return https
|
||||
default:
|
||||
return "http"
|
||||
}
|
||||
|
|
|
@ -16,10 +16,8 @@ import (
|
|||
"github.com/rs/zerolog/hlog"
|
||||
)
|
||||
|
||||
/*
|
||||
* Attempt returns an http.HandlerFunc middleware that authenticates
|
||||
* the http.Request if authentication payload is available.
|
||||
*/
|
||||
// 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 func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -32,11 +30,15 @@ func Attempt(authenticator authn.Authenticator) func(http.Handler) http.Handler
|
|||
// if there was no auth data in the request - continue as is
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
} else if err != nil {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// for any other error we fail
|
||||
render.Unauthorized(w)
|
||||
return
|
||||
} else if user == nil {
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
// when err == nil user should never be nil!
|
||||
log.Error().Msg("User is nil eventhough the authenticator didn't return any error!")
|
||||
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
package encode
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/harness/gitness/types"
|
||||
)
|
||||
|
||||
/*
|
||||
* Wraps an http.HandlerFunc in a layer that encodes Paths coming as part of the GIT api
|
||||
* (e.g. "space1/repo.git") before executing the provided http.HandlerFunc.
|
||||
* The first prefix that matches the URL.Path will be used during encoding.
|
||||
*/
|
||||
// GitPathBefore wraps an http.HandlerFunc in a layer that encodes Paths coming as part of the GIT api
|
||||
// (e.g. "space1/repo.git") before executing the provided http.HandlerFunc
|
||||
// The first prefix that matches the URL.Path will be used during encoding.
|
||||
func GitPathBefore(h http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
r, _ = pathTerminatedWithMarker(r, "", ".git", false)
|
||||
|
@ -21,16 +20,14 @@ func GitPathBefore(h http.HandlerFunc) http.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wraps an http.HandlerFunc in a layer that encodes a terminated path (e.g. "/space1/space2/+")
|
||||
* before executing the provided http.HandlerFunc.
|
||||
* The first prefix that matches the URL.Path will be used during encoding.
|
||||
*/
|
||||
// TerminatedPathBefore wraps an http.HandlerFunc in a layer that encodes a terminated path (e.g. "/space1/space2/+")
|
||||
// before executing the provided http.HandlerFunc. The first prefix that matches the URL.Path will
|
||||
// be used during encoding.
|
||||
func TerminatedPathBefore(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
|
||||
var changed bool
|
||||
if r, changed = pathTerminatedWithMarker(r, p, "/+", false); changed {
|
||||
break
|
||||
}
|
||||
|
@ -40,16 +37,15 @@ func TerminatedPathBefore(prefixes []string, h http.HandlerFunc) http.HandlerFun
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function encodes a path followed by a custom marker and returns a request with an updated URL.Path.
|
||||
* A non-empty prefix can be provided to encode encode only after the prefix.
|
||||
* It allows our Rest API to handle paths of the form "/spaces/space1/space2/+/authToken"
|
||||
*
|
||||
* Examples:
|
||||
* Prefix: "" Path: "/space1/space2/+" => "/space1%2Fspace2"
|
||||
* Prefix: "" Path: "/space1/space2.git" => "/space1%2Fspace2"
|
||||
* Prefix: "/spaces" Path: "/spaces/space1/space2/+/authToken" => "/spaces/space1%2Fspace2/authToken"
|
||||
*/
|
||||
// pathTerminatedWithMarker function encodes a path followed by a custom marker and returns a request with an
|
||||
// updated URL.Path.
|
||||
// A non-empty prefix can be provided to encode encode only after the prefix.
|
||||
// It allows our Rest API to handle paths of the form "/spaces/space1/space2/+/authToken"
|
||||
//
|
||||
// Examples:
|
||||
// Prefix: "" Path: "/space1/space2/+" => "/space1%2Fspace2"
|
||||
// Prefix: "" Path: "/space1/space2.git" => "/space1%2Fspace2"
|
||||
// Prefix: "/spaces" Path: "/spaces/space1/space2/+/authToken" => "/spaces/space1%2Fspace2/authToken".
|
||||
func pathTerminatedWithMarker(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 {
|
||||
|
@ -65,14 +61,14 @@ func pathTerminatedWithMarker(r *http.Request, prefix string, marker string, kee
|
|||
}
|
||||
|
||||
// if marker was found - convert to escaped version (skip first character in case path starts with '/')
|
||||
escapedPath := path[0:1] + strings.Replace(path[1:], types.PathSeparator, "%2F", -1)
|
||||
escapedPath := path[0:1] + strings.ReplaceAll(path[1:], types.PathSeparator, "%2F")
|
||||
if keepMarker {
|
||||
escapedPath += marker
|
||||
}
|
||||
updatedSubPath := escapedPath + suffix
|
||||
|
||||
// TODO: Proper Logging
|
||||
fmt.Printf(
|
||||
log.Debug().Msgf(
|
||||
"[Encode] prefix: '%s', marker: '%s', original: '%s', updated: '%s'.\n",
|
||||
prefix,
|
||||
marker,
|
||||
|
|
|
@ -28,7 +28,6 @@ type registerRequest struct {
|
|||
// helper function that constructs the openapi specification
|
||||
// for the account registration and login endpoints.
|
||||
func buildAccount(reflector *openapi3.Reflector) {
|
||||
|
||||
onLogin := openapi3.Operation{}
|
||||
onLogin.WithTags("account")
|
||||
onLogin.WithMapOfAnything(map[string]interface{}{"operationId": "onLogin"})
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
)
|
||||
|
||||
type (
|
||||
// base request
|
||||
baseRequest struct {
|
||||
Account string `query:"accountIdentifier"`
|
||||
Organization string `query:"orgIdentifier"`
|
||||
|
@ -19,45 +18,12 @@ type (
|
|||
Routing string `query:"routingId"`
|
||||
}
|
||||
|
||||
// base request for pagination
|
||||
paginationRequest struct {
|
||||
Page int `query:"page" default:"1"`
|
||||
Size int `query:"per_page" default:"100"`
|
||||
}
|
||||
|
||||
// TODO: base response for pagination
|
||||
//paginationResponse struct {
|
||||
// Total int `header:"x-total"`
|
||||
// Pagelen int `header:"x-total-pages"`
|
||||
// Page int `header:"x-page"`
|
||||
// Size int `header:"x-per-page"`
|
||||
// Next int `header:"x-next"`
|
||||
// Prev int `header:"x-prev"`
|
||||
// Link []string `header:"Link"`
|
||||
//}
|
||||
)
|
||||
|
||||
// Handler returns an http.HandlerFunc that writes the openapi v3
|
||||
// specification file to the http.Response body.
|
||||
// TODO: unused function
|
||||
//func Handler() http.HandlerFunc {
|
||||
// spec := Generate()
|
||||
// yaml, _ := spec.MarshalYAML()
|
||||
// json, _ := spec.MarshalJSON()
|
||||
//
|
||||
// yaml = normalize(yaml)
|
||||
// json = normalize(json)
|
||||
//
|
||||
// return func(w http.ResponseWriter, r *http.Request) {
|
||||
// switch {
|
||||
// case strings.HasSuffix(r.URL.Path, ".json"):
|
||||
// w.Write(json)
|
||||
// default:
|
||||
// w.Write(yaml)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
// Generate is a helper function that constructs the
|
||||
// openapi specification object, which can be marshaled
|
||||
// to json or yaml, as needed.
|
||||
|
@ -102,14 +68,3 @@ func Generate() *openapi3.Spec {
|
|||
|
||||
return reflector.Spec
|
||||
}
|
||||
|
||||
// helper function normalizes the output to ensure
|
||||
// automatically-generated names are more user friendly.
|
||||
// TODO: unused function
|
||||
//func normalize(data []byte) []byte {
|
||||
// data = bytes.ReplaceAll(data, []byte("Types"), []byte(""))
|
||||
// data = bytes.ReplaceAll(data, []byte("Openapi"), []byte(""))
|
||||
// data = bytes.ReplaceAll(data, []byte("FormData"), []byte(""))
|
||||
// data = bytes.ReplaceAll(data, []byte("RenderError"), []byte("Error"))
|
||||
// return data
|
||||
//}
|
||||
|
|
|
@ -21,7 +21,6 @@ type currentUserResponse struct {
|
|||
// helper function that constructs the openapi specification
|
||||
// for user account resources.
|
||||
func buildUser(reflector *openapi3.Reflector) {
|
||||
|
||||
opFind := openapi3.Operation{}
|
||||
opFind.WithTags("user")
|
||||
opFind.WithMapOfAnything(map[string]interface{}{"operationId": "getUser"})
|
||||
|
|
|
@ -40,7 +40,6 @@ type (
|
|||
// helper function that constructs the openapi specification
|
||||
// for user resources.
|
||||
func buildUsers(reflector *openapi3.Reflector) {
|
||||
|
||||
opFind := openapi3.Operation{}
|
||||
opFind.WithTags("users")
|
||||
opFind.WithMapOfAnything(map[string]interface{}{"operationId": "getUserEmail"})
|
||||
|
|
|
@ -35,10 +35,11 @@ var (
|
|||
// ErrPathTooLong is returned if user action would lead to a path that is too long.
|
||||
ErrPathTooLong = New("The resource path is too long")
|
||||
|
||||
// ErrCyclicHierarchy is returned if the user action would create a cyclic dependency between spaces
|
||||
ErrCyclicHierarchy = New(("Unable to perform the action as it would lead to a cyclic dependency."))
|
||||
// ErrCyclicHierarchy is returned if the user action would create a cyclic dependency between spaces.
|
||||
ErrCyclicHierarchy = New("Unable to perform the action as it would lead to a cyclic dependency.")
|
||||
|
||||
// ErrSpaceWithChildsCantBeDeleted is returned if the user is trying to delete a space that still has child resources
|
||||
// ErrSpaceWithChildsCantBeDeleted is returned if the user is trying to delete a space that
|
||||
// still has child resources.
|
||||
ErrSpaceWithChildsCantBeDeleted = New("Space can't be deleted as it still contains child resources.")
|
||||
)
|
||||
|
||||
|
|
|
@ -13,9 +13,9 @@ func RenderResource(w http.ResponseWriter, code int, v interface{}) {
|
|||
payload := new(wrapper)
|
||||
payload.Status = "SUCCESS"
|
||||
payload.Data, _ = json.Marshal(v)
|
||||
if code > 399 {
|
||||
if code >= http.StatusBadRequest {
|
||||
payload.Status = "ERROR"
|
||||
} else if code > 299 {
|
||||
} else if code >= http.StatusMultipleChoices {
|
||||
payload.Status = "FAILURE"
|
||||
}
|
||||
render.JSON(w, code, payload)
|
||||
|
|
|
@ -12,11 +12,13 @@ import (
|
|||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types/check"
|
||||
)
|
||||
|
||||
// indent the json-encoded API responses
|
||||
// indent the json-encoded API responses.
|
||||
var indent bool
|
||||
|
||||
func init() {
|
||||
|
@ -25,31 +27,29 @@ func init() {
|
|||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* UserfiedErrorOrInternal renders the appropriate user facing message for the provided error.
|
||||
* If the error is unknown, an internal error is rendered.
|
||||
*/
|
||||
// UserfiedErrorOrInternal renders the appropriate user facing message for the provided error.
|
||||
// If the error is unknown, an internal error is rendered.
|
||||
func UserfiedErrorOrInternal(w http.ResponseWriter, err error) {
|
||||
|
||||
if errors.Is(err, check.ErrAny) {
|
||||
switch {
|
||||
case errors.Is(err, check.ErrAny):
|
||||
ErrorObject(w, http.StatusBadRequest, &Error{err.Error()})
|
||||
} else if errors.Is(err, store.ErrResourceNotFound) {
|
||||
case errors.Is(err, store.ErrResourceNotFound):
|
||||
ErrorObject(w, http.StatusNotFound, ErrNotFound)
|
||||
} else if errors.Is(err, store.ErrDuplicate) {
|
||||
case errors.Is(err, store.ErrDuplicate):
|
||||
ErrorObject(w, http.StatusBadRequest, ErrDuplicate)
|
||||
} else if errors.Is(err, store.ErrPrimaryPathCantBeDeleted) {
|
||||
case errors.Is(err, store.ErrPrimaryPathCantBeDeleted):
|
||||
ErrorObject(w, http.StatusBadRequest, ErrPrimaryPathCantBeDeleted)
|
||||
} else if errors.Is(err, store.ErrPathTooLong) {
|
||||
case errors.Is(err, store.ErrPathTooLong):
|
||||
ErrorObject(w, http.StatusBadRequest, ErrPathTooLong)
|
||||
} else if errors.Is(err, store.ErrNoChangeInRequestedMove) {
|
||||
case errors.Is(err, store.ErrNoChangeInRequestedMove):
|
||||
ErrorObject(w, http.StatusBadRequest, ErrNoChange)
|
||||
} else if errors.Is(err, store.ErrIllegalMoveCyclicHierarchy) {
|
||||
case errors.Is(err, store.ErrIllegalMoveCyclicHierarchy):
|
||||
ErrorObject(w, http.StatusBadRequest, ErrCyclicHierarchy)
|
||||
} else if errors.Is(err, store.ErrSpaceWithChildsCantBeDeleted) {
|
||||
case errors.Is(err, store.ErrSpaceWithChildsCantBeDeleted):
|
||||
ErrorObject(w, http.StatusBadRequest, ErrSpaceWithChildsCantBeDeleted)
|
||||
} else {
|
||||
default:
|
||||
// nothing found - render internal error
|
||||
fmt.Println(err)
|
||||
log.Err(err)
|
||||
InternalError(w)
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ func ErrorObject(w http.ResponseWriter, code int, err *Error) {
|
|||
}
|
||||
|
||||
// JSON writes the json-encoded value to the response
|
||||
// with the provides status
|
||||
// with the provides status.
|
||||
func JSON(w http.ResponseWriter, code int, v interface{}) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
|
|
|
@ -18,22 +18,21 @@ func pagelen(size, total int) int {
|
|||
}
|
||||
}
|
||||
|
||||
// max returns the larger of x or y.
|
||||
// max returns the largest of x or y.
|
||||
func max(x, y int) int {
|
||||
if x > y {
|
||||
return x
|
||||
} else {
|
||||
return y
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
// max returns the smaller of x or y.
|
||||
func min(x, y int) int {
|
||||
if y == 0 {
|
||||
return x
|
||||
} else if x < y {
|
||||
return x
|
||||
} else {
|
||||
return y
|
||||
}
|
||||
if x < y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ const (
|
|||
)
|
||||
|
||||
// WithUser returns a copy of parent in which the user
|
||||
// value is set
|
||||
// value is set.
|
||||
func WithUser(parent context.Context, v *types.User) context.Context {
|
||||
return context.WithValue(parent, userKey, v)
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ func UserFrom(ctx context.Context) (*types.User, bool) {
|
|||
return v, ok && v != nil
|
||||
}
|
||||
|
||||
// WithSpace returns a copy of parent in which the space value is set
|
||||
// WithSpace returns a copy of parent in which the space value is set.
|
||||
func WithSpace(parent context.Context, v *types.Space) context.Context {
|
||||
return context.WithValue(parent, spaceKey, v)
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ func SpaceFrom(ctx context.Context) (*types.Space, bool) {
|
|||
return v, ok && v != nil
|
||||
}
|
||||
|
||||
// WithRepo returns a copy of parent in which the repo value is set
|
||||
// WithRepo returns a copy of parent in which the repo value is set.
|
||||
func WithRepo(parent context.Context, v *types.Repository) context.Context {
|
||||
return context.WithValue(parent, repoKey, v)
|
||||
}
|
||||
|
|
|
@ -9,16 +9,16 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
PathIdParamName = "pathId"
|
||||
PathIDParamName = "pathId"
|
||||
)
|
||||
|
||||
func GetPathId(r *http.Request) (int64, error) {
|
||||
rawId := chi.URLParam(r, PathIdParamName)
|
||||
if rawId == "" {
|
||||
return 0, errors.New("Path id parameter not found in request.")
|
||||
func GetPathID(r *http.Request) (int64, error) {
|
||||
rawID := chi.URLParam(r, PathIDParamName)
|
||||
if rawID == "" {
|
||||
return 0, errors.New("path id parameter not found in request")
|
||||
}
|
||||
|
||||
id, err := strconv.ParseInt(rawId, 10, 64)
|
||||
id, err := strconv.ParseInt(rawID, 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrRepoReferenceNotFound = errors.New("No repository reference found in request.")
|
||||
ErrRepoReferenceNotFound = errors.New("no repository reference found in request")
|
||||
)
|
||||
|
||||
func GetRepoRef(r *http.Request) (string, error) {
|
||||
|
|
|
@ -14,7 +14,7 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrSpaceReferenceNotFound = errors.New("No space reference found in request.")
|
||||
ErrSpaceReferenceNotFound = errors.New("no space reference found in request")
|
||||
)
|
||||
|
||||
func GetSpaceRef(r *http.Request) (string, error) {
|
||||
|
|
|
@ -24,12 +24,13 @@ func ParsePage(r *http.Request) int {
|
|||
|
||||
// ParseSize extracts the size parameter from the url.
|
||||
func ParseSize(r *http.Request) int {
|
||||
const itemsPerPage = 100
|
||||
s := r.FormValue("per_page")
|
||||
i, _ := strconv.Atoi(s)
|
||||
if i == 0 {
|
||||
i = 100
|
||||
} else if i > 100 {
|
||||
i = 100
|
||||
i = itemsPerPage
|
||||
} else if i > itemsPerPage {
|
||||
i = itemsPerPage
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
@ -42,7 +43,7 @@ func ParseOrder(r *http.Request) enum.Order {
|
|||
}
|
||||
|
||||
// ParseSort extracts the sort parameter from the url.
|
||||
func ParseSort(r *http.Request) (s string) {
|
||||
func ParseSort(r *http.Request) string {
|
||||
return r.FormValue("sort")
|
||||
}
|
||||
|
||||
|
|
|
@ -12,14 +12,12 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
// An error that is returned if the authorizer doesn't find any data in the request that can be used for auth.
|
||||
ErrNoAuthData = errors.New("The request doesn't contain any auth data that can be used by the Authorizer.")
|
||||
// ErrNoAuthData that is returned if the authorizer doesn't find any data in the request that can be used for auth.
|
||||
ErrNoAuthData = errors.New("the request doesn't contain any auth data that can be used by the Authorizer")
|
||||
)
|
||||
|
||||
/*
|
||||
* An abstraction of an entity thats responsible for authenticating users
|
||||
* that are making calls via HTTP.
|
||||
*/
|
||||
// Authenticator is abstraction of an entity that's responsible for authenticating users
|
||||
// that are making calls via HTTP.
|
||||
type Authenticator interface {
|
||||
/*
|
||||
* Tries to authenticate a user if credentials are available.
|
||||
|
|
|
@ -13,9 +13,7 @@ import (
|
|||
|
||||
var _ authn.Authenticator = (*Authenticator)(nil)
|
||||
|
||||
/*
|
||||
* An authenticator that validates access token provided by harness SAAS.
|
||||
*/
|
||||
// Authenticator that validates access token provided by harness SAAS.
|
||||
type Authenticator struct {
|
||||
// some config to validate jwt
|
||||
}
|
||||
|
@ -24,6 +22,6 @@ func NewAuthenticator() (authn.Authenticator, error) {
|
|||
return &Authenticator{}, nil
|
||||
}
|
||||
|
||||
func (this *Authenticator) Authenticate(r *http.Request) (*types.User, error) {
|
||||
func (a *Authenticator) Authenticate(r *http.Request) (*types.User, error) {
|
||||
return &types.User{}, nil
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ func (a *TokenAuthenticator) Authenticate(r *http.Request) (*types.User, error)
|
|||
Error().Err(err).
|
||||
Int64("user", id).
|
||||
Msg("cannot find user")
|
||||
return nil, fmt.Errorf("Failed to get user info: %s", err)
|
||||
return nil, fmt.Errorf("failed to get user info: %w", err)
|
||||
}
|
||||
return []byte(user.Salt), nil
|
||||
})
|
||||
|
@ -65,11 +65,11 @@ func (a *TokenAuthenticator) Authenticate(r *http.Request) (*types.User, error)
|
|||
return nil, err
|
||||
}
|
||||
if !parsed.Valid {
|
||||
return nil, errors.New("Invalid token")
|
||||
return nil, errors.New("invalid token")
|
||||
}
|
||||
|
||||
if _, ok := parsed.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, errors.New("Invalid token")
|
||||
return nil, errors.New("invalid token")
|
||||
}
|
||||
|
||||
// this code should be deprecated, since the jwt.ParseWithClaims
|
||||
|
@ -78,7 +78,7 @@ func (a *TokenAuthenticator) Authenticate(r *http.Request) (*types.User, error)
|
|||
if claims, ok := parsed.Claims.(*token.Claims); ok {
|
||||
if claims.ExpiresAt > 0 {
|
||||
if time.Now().Unix() > claims.ExpiresAt {
|
||||
return nil, errors.New("Expired token")
|
||||
return nil, errors.New("expired token")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
// WireSet provides a wire set for this package
|
||||
// WireSet provides a wire set for this package.
|
||||
var WireSet = wire.NewSet(
|
||||
NewTokenAuthenticator,
|
||||
)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package authz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/harness/gitness/types"
|
||||
|
@ -12,13 +13,11 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
// An error that is thrown if no permission checks are provided
|
||||
ErrNoPermissionCheckProvided = errors.New("No permission checks provided")
|
||||
// ErrNoPermissionCheckProvided is error that is thrown if no permission checks are provided.
|
||||
ErrNoPermissionCheckProvided = errors.New("no permission checks provided")
|
||||
)
|
||||
|
||||
/*
|
||||
* An abstraction of an entity responsible for authorizing access to resources.
|
||||
*/
|
||||
// Authorizer abstraction of an entity responsible for authorizing access to resources.
|
||||
type Authorizer interface {
|
||||
/*
|
||||
* Checks whether the provided principal has the permission to execute the action on the resource within the scope.
|
||||
|
@ -27,14 +26,23 @@ type Authorizer interface {
|
|||
* (false, nil) - the principal does not have permission to perform the action
|
||||
* (false, err) - an error occured while performing the permission check and the action should be denied
|
||||
*/
|
||||
Check(principalType enum.PrincipalType, principalId string, scope *types.Scope, resource *types.Resource, permission enum.Permission) (bool, error)
|
||||
Check(ctx context.Context,
|
||||
principalType enum.PrincipalType,
|
||||
principalID string,
|
||||
scope *types.Scope,
|
||||
resource *types.Resource,
|
||||
permission enum.Permission) (bool, error)
|
||||
|
||||
/*
|
||||
* Checks whether the provided principal the required permission to execute ALL the requested actions on the resource within the scope.
|
||||
* Checks whether the provided principal the required permission to execute ALL the requested actions on the
|
||||
* resource within the scope.
|
||||
* Returns
|
||||
* (true, nil) - the principal has permission to perform all the requested actions
|
||||
* (false, nil) - the principal does not have permission to perform all the actions (at least one is not allowed)
|
||||
* (false, err) - an error occured while performing the permission check and all actions should be denied
|
||||
*/
|
||||
CheckAll(principalType enum.PrincipalType, principalId string, permissionChecks ...types.PermissionCheck) (bool, error)
|
||||
CheckAll(ctx context.Context,
|
||||
principalType enum.PrincipalType,
|
||||
principalID string,
|
||||
permissionChecks ...types.PermissionCheck) (bool, error)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ package harness
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
@ -30,7 +31,10 @@ func NewAuthorizer(aclEndpoint, authToken string) (authz.Authorizer, error) {
|
|||
// build http client - could be injected, too
|
||||
tr := &http.Transport{
|
||||
// TODO: expose InsecureSkipVerify in config
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
TLSClientConfig: &tls.Config{
|
||||
//nolint:gosec // accept any host cert
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
|
||||
|
@ -41,16 +45,22 @@ func NewAuthorizer(aclEndpoint, authToken string) (authz.Authorizer, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (a *Authorizer) Check(principalType enum.PrincipalType, principalId string, scope *types.Scope, resource *types.Resource, permission enum.Permission) (bool, error) {
|
||||
return a.CheckAll(principalType, principalId, types.PermissionCheck{Scope: *scope, Resource: *resource, Permission: permission})
|
||||
func (a *Authorizer) Check(ctx context.Context, principalType enum.PrincipalType, principalID string,
|
||||
scope *types.Scope, resource *types.Resource, permission enum.Permission) (bool, error) {
|
||||
return a.CheckAll(ctx, principalType, principalID, types.PermissionCheck{
|
||||
Scope: *scope,
|
||||
Resource: *resource,
|
||||
Permission: permission,
|
||||
})
|
||||
}
|
||||
|
||||
func (a *Authorizer) CheckAll(principalType enum.PrincipalType, principalId string, permissionChecks ...types.PermissionCheck) (bool, error) {
|
||||
func (a *Authorizer) CheckAll(ctx context.Context, principalType enum.PrincipalType, principalID string,
|
||||
permissionChecks ...types.PermissionCheck) (bool, error) {
|
||||
if len(permissionChecks) == 0 {
|
||||
return false, authz.ErrNoPermissionCheckProvided
|
||||
}
|
||||
|
||||
requestDto, err := createAclRequest(principalType, principalId, permissionChecks)
|
||||
requestDto, err := createACLRequest(principalType, principalID, permissionChecks)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -61,7 +71,7 @@ func (a *Authorizer) CheckAll(principalType enum.PrincipalType, principalId stri
|
|||
|
||||
// TODO: accountId might be different!
|
||||
url := a.aclEndpoint + "?routingId=" + requestDto.Permissions[0].ResourceScope.AccountIdentifier
|
||||
httpRequest, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(byt))
|
||||
httpRequest, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(byt))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -71,16 +81,19 @@ func (a *Authorizer) CheckAll(principalType enum.PrincipalType, principalId stri
|
|||
"Authorization": []string{"Bearer " + a.authToken},
|
||||
}
|
||||
|
||||
httpResponse, err := a.client.Do(httpRequest)
|
||||
response, err := a.client.Do(httpRequest)
|
||||
if response != nil {
|
||||
defer response.Body.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if httpResponse.StatusCode != 200 {
|
||||
return false, fmt.Errorf("Got unexpected status code '%d' - assume unauthorized.", httpResponse.StatusCode)
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return false, fmt.Errorf("got unexpected status code '%d' - assume unauthorized", response.StatusCode)
|
||||
}
|
||||
|
||||
bodyByte, err := ioutil.ReadAll(httpResponse.Body)
|
||||
bodyByte, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -91,25 +104,23 @@ func (a *Authorizer) CheckAll(principalType enum.PrincipalType, principalId stri
|
|||
return false, err
|
||||
}
|
||||
|
||||
return checkAclResponse(permissionChecks, responseDto)
|
||||
return checkACLResponse(permissionChecks, responseDto)
|
||||
}
|
||||
|
||||
func createAclRequest(principalType enum.PrincipalType, principalId string, permissionChecks []types.PermissionCheck) (*aclRequest, error) {
|
||||
func createACLRequest(principalType enum.PrincipalType, principalID string,
|
||||
permissionChecks []types.PermissionCheck) (*aclRequest, error) {
|
||||
// Generate ACL req
|
||||
req := aclRequest{
|
||||
Permissions: []aclPermission{},
|
||||
Principal: aclPrincipal{
|
||||
PrincipalIdentifier: principalId,
|
||||
PrincipalIdentifier: principalID,
|
||||
PrincipalType: string(principalType),
|
||||
},
|
||||
}
|
||||
|
||||
// map all permissionchecks to ACL permission checks
|
||||
for _, c := range permissionChecks {
|
||||
mappedPermission, err := mapPermission(c.Permission)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mappedPermission := mapPermission(c.Permission)
|
||||
mappedResourceScope, err := mapScope(c.Scope)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -126,7 +137,7 @@ func createAclRequest(principalType enum.PrincipalType, principalId string, perm
|
|||
return &req, nil
|
||||
}
|
||||
|
||||
func checkAclResponse(permissionChecks []types.PermissionCheck, responseDto aclResponse) (bool, error) {
|
||||
func checkACLResponse(permissionChecks []types.PermissionCheck, responseDto aclResponse) (bool, error) {
|
||||
/*
|
||||
* We are assuming two things:
|
||||
* - All permission checks were made for the same principal.
|
||||
|
@ -150,7 +161,7 @@ func checkAclResponse(permissionChecks []types.PermissionCheck, responseDto aclR
|
|||
}
|
||||
|
||||
if !permissionPermitted {
|
||||
return false, fmt.Errorf("Permission '%s' is not permitted according to ACL (correlationId: '%s')",
|
||||
return false, fmt.Errorf("permission '%s' is not permitted according to ACL (correlationId: '%s')",
|
||||
check.Permission,
|
||||
responseDto.CorrelationID)
|
||||
}
|
||||
|
@ -160,7 +171,6 @@ func checkAclResponse(permissionChecks []types.PermissionCheck, responseDto aclR
|
|||
}
|
||||
|
||||
func mapScope(scope types.Scope) (*aclResourceScope, error) {
|
||||
|
||||
/*
|
||||
* ASSUMPTION:
|
||||
* Harness embeded structure is mapped to the following scm space:
|
||||
|
@ -177,26 +187,34 @@ func mapScope(scope types.Scope) (*aclResourceScope, error) {
|
|||
* TODO: Handle scope.Repository in harness embedded mode
|
||||
*/
|
||||
|
||||
const (
|
||||
accIndex = 0
|
||||
orgIndex = 1
|
||||
projectIndex = 2
|
||||
scopes = 3
|
||||
)
|
||||
|
||||
harnessIdentifiers := strings.Split(scope.SpacePath, "/")
|
||||
if len(harnessIdentifiers) > 3 {
|
||||
return nil, fmt.Errorf("Unable to convert '%s' to harness resource scope (expected {Account}/{Organization}/{Project} or a sub scope).", scope.SpacePath)
|
||||
if len(harnessIdentifiers) > scopes {
|
||||
return nil, fmt.Errorf("unable to convert '%s' to harness resource scope "+
|
||||
"(expected {Account}/{Organization}/{Project} or a sub scope)", scope.SpacePath)
|
||||
}
|
||||
|
||||
aclScope := &aclResourceScope{}
|
||||
if len(harnessIdentifiers) > 0 {
|
||||
aclScope.AccountIdentifier = harnessIdentifiers[0]
|
||||
if len(harnessIdentifiers) > accIndex {
|
||||
aclScope.AccountIdentifier = harnessIdentifiers[accIndex]
|
||||
}
|
||||
if len(harnessIdentifiers) > 1 {
|
||||
aclScope.OrgIdentifier = harnessIdentifiers[1]
|
||||
if len(harnessIdentifiers) > orgIndex {
|
||||
aclScope.OrgIdentifier = harnessIdentifiers[orgIndex]
|
||||
}
|
||||
if len(harnessIdentifiers) > 2 {
|
||||
aclScope.ProjectIdentifier = harnessIdentifiers[2]
|
||||
if len(harnessIdentifiers) > projectIndex {
|
||||
aclScope.ProjectIdentifier = harnessIdentifiers[projectIndex]
|
||||
}
|
||||
|
||||
return aclScope, nil
|
||||
}
|
||||
|
||||
func mapPermission(permission enum.Permission) (string, error) {
|
||||
func mapPermission(permission enum.Permission) string {
|
||||
// harness has multiple modules - add scm prefix
|
||||
return "scm_" + string(permission), nil
|
||||
return "scm_" + string(permission)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
package authz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
@ -22,11 +24,12 @@ func NewUnsafeAuthorizer() Authorizer {
|
|||
return &UnsafeAuthorizer{}
|
||||
}
|
||||
|
||||
func (a *UnsafeAuthorizer) Check(principalType enum.PrincipalType, principalId string, scope *types.Scope, resource *types.Resource, permission enum.Permission) (bool, error) {
|
||||
fmt.Printf(
|
||||
func (a *UnsafeAuthorizer) Check(ctx context.Context, principalType enum.PrincipalType, principalID string,
|
||||
scope *types.Scope, resource *types.Resource, permission enum.Permission) (bool, error) {
|
||||
log.Debug().Msgf(
|
||||
"[Authz] %s '%s' requests %s for %s '%s' in scope %v\n",
|
||||
principalType,
|
||||
principalId,
|
||||
principalID,
|
||||
permission,
|
||||
resource.Type,
|
||||
resource.Name,
|
||||
|
@ -35,9 +38,10 @@ func (a *UnsafeAuthorizer) Check(principalType enum.PrincipalType, principalId s
|
|||
|
||||
return true, nil
|
||||
}
|
||||
func (a *UnsafeAuthorizer) CheckAll(principalType enum.PrincipalType, principalId string, permissionChecks ...types.PermissionCheck) (bool, error) {
|
||||
func (a *UnsafeAuthorizer) CheckAll(ctx context.Context, principalType enum.PrincipalType, principalID string,
|
||||
permissionChecks ...types.PermissionCheck) (bool, error) {
|
||||
for _, p := range permissionChecks {
|
||||
if _, err := a.Check(principalType, principalId, &p.Scope, &p.Resource, p.Permission); err != nil {
|
||||
if _, err := a.Check(ctx, principalType, principalID, &p.Scope, &p.Resource, p.Permission); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
// WireSet provides a wire set for this package
|
||||
// WireSet provides a wire set for this package.
|
||||
var WireSet = wire.NewSet(
|
||||
NewUnsafeAuthorizer,
|
||||
)
|
||||
|
|
|
@ -23,7 +23,8 @@ func NewNightly() *Nightly {
|
|||
|
||||
// Run runs the purge sub-routine.
|
||||
func (n *Nightly) Run(ctx context.Context) {
|
||||
ticker := time.NewTicker(time.Hour * 24)
|
||||
const hoursPerDay = 24
|
||||
ticker := time.NewTicker(hoursPerDay * time.Hour)
|
||||
logger := log.Ctx(ctx)
|
||||
for {
|
||||
select {
|
||||
|
|
|
@ -6,5 +6,5 @@ package cron
|
|||
|
||||
import "github.com/google/wire"
|
||||
|
||||
// WireSet provides a wire set for this package
|
||||
// WireSet provides a wire set for this package.
|
||||
var WireSet = wire.NewSet(NewNightly)
|
||||
|
|
|
@ -12,13 +12,11 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrPathEmpty = errors.New("Path is empty.")
|
||||
ErrPathEmpty = errors.New("path is empty")
|
||||
)
|
||||
|
||||
/*
|
||||
* Splits a path into its parent path and the leaf name.
|
||||
* e.g. /space1/space2/space3 -> (/space1/space2, space3, nil)
|
||||
*/
|
||||
// Disect splits a path into its parent path and the leaf name
|
||||
// e.g. /space1/space2/space3 -> (/space1/space2, space3, nil).
|
||||
func Disect(path string) (string, string, error) {
|
||||
if path == "" {
|
||||
return "", "", ErrPathEmpty
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/types"
|
||||
|
||||
"github.com/harness/gitness/internal/api/guard"
|
||||
"github.com/harness/gitness/internal/api/handler/account"
|
||||
handler_repo "github.com/harness/gitness/internal/api/handler/repo"
|
||||
|
@ -31,22 +34,21 @@ import (
|
|||
* Mounts the Rest API Router under mountPath (path has to end with ).
|
||||
* The handler is wrapped within a layer that handles encoding terminated Paths.
|
||||
*/
|
||||
func newApiHandler(
|
||||
func newAPIHandler(
|
||||
mountPath string,
|
||||
systemStore store.SystemStore,
|
||||
userStore store.UserStore,
|
||||
spaceStore store.SpaceStore,
|
||||
repoStore store.RepoStore,
|
||||
authenticator authn.Authenticator,
|
||||
authorizer authz.Authorizer) (http.Handler, error) {
|
||||
|
||||
config := systemStore.Config(nocontext)
|
||||
guard := guard.New(authorizer)
|
||||
authorizer authz.Authorizer) http.Handler {
|
||||
//
|
||||
config := systemStore.Config(context.Background())
|
||||
g := guard.New(authorizer)
|
||||
|
||||
// Use go-chi router for inner routing (restricted to mountPath!)
|
||||
r := chi.NewRouter()
|
||||
r.Route(mountPath, func(r chi.Router) {
|
||||
|
||||
// Apply common api middleware
|
||||
r.Use(middleware.NoCache)
|
||||
r.Use(middleware.Recoverer)
|
||||
|
@ -59,23 +61,13 @@ func newApiHandler(
|
|||
r.Use(accesslog.HlogHandler())
|
||||
|
||||
// configure cors middleware
|
||||
cors := cors.New(
|
||||
cors.Options{
|
||||
AllowedOrigins: config.Cors.AllowedOrigins,
|
||||
AllowedMethods: config.Cors.AllowedMethods,
|
||||
AllowedHeaders: config.Cors.AllowedHeaders,
|
||||
ExposedHeaders: config.Cors.ExposedHeaders,
|
||||
AllowCredentials: config.Cors.AllowCredentials,
|
||||
MaxAge: config.Cors.MaxAge,
|
||||
},
|
||||
)
|
||||
r.Use(cors.Handler)
|
||||
r.Use(corsHandler(config))
|
||||
|
||||
// for now always attempt auth - enforced per operation
|
||||
r.Use(middleware_authn.Attempt(authenticator))
|
||||
|
||||
r.Route("/v1", func(r chi.Router) {
|
||||
setupRoutesV1(r, systemStore, userStore, spaceStore, repoStore, authenticator, guard)
|
||||
setupRoutesV1(r, systemStore, userStore, spaceStore, repoStore, authenticator, g)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -85,7 +77,20 @@ func newApiHandler(
|
|||
mountPath + "/v1/repos",
|
||||
}
|
||||
|
||||
return encode.TerminatedPathBefore(terminatedPathPrefixes, r.ServeHTTP), nil
|
||||
return encode.TerminatedPathBefore(terminatedPathPrefixes, r.ServeHTTP)
|
||||
}
|
||||
|
||||
func corsHandler(config *types.Config) func(http.Handler) http.Handler {
|
||||
return cors.New(
|
||||
cors.Options{
|
||||
AllowedOrigins: config.Cors.AllowedOrigins,
|
||||
AllowedMethods: config.Cors.AllowedMethods,
|
||||
AllowedHeaders: config.Cors.AllowedHeaders,
|
||||
ExposedHeaders: config.Cors.ExposedHeaders,
|
||||
AllowCredentials: config.Cors.AllowCredentials,
|
||||
MaxAge: config.Cors.MaxAge,
|
||||
},
|
||||
).Handler
|
||||
}
|
||||
|
||||
func setupRoutesV1(
|
||||
|
@ -94,9 +99,8 @@ func setupRoutesV1(
|
|||
userStore store.UserStore,
|
||||
spaceStore store.SpaceStore,
|
||||
repoStore store.RepoStore,
|
||||
authenticator authn.Authenticator,
|
||||
_ authn.Authenticator,
|
||||
guard *guard.Guard) {
|
||||
|
||||
// SPACES
|
||||
r.Route("/spaces", func(r chi.Router) {
|
||||
// Create takes path and parentId via body, not uri
|
||||
|
@ -121,7 +125,7 @@ func setupRoutesV1(
|
|||
r.Post("/", handler_space.HandleCreatePath(guard, spaceStore))
|
||||
|
||||
// per path operations
|
||||
r.Route(fmt.Sprintf("/{%s}", request.PathIdParamName), func(r chi.Router) {
|
||||
r.Route(fmt.Sprintf("/{%s}", request.PathIDParamName), func(r chi.Router) {
|
||||
r.Delete("/", handler_space.HandleDeletePath(guard, spaceStore))
|
||||
})
|
||||
})
|
||||
|
@ -150,7 +154,7 @@ func setupRoutesV1(
|
|||
r.Post("/", handler_repo.HandleCreatePath(guard, repoStore))
|
||||
|
||||
// per path operations
|
||||
r.Route(fmt.Sprintf("/{%s}", request.PathIdParamName), func(r chi.Router) {
|
||||
r.Route(fmt.Sprintf("/{%s}", request.PathIDParamName), func(r chi.Router) {
|
||||
r.Delete("/", handler_repo.HandleDeletePath(guard, repoStore))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -27,19 +27,16 @@ import (
|
|||
*/
|
||||
func newGitHandler(
|
||||
mountPath string,
|
||||
systemStore store.SystemStore,
|
||||
userStore store.UserStore,
|
||||
spaceStore store.SpaceStore,
|
||||
_ store.SystemStore,
|
||||
_ store.UserStore,
|
||||
_ store.SpaceStore,
|
||||
repoStore store.RepoStore,
|
||||
authenticator authn.Authenticator,
|
||||
authorizer authz.Authorizer) (http.Handler, error) {
|
||||
|
||||
authorizer authz.Authorizer) http.Handler {
|
||||
guard := guard.New(authorizer)
|
||||
|
||||
// Use go-chi router for inner routing (restricted to mountPath!)
|
||||
r := chi.NewRouter()
|
||||
r.Route(mountPath, func(r chi.Router) {
|
||||
|
||||
// Apply common api middleware
|
||||
r.Use(middleware.NoCache)
|
||||
r.Use(middleware.Recoverer)
|
||||
|
@ -68,9 +65,9 @@ func newGitHandler(
|
|||
|
||||
// Read operations (only need of it not public)
|
||||
r.Group(func(r chi.Router) {
|
||||
|
||||
// middlewares
|
||||
r.Use(guard.ForRepo(enum.PermissionRepoView, true))
|
||||
|
||||
// handlers
|
||||
r.Post("/git-receive-pack", stubGitHandler)
|
||||
r.Get("/info/refs", stubGitHandler)
|
||||
r.Get("/HEAD", stubGitHandler)
|
||||
|
@ -85,7 +82,7 @@ func newGitHandler(
|
|||
})
|
||||
})
|
||||
|
||||
return encode.GitPathBefore(r.ServeHTTP), nil
|
||||
return encode.GitPathBefore(r.ServeHTTP)
|
||||
}
|
||||
|
||||
func stubGitHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
|
@ -21,9 +20,6 @@ const (
|
|||
gitUserAgentPrefix = "git/"
|
||||
)
|
||||
|
||||
// empty context
|
||||
var nocontext = context.Background()
|
||||
|
||||
type Router struct {
|
||||
api http.Handler
|
||||
git http.Handler
|
||||
|
@ -40,18 +36,9 @@ func New(
|
|||
authenticator authn.Authenticator,
|
||||
authorizer authz.Authorizer,
|
||||
) (http.Handler, error) {
|
||||
api, err := newApiHandler(restMount, systemStore, userStore, spaceStore, repoStore, authenticator, authorizer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
git, err := newGitHandler("/", systemStore, userStore, spaceStore, repoStore, authenticator, authorizer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
web, err := newWebHandler("/", systemStore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
api := newAPIHandler(restMount, systemStore, userStore, spaceStore, repoStore, authenticator, authorizer)
|
||||
git := newGitHandler("/", systemStore, userStore, spaceStore, repoStore, authenticator, authorizer)
|
||||
web := newWebHandler("/", systemStore)
|
||||
|
||||
return &Router{
|
||||
api: api,
|
||||
|
@ -61,7 +48,6 @@ func New(
|
|||
}
|
||||
|
||||
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
/*
|
||||
* 1. GIT
|
||||
*
|
||||
|
|
|
@ -15,14 +15,14 @@ func TestTokenGate(t *testing.T) {
|
|||
|
||||
// this unit test ensures routes that require pipeline access
|
||||
// return a 403 forbidden if the user does not have acess
|
||||
// to the pipeline
|
||||
// to the pipeline.
|
||||
func TestPipelineGate(t *testing.T) {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
// this unit test ensures routes that require system access
|
||||
// return a 403 forbidden if the user does not have acess
|
||||
// to the pipeline
|
||||
// to the pipeline.
|
||||
func TestSystemGate(t *testing.T) {
|
||||
t.Skip()
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/internal/api/middleware/encode"
|
||||
|
@ -18,14 +19,13 @@ import (
|
|||
*/
|
||||
func newWebHandler(
|
||||
mountPath string,
|
||||
systemStore store.SystemStore) (http.Handler, error) {
|
||||
|
||||
config := systemStore.Config(nocontext)
|
||||
systemStore store.SystemStore) http.Handler {
|
||||
//
|
||||
config := systemStore.Config(context.Background())
|
||||
|
||||
// Use go-chi router for inner routing (restricted to mountPath!)
|
||||
r := chi.NewRouter()
|
||||
r.Route(mountPath, func(r chi.Router) {
|
||||
|
||||
// create middleware to enforce security best practices for
|
||||
// the user interface. note that theis middleware is only used
|
||||
// when serving the user interface (not found handler, below).
|
||||
|
@ -62,5 +62,5 @@ func newWebHandler(
|
|||
})
|
||||
|
||||
// web doesn't have any prefixes for terminated paths
|
||||
return encode.TerminatedPathBefore([]string{""}, r.ServeHTTP), nil
|
||||
return encode.TerminatedPathBefore([]string{""}, r.ServeHTTP)
|
||||
}
|
||||
|
|
|
@ -8,5 +8,5 @@ import (
|
|||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
// WireSet provides a wire set for this package
|
||||
// WireSet provides a wire set for this package.
|
||||
var WireSet = wire.NewSet(New)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"context"
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
@ -37,8 +38,9 @@ func (s *Server) ListenAndServe(ctx context.Context) error {
|
|||
func (s *Server) listenAndServe(ctx context.Context) error {
|
||||
var g errgroup.Group
|
||||
s1 := &http.Server{
|
||||
Addr: s.Addr,
|
||||
Handler: s.Handler,
|
||||
Addr: s.Addr,
|
||||
ReadHeaderTimeout: 2 * time.Second,
|
||||
Handler: s.Handler,
|
||||
}
|
||||
g.Go(func() error {
|
||||
<-ctx.Done()
|
||||
|
@ -53,12 +55,14 @@ func (s *Server) listenAndServe(ctx context.Context) error {
|
|||
func (s *Server) listenAndServeTLS(ctx context.Context) error {
|
||||
var g errgroup.Group
|
||||
s1 := &http.Server{
|
||||
Addr: ":http",
|
||||
Handler: http.HandlerFunc(redirect),
|
||||
Addr: ":http",
|
||||
ReadHeaderTimeout: 2 * time.Second,
|
||||
Handler: http.HandlerFunc(redirect),
|
||||
}
|
||||
s2 := &http.Server{
|
||||
Addr: ":https",
|
||||
Handler: s.Handler,
|
||||
Addr: ":https",
|
||||
ReadHeaderTimeout: 2 * time.Second,
|
||||
Handler: s.Handler,
|
||||
}
|
||||
g.Go(func() error {
|
||||
return s1.ListenAndServe()
|
||||
|
@ -90,13 +94,16 @@ func (s Server) listenAndServeAcme(ctx context.Context) error {
|
|||
HostPolicy: autocert.HostWhitelist(s.Host),
|
||||
}
|
||||
s1 := &http.Server{
|
||||
Addr: ":http",
|
||||
Handler: m.HTTPHandler(nil),
|
||||
Addr: ":http",
|
||||
ReadHeaderTimeout: 2 * time.Second,
|
||||
Handler: m.HTTPHandler(nil),
|
||||
}
|
||||
s2 := &http.Server{
|
||||
Addr: ":https",
|
||||
Handler: s.Handler,
|
||||
Addr: ":https",
|
||||
Handler: s.Handler,
|
||||
ReadHeaderTimeout: 2 * time.Second,
|
||||
TLSConfig: &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
GetCertificate: m.GetCertificate,
|
||||
NextProtos: []string{"h2", "http/1.1"},
|
||||
},
|
||||
|
|
|
@ -12,10 +12,10 @@ import (
|
|||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
// WireSet provides a wire set for this package
|
||||
// WireSet provides a wire set for this package.
|
||||
var WireSet = wire.NewSet(ProvideServer)
|
||||
|
||||
// ProvideServer provides a server instance
|
||||
// ProvideServer provides a server instance.
|
||||
func ProvideServer(config *types.Config, handler http.Handler) *Server {
|
||||
return &Server{
|
||||
Acme: config.Server.Acme.Enabled,
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// background context
|
||||
// noContext is simple background context.
|
||||
var noContext = context.Background()
|
||||
|
||||
//go:embed postgres/*.sql
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// Creates a new alias path (Don't call this for new path creation!)
|
||||
// CreateAliasPath a new alias path (Don't call this for new path creation!)
|
||||
func CreateAliasPath(ctx context.Context, db *sqlx.DB, path *types.Path) error {
|
||||
if !path.IsAlias {
|
||||
return store.ErrAliasPathRequired
|
||||
|
@ -32,19 +32,18 @@ func CreateAliasPath(ctx context.Context, db *sqlx.DB, path *types.Path) error {
|
|||
|
||||
query, arg, err := db.BindNamed(pathInsert, path)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to bind path object")
|
||||
return processSQLErrorf(err, "Failed to bind path object")
|
||||
}
|
||||
|
||||
if err = db.QueryRowContext(ctx, query, arg...).Scan(&path.ID); err != nil {
|
||||
return processSqlErrorf(err, "Insert query failed")
|
||||
return processSQLErrorf(err, "Insert query failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Creates a new path as part of a transaction
|
||||
// CreatePathTx creates a new path as part of a transaction.
|
||||
func CreatePathTx(ctx context.Context, db *sqlx.DB, tx *sqlx.Tx, path *types.Path) error {
|
||||
|
||||
// ensure path length is okay
|
||||
if check.PathTooLong(path.Value, path.TargetType == enum.PathTargetTypeSpace) {
|
||||
log.Warn().Msgf("Path '%s' is too long.", path.Value)
|
||||
|
@ -53,7 +52,7 @@ func CreatePathTx(ctx context.Context, db *sqlx.DB, tx *sqlx.Tx, path *types.Pat
|
|||
|
||||
// In case it's not an alias, ensure there are no duplicates
|
||||
if !path.IsAlias {
|
||||
if cnt, err := CountPathsTx(ctx, tx, path.TargetType, path.TargetId); err != nil {
|
||||
if cnt, err := CountPathsTx(ctx, tx, path.TargetType, path.TargetID); err != nil {
|
||||
return err
|
||||
} else if cnt > 0 {
|
||||
return store.ErrPrimaryPathAlreadyExists
|
||||
|
@ -62,11 +61,11 @@ func CreatePathTx(ctx context.Context, db *sqlx.DB, tx *sqlx.Tx, path *types.Pat
|
|||
|
||||
query, arg, err := db.BindNamed(pathInsert, path)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to bind path object")
|
||||
return processSQLErrorf(err, "Failed to bind path object")
|
||||
}
|
||||
|
||||
if err = tx.QueryRowContext(ctx, query, arg...).Scan(&path.ID); err != nil {
|
||||
return processSqlErrorf(err, "Insert query failed")
|
||||
return processSQLErrorf(err, "Insert query failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -76,7 +75,7 @@ func CountPrimaryChildPathsTx(ctx context.Context, tx *sqlx.Tx, prefix string) (
|
|||
var count int64
|
||||
err := tx.QueryRowContext(ctx, pathCountPrimaryForPrefix, paths.Concatinate(prefix, "%")).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, processSqlErrorf(err, "Count query failed")
|
||||
return 0, processSQLErrorf(err, "Count query failed")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
@ -85,13 +84,13 @@ func ListPrimaryChildPathsTx(ctx context.Context, tx *sqlx.Tx, prefix string) ([
|
|||
childs := []*types.Path{}
|
||||
|
||||
if err := tx.SelectContext(ctx, &childs, pathSelectPrimaryForPrefix, paths.Concatinate(prefix, "%")); err != nil {
|
||||
return nil, processSqlErrorf(err, "Select query failed")
|
||||
return nil, processSQLErrorf(err, "Select query failed")
|
||||
}
|
||||
|
||||
return childs, nil
|
||||
}
|
||||
|
||||
// Replaces the path for a target as part of a transaction - keeps the existing as alias if requested.
|
||||
// ReplacePathTx replace the path for a target as part of a transaction - keeps the existing as alias if requested.
|
||||
func ReplacePathTx(ctx context.Context, db *sqlx.DB, tx *sqlx.Tx, path *types.Path, keepAsAlias bool) error {
|
||||
|
||||
if path.IsAlias {
|
||||
|
@ -106,21 +105,21 @@ func ReplacePathTx(ctx context.Context, db *sqlx.DB, tx *sqlx.Tx, path *types.Pa
|
|||
|
||||
// existing is always non-alias (as query filters for IsAlias=0)
|
||||
existing := new(types.Path)
|
||||
err := tx.GetContext(ctx, existing, pathSelectPrimaryForTarget, string(path.TargetType), fmt.Sprint(path.TargetId))
|
||||
err := tx.GetContext(ctx, existing, pathSelectPrimaryForTarget, string(path.TargetType), fmt.Sprint(path.TargetID))
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to get the existing primary path")
|
||||
return processSQLErrorf(err, "Failed to get the existing primary path")
|
||||
}
|
||||
|
||||
// Only look for childs if the type can have childs
|
||||
// Only look for children if the type can have children
|
||||
if path.TargetType == enum.PathTargetTypeSpace {
|
||||
|
||||
var childPaths []*types.Path
|
||||
// get all primary paths that start with the current path before updating (or we can run into recursion)
|
||||
childs, err := ListPrimaryChildPathsTx(ctx, tx, existing.Value)
|
||||
childPaths, err = ListPrimaryChildPathsTx(ctx, tx, existing.Value)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Failed to get primary child paths for '%s'", existing.Value)
|
||||
}
|
||||
|
||||
for _, child := range childs {
|
||||
for _, child := range childPaths {
|
||||
// create path with updated path (child already is primary)
|
||||
updatedChild := new(types.Path)
|
||||
*updatedChild = *child
|
||||
|
@ -136,24 +135,28 @@ func ReplacePathTx(ctx context.Context, db *sqlx.DB, tx *sqlx.Tx, path *types.Pa
|
|||
return store.ErrPathTooLong
|
||||
}
|
||||
|
||||
query, arg, err := db.BindNamed(pathInsert, updatedChild)
|
||||
var (
|
||||
query string
|
||||
args []interface{}
|
||||
)
|
||||
|
||||
query, args, err = db.BindNamed(pathInsert, updatedChild)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to bind path object")
|
||||
return processSQLErrorf(err, "Failed to bind path object")
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(ctx, query, arg...)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to create new primary child path '%s'", updatedChild.Value)
|
||||
if _, err = tx.ExecContext(ctx, query, args...); err != nil {
|
||||
return processSQLErrorf(err, "Failed to create new primary child path '%s'", updatedChild.Value)
|
||||
}
|
||||
|
||||
// make current child an alias or delete it
|
||||
query = pathDeleteID
|
||||
if keepAsAlias {
|
||||
_, err = tx.ExecContext(ctx, pathMakeAlias, child.ID)
|
||||
} else {
|
||||
_, err = tx.ExecContext(ctx, pathDeleteId, child.ID)
|
||||
query = pathMakeAlias
|
||||
}
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to mark existing child path '%s' as alias", updatedChild.Value)
|
||||
if _, err = tx.ExecContext(ctx, query, child.ID); err != nil {
|
||||
return processSQLErrorf(err, "Failed to mark existing child path '%s' as alias",
|
||||
updatedChild.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,33 +164,32 @@ func ReplacePathTx(ctx context.Context, db *sqlx.DB, tx *sqlx.Tx, path *types.Pa
|
|||
// insert the new Path
|
||||
query, arg, err := db.BindNamed(pathInsert, path)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to bind path object")
|
||||
return processSQLErrorf(err, "Failed to bind path object")
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(ctx, query, arg...)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to create new primary path '%s'", path.Value)
|
||||
return processSQLErrorf(err, "Failed to create new primary path '%s'", path.Value)
|
||||
}
|
||||
|
||||
// make existing an alias
|
||||
query = pathDeleteID
|
||||
if keepAsAlias {
|
||||
_, err = tx.ExecContext(ctx, pathMakeAlias, existing.ID)
|
||||
} else {
|
||||
_, err = tx.ExecContext(ctx, pathDeleteId, existing.ID)
|
||||
query = pathMakeAlias
|
||||
}
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to mark existing path '%s' as alias", existing.Value)
|
||||
if _, err = tx.ExecContext(ctx, query, existing.ID); err != nil {
|
||||
return processSQLErrorf(err, "Failed to mark existing path '%s' as alias", existing.Value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Finds the primary path for a target.
|
||||
func FindPathTx(ctx context.Context, tx *sqlx.Tx, targetType enum.PathTargetType, targetId int64) (*types.Path, error) {
|
||||
// FindPathTx finds the primary path for a target.
|
||||
func FindPathTx(ctx context.Context, tx *sqlx.Tx, targetType enum.PathTargetType, targetID int64) (*types.Path, error) {
|
||||
dst := new(types.Path)
|
||||
err := tx.GetContext(ctx, dst, pathSelectPrimaryForTarget, string(targetType), fmt.Sprint(targetId))
|
||||
err := tx.GetContext(ctx, dst, pathSelectPrimaryForTarget, string(targetType), fmt.Sprint(targetID))
|
||||
if err != nil {
|
||||
return nil, processSqlErrorf(err, "Select query failed")
|
||||
return nil, processSQLErrorf(err, "Select query failed")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
|
@ -197,7 +199,7 @@ func FindPathTx(ctx context.Context, tx *sqlx.Tx, targetType enum.PathTargetType
|
|||
func DeletePath(ctx context.Context, db *sqlx.DB, id int64) error {
|
||||
tx, err := db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to start a new transaction")
|
||||
return processSQLErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer func(tx *sqlx.Tx) {
|
||||
_ = tx.Rollback()
|
||||
|
@ -205,62 +207,65 @@ func DeletePath(ctx context.Context, db *sqlx.DB, id int64) error {
|
|||
|
||||
// ensure path is an alias
|
||||
dst := new(types.Path)
|
||||
if err = tx.GetContext(ctx, dst, pathSelectId, id); err != nil {
|
||||
return processSqlErrorf(err, "Failed to find path with id %d", id)
|
||||
if err = tx.GetContext(ctx, dst, pathSelectID, id); err != nil {
|
||||
return processSQLErrorf(err, "Failed to find path with id %d", id)
|
||||
} else if !dst.IsAlias {
|
||||
return store.ErrPrimaryPathCantBeDeleted
|
||||
}
|
||||
|
||||
// delete the path
|
||||
if _, err = tx.ExecContext(ctx, pathDeleteId, id); err != nil {
|
||||
return processSqlErrorf(err, "Delete query failed")
|
||||
if _, err = tx.ExecContext(ctx, pathDeleteID, id); err != nil {
|
||||
return processSQLErrorf(err, "Delete query failed")
|
||||
}
|
||||
|
||||
if err = tx.Commit(); err != nil {
|
||||
return processSqlErrorf(err, "Failed to commit transaction")
|
||||
return processSQLErrorf(err, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deletes all paths for a target as part of a transaction.
|
||||
func DeleteAllPaths(ctx context.Context, tx *sqlx.Tx, targetType enum.PathTargetType, targetId int64) error {
|
||||
func DeleteAllPaths(ctx context.Context, tx *sqlx.Tx, targetType enum.PathTargetType, targetID int64) error {
|
||||
// delete all entries for the target
|
||||
if _, err := tx.ExecContext(ctx, pathDeleteTarget, string(targetType), fmt.Sprint(targetId)); err != nil {
|
||||
return processSqlErrorf(err, "Query for deleting all pahts failed")
|
||||
if _, err := tx.ExecContext(ctx, pathDeleteTarget, string(targetType), fmt.Sprint(targetID)); err != nil {
|
||||
return processSQLErrorf(err, "Query for deleting all pahts failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Lists all paths for a target.
|
||||
func ListPaths(ctx context.Context, db *sqlx.DB, targetType enum.PathTargetType, targetId int64, opts *types.PathFilter) ([]*types.Path, error) {
|
||||
func ListPaths(ctx context.Context, db *sqlx.DB, targetType enum.PathTargetType, targetID int64,
|
||||
opts *types.PathFilter) ([]*types.Path, error) {
|
||||
dst := []*types.Path{}
|
||||
|
||||
// if the user does not provide any customer filter
|
||||
// or sorting we use the default select statement.
|
||||
if opts.Sort == enum.PathAttrNone {
|
||||
err := db.SelectContext(ctx, &dst, pathSelect, string(targetType), fmt.Sprint(targetId), limit(opts.Size), offset(opts.Page, opts.Size))
|
||||
err := db.SelectContext(ctx, &dst, pathSelect, string(targetType), fmt.Sprint(targetID), limit(opts.Size),
|
||||
offset(opts.Page, opts.Size))
|
||||
if err != nil {
|
||||
return nil, processSqlErrorf(err, "Default select query failed")
|
||||
return nil, processSQLErrorf(err, "Default select query failed")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// else we construct the sql statement.
|
||||
stmt := builder.Select("*").From("paths").Where("path_targetType = $1 AND path_targetId = $2", string(targetType), fmt.Sprint(targetId))
|
||||
stmt := builder.Select("*").From("paths").Where("path_targetType = $1 AND path_targetId = $2",
|
||||
string(targetType), fmt.Sprint(targetID))
|
||||
stmt = stmt.Limit(uint64(limit(opts.Size)))
|
||||
stmt = stmt.Offset(uint64(offset(opts.Page, opts.Size)))
|
||||
|
||||
switch opts.Sort {
|
||||
case enum.PathAttrCreated:
|
||||
// NOTE: string concatination is safe because the
|
||||
// NOTE: string concatenation is safe because the
|
||||
// order attribute is an enum and is not user-defined,
|
||||
// and is therefore not subject to injection attacks.
|
||||
stmt = stmt.OrderBy("path_created " + opts.Order.String())
|
||||
case enum.PathAttrUpdated:
|
||||
stmt = stmt.OrderBy("path_updated " + opts.Order.String())
|
||||
case enum.PathAttrId:
|
||||
case enum.PathAttrID:
|
||||
stmt = stmt.OrderBy("path_id " + opts.Order.String())
|
||||
case enum.PathAttrPath:
|
||||
stmt = stmt.OrderBy("path_value" + opts.Order.String())
|
||||
|
@ -272,28 +277,18 @@ func ListPaths(ctx context.Context, db *sqlx.DB, targetType enum.PathTargetType,
|
|||
}
|
||||
|
||||
if err = db.SelectContext(ctx, &dst, sql); err != nil {
|
||||
return nil, processSqlErrorf(err, "Customer select query failed")
|
||||
return nil, processSQLErrorf(err, "Customer select query failed")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// Coutn paths for a target.
|
||||
func CountPaths(ctx context.Context, db *sqlx.DB, targetType enum.PathTargetType, targetId int64) (int64, error) {
|
||||
// CountPathsTx Count paths for a target as part of a transaction.
|
||||
func CountPathsTx(ctx context.Context, tx *sqlx.Tx, targetType enum.PathTargetType, targetID int64) (int64, error) {
|
||||
var count int64
|
||||
err := db.QueryRowContext(ctx, pathCount, string(targetType), fmt.Sprint(targetId)).Scan(&count)
|
||||
err := tx.QueryRowContext(ctx, pathCount, string(targetType), fmt.Sprint(targetID)).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, processSqlErrorf(err, "Query failed")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// Count paths for a target as part of a transaction.
|
||||
func CountPathsTx(ctx context.Context, tx *sqlx.Tx, targetType enum.PathTargetType, targetId int64) (int64, error) {
|
||||
var count int64
|
||||
err := tx.QueryRowContext(ctx, pathCount, string(targetType), fmt.Sprint(targetId)).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, processSqlErrorf(err, "Query failed")
|
||||
return 0, processSQLErrorf(err, "Query failed")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
@ -316,7 +311,7 @@ ORDER BY path_isAlias DESC, path_value ASC
|
|||
LIMIT $3 OFFSET $4
|
||||
`
|
||||
|
||||
// there's only one entry with a given target & targetId for isAlias -- false
|
||||
// there's only one entry with a given target & targetId for isAlias -- false.
|
||||
const pathSelectPrimaryForTarget = pathBase + `
|
||||
WHERE path_targetType = $1 AND path_targetId = $2 AND path_isAlias = 0
|
||||
`
|
||||
|
@ -357,11 +352,11 @@ INSERT INTO paths (
|
|||
) RETURNING path_id
|
||||
`
|
||||
|
||||
const pathSelectId = pathBase + `
|
||||
const pathSelectID = pathBase + `
|
||||
WHERE path_id = $1
|
||||
`
|
||||
|
||||
const pathDeleteId = `
|
||||
const pathDeleteID = `
|
||||
DELETE FROM paths
|
||||
WHERE path_id = $1
|
||||
`
|
||||
|
|
|
@ -33,8 +33,8 @@ type RepoStore struct {
|
|||
// Finds the repo by id.
|
||||
func (s *RepoStore) Find(ctx context.Context, id int64) (*types.Repository, error) {
|
||||
dst := new(types.Repository)
|
||||
if err := s.db.GetContext(ctx, dst, repoSelectById, id); err != nil {
|
||||
return nil, processSqlErrorf(err, "Select query failed")
|
||||
if err := s.db.GetContext(ctx, dst, repoSelectByID, id); err != nil {
|
||||
return nil, processSQLErrorf(err, "Select query failed")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -43,16 +43,16 @@ func (s *RepoStore) Find(ctx context.Context, id int64) (*types.Repository, erro
|
|||
func (s *RepoStore) FindByPath(ctx context.Context, path string) (*types.Repository, error) {
|
||||
dst := new(types.Repository)
|
||||
if err := s.db.GetContext(ctx, dst, repoSelectByPath, path); err != nil {
|
||||
return nil, processSqlErrorf(err, "Select query failed")
|
||||
return nil, processSQLErrorf(err, "Select query failed")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// Creates a new repo
|
||||
// Create creates a new repository.
|
||||
func (s *RepoStore) Create(ctx context.Context, repo *types.Repository) error {
|
||||
tx, err := s.db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to start a new transaction")
|
||||
return processSQLErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer func(tx *sqlx.Tx) {
|
||||
_ = tx.Rollback()
|
||||
|
@ -61,15 +61,15 @@ func (s *RepoStore) Create(ctx context.Context, repo *types.Repository) error {
|
|||
// insert repo first so we get id
|
||||
query, arg, err := s.db.BindNamed(repoInsert, repo)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to bind repo object")
|
||||
return processSQLErrorf(err, "Failed to bind repo object")
|
||||
}
|
||||
|
||||
if err = tx.QueryRow(query, arg...).Scan(&repo.ID); err != nil {
|
||||
return processSqlErrorf(err, "Insert query failed")
|
||||
return processSQLErrorf(err, "Insert query failed")
|
||||
}
|
||||
|
||||
// Get parent path (repo always has a parent)
|
||||
parentPath, err := FindPathTx(ctx, tx, enum.PathTargetTypeSpace, repo.SpaceId)
|
||||
parentPath, err := FindPathTx(ctx, tx, enum.PathTargetTypeSpace, repo.SpaceID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to find path of parent space")
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ func (s *RepoStore) Create(ctx context.Context, repo *types.Repository) error {
|
|||
// create path only once we know the id of the repo
|
||||
p := &types.Path{
|
||||
TargetType: enum.PathTargetTypeRepo,
|
||||
TargetId: repo.ID,
|
||||
TargetID: repo.ID,
|
||||
IsAlias: false,
|
||||
Value: path,
|
||||
CreatedBy: repo.CreatedBy,
|
||||
|
@ -94,7 +94,7 @@ func (s *RepoStore) Create(ctx context.Context, repo *types.Repository) error {
|
|||
|
||||
// commit
|
||||
if err = tx.Commit(); err != nil {
|
||||
return processSqlErrorf(err, "Failed to commit transaction")
|
||||
return processSQLErrorf(err, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
// update path in repo object
|
||||
|
@ -103,24 +103,25 @@ func (s *RepoStore) Create(ctx context.Context, repo *types.Repository) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Moves an existing space.
|
||||
func (s *RepoStore) Move(ctx context.Context, userId int64, repoId int64, newSpaceId int64, newName string, keepAsAlias bool) (*types.Repository, error) {
|
||||
// Move moves an existing space.
|
||||
func (s *RepoStore) Move(ctx context.Context, userID int64, repoID int64, newSpaceID int64, newName string,
|
||||
keepAsAlias bool) (*types.Repository, error) {
|
||||
tx, err := s.db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, processSqlErrorf(err, "Failed to start a new transaction")
|
||||
return nil, processSQLErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer func(tx *sqlx.Tx) {
|
||||
_ = tx.Rollback() // should we take care about rollbacks errors?
|
||||
}(tx)
|
||||
|
||||
// get current path of repo
|
||||
currentPath, err := FindPathTx(ctx, tx, enum.PathTargetTypeRepo, repoId)
|
||||
currentPath, err := FindPathTx(ctx, tx, enum.PathTargetTypeRepo, repoID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to find the primary path of the repo")
|
||||
}
|
||||
|
||||
// get path of new parent space
|
||||
spacePath, err := FindPathTx(ctx, tx, enum.PathTargetTypeSpace, newSpaceId)
|
||||
spacePath, err := FindPathTx(ctx, tx, enum.PathTargetTypeSpace, newSpaceID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to find the primary path of the new space")
|
||||
}
|
||||
|
@ -132,10 +133,10 @@ func (s *RepoStore) Move(ctx context.Context, userId int64, repoId int64, newSpa
|
|||
|
||||
p := &types.Path{
|
||||
TargetType: enum.PathTargetTypeRepo,
|
||||
TargetId: repoId,
|
||||
TargetID: repoID,
|
||||
IsAlias: false,
|
||||
Value: newPath,
|
||||
CreatedBy: userId,
|
||||
CreatedBy: userID,
|
||||
Created: time.Now().UnixMilli(),
|
||||
Updated: time.Now().UnixMilli(),
|
||||
}
|
||||
|
@ -146,19 +147,19 @@ func (s *RepoStore) Move(ctx context.Context, userId int64, repoId int64, newSpa
|
|||
}
|
||||
|
||||
// Rename the repo itself
|
||||
if _, err := tx.ExecContext(ctx, repoUpdateNameAndSpaceId, newName, newSpaceId, repoId); err != nil {
|
||||
return nil, processSqlErrorf(err, "Query for renaming and updating the space id failed")
|
||||
if _, err = tx.ExecContext(ctx, repoUpdateNameAndSpaceID, newName, newSpaceID, repoID); err != nil {
|
||||
return nil, processSQLErrorf(err, "Query for renaming and updating the space id failed")
|
||||
}
|
||||
|
||||
// TODO: return repo as part of rename db operation?
|
||||
dst := new(types.Repository)
|
||||
if err = tx.GetContext(ctx, dst, repoSelectById, repoId); err != nil {
|
||||
return nil, processSqlErrorf(err, "Select query to get the repo's latest state failed")
|
||||
if err = tx.GetContext(ctx, dst, repoSelectByID, repoID); err != nil {
|
||||
return nil, processSQLErrorf(err, "Select query to get the repo's latest state failed")
|
||||
}
|
||||
|
||||
// commit
|
||||
if err = tx.Commit(); err != nil {
|
||||
return nil, processSqlErrorf(err, "Failed to commit transaction")
|
||||
return nil, processSQLErrorf(err, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
|
@ -168,21 +169,21 @@ func (s *RepoStore) Move(ctx context.Context, userId int64, repoId int64, newSpa
|
|||
func (s *RepoStore) Update(ctx context.Context, repo *types.Repository) error {
|
||||
query, arg, err := s.db.BindNamed(repoUpdate, repo)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to bind repo object")
|
||||
return processSQLErrorf(err, "Failed to bind repo object")
|
||||
}
|
||||
|
||||
if _, err = s.db.ExecContext(ctx, query, arg...); err != nil {
|
||||
return processSqlErrorf(err, "Update query failed")
|
||||
return processSQLErrorf(err, "Update query failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deletes the repo.
|
||||
// Delete the repository.
|
||||
func (s *RepoStore) Delete(ctx context.Context, id int64) error {
|
||||
tx, err := s.db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to start a new transaction")
|
||||
return processSQLErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer func(tx *sqlx.Tx) {
|
||||
_ = tx.Rollback()
|
||||
|
@ -195,38 +196,38 @@ func (s *RepoStore) Delete(ctx context.Context, id int64) error {
|
|||
}
|
||||
|
||||
// delete the repo
|
||||
if _, err := tx.ExecContext(ctx, repoDelete, id); err != nil {
|
||||
return processSqlErrorf(err, "The delete query failed")
|
||||
if _, err = tx.ExecContext(ctx, repoDelete, id); err != nil {
|
||||
return processSQLErrorf(err, "The delete query failed")
|
||||
}
|
||||
|
||||
if err = tx.Commit(); err != nil {
|
||||
return processSqlErrorf(err, "Failed to commit transaction")
|
||||
return processSQLErrorf(err, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Count of repos in a space.
|
||||
func (s *RepoStore) Count(ctx context.Context, spaceId int64) (int64, error) {
|
||||
func (s *RepoStore) Count(ctx context.Context, spaceID int64) (int64, error) {
|
||||
var count int64
|
||||
err := s.db.QueryRow(repoCount, spaceId).Scan(&count)
|
||||
err := s.db.QueryRow(repoCount, spaceID).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, processSqlErrorf(err, "Failed executing count query")
|
||||
return 0, processSQLErrorf(err, "Failed executing count query")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// List returns a list of repos in a space.
|
||||
// TODO: speed up list - for some reason is 200ms for 1 repo as well as 1000
|
||||
func (s *RepoStore) List(ctx context.Context, spaceId int64, opts *types.RepoFilter) ([]*types.Repository, error) {
|
||||
func (s *RepoStore) List(ctx context.Context, spaceID int64, opts *types.RepoFilter) ([]*types.Repository, error) {
|
||||
dst := []*types.Repository{}
|
||||
|
||||
// if the user does not provide any customer filter
|
||||
// or sorting we use the default select statement.
|
||||
if opts.Sort == enum.RepoAttrNone {
|
||||
err := s.db.SelectContext(ctx, &dst, repoSelect, spaceId, limit(opts.Size), offset(opts.Page, opts.Size))
|
||||
err := s.db.SelectContext(ctx, &dst, repoSelect, spaceID, limit(opts.Size), offset(opts.Page, opts.Size))
|
||||
if err != nil {
|
||||
return nil, processSqlErrorf(err, "Failed executing default list query")
|
||||
return nil, processSQLErrorf(err, "Failed executing default list query")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -235,20 +236,21 @@ func (s *RepoStore) List(ctx context.Context, spaceId int64, opts *types.RepoFil
|
|||
stmt := builder.
|
||||
Select("repositories.*,path_value AS repo_path").
|
||||
From("repositories").
|
||||
InnerJoin("paths ON repositories.repo_id=paths.path_targetId AND paths.path_targetType='repo' AND paths.path_isAlias=0").
|
||||
Where("repo_spaceId = " + fmt.Sprint(spaceId))
|
||||
InnerJoin("paths ON repositories.repo_id=paths.path_targetId AND paths.path_targetType='repo' " +
|
||||
"AND paths.path_isAlias=0").
|
||||
Where("repo_spaceId = " + fmt.Sprint(spaceID))
|
||||
stmt = stmt.Limit(uint64(limit(opts.Size)))
|
||||
stmt = stmt.Offset(uint64(offset(opts.Page, opts.Size)))
|
||||
|
||||
switch opts.Sort {
|
||||
case enum.RepoAttrCreated:
|
||||
// NOTE: string concatination is safe because the
|
||||
// NOTE: string concatenation is safe because the
|
||||
// order attribute is an enum and is not user-defined,
|
||||
// and is therefore not subject to injection attacks.
|
||||
stmt = stmt.OrderBy("repo_created " + opts.Order.String())
|
||||
case enum.RepoAttrUpdated:
|
||||
stmt = stmt.OrderBy("repo_updated " + opts.Order.String())
|
||||
case enum.RepoAttrId:
|
||||
case enum.RepoAttrID:
|
||||
stmt = stmt.OrderBy("repo_id " + opts.Order.String())
|
||||
case enum.RepoAttrName:
|
||||
stmt = stmt.OrderBy("repo_name " + opts.Order.String())
|
||||
|
@ -264,22 +266,22 @@ func (s *RepoStore) List(ctx context.Context, spaceId int64, opts *types.RepoFil
|
|||
}
|
||||
|
||||
if err = s.db.SelectContext(ctx, &dst, sql); err != nil {
|
||||
return nil, processSqlErrorf(err, "Failed executing custom list query")
|
||||
return nil, processSQLErrorf(err, "Failed executing custom list query")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// List returns a list of all paths of a repo.
|
||||
// ListAllPaths returns a list of all paths of a repo.
|
||||
func (s *RepoStore) ListAllPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) {
|
||||
return ListPaths(ctx, s.db, enum.PathTargetTypeRepo, id, opts)
|
||||
}
|
||||
|
||||
// Create an alias for a repo
|
||||
func (s *RepoStore) CreatePath(ctx context.Context, repoId int64, params *types.PathParams) (*types.Path, error) {
|
||||
// CreatePath creates an alias for a repository.
|
||||
func (s *RepoStore) CreatePath(ctx context.Context, repoID int64, params *types.PathParams) (*types.Path, error) {
|
||||
p := &types.Path{
|
||||
TargetType: enum.PathTargetTypeRepo,
|
||||
TargetId: repoId,
|
||||
TargetID: repoID,
|
||||
IsAlias: true,
|
||||
|
||||
// get remaining infor from params
|
||||
|
@ -292,9 +294,9 @@ func (s *RepoStore) CreatePath(ctx context.Context, repoId int64, params *types.
|
|||
return p, CreateAliasPath(ctx, s.db, p)
|
||||
}
|
||||
|
||||
// Delete an alias of a repo
|
||||
func (s *RepoStore) DeletePath(ctx context.Context, repoId int64, pathId int64) error {
|
||||
return DeletePath(ctx, s.db, pathId)
|
||||
// DeletePath an alias of a repository.
|
||||
func (s *RepoStore) DeletePath(ctx context.Context, repoID int64, pathID int64) error {
|
||||
return DeletePath(ctx, s.db, pathID)
|
||||
}
|
||||
|
||||
const repoSelectBase = `
|
||||
|
@ -334,13 +336,14 @@ FROM repositories
|
|||
WHERE repo_spaceId = $1
|
||||
`
|
||||
|
||||
const repoSelectById = repoSelectBaseWithJoin + `
|
||||
const repoSelectByID = repoSelectBaseWithJoin + `
|
||||
WHERE repo_id = $1
|
||||
`
|
||||
|
||||
const repoSelectByPath = repoSelectBase + `
|
||||
FROM paths paths1
|
||||
INNER JOIN repositories ON repositories.repo_id=paths1.path_targetId AND paths1.path_targetType='repo' AND paths1.path_value = $1
|
||||
INNER JOIN repositories ON repositories.repo_id=paths1.path_targetId AND paths1.path_targetType='repo'
|
||||
AND paths1.path_value = $1
|
||||
INNER JOIN paths ON repositories.repo_id=paths.path_targetId AND paths.path_targetType='repo' AND paths.path_isAlias=0
|
||||
`
|
||||
|
||||
|
@ -395,7 +398,7 @@ SET
|
|||
WHERE repo_id = :repo_id
|
||||
`
|
||||
|
||||
const repoUpdateNameAndSpaceId = `
|
||||
const repoUpdateNameAndSpaceID = `
|
||||
UPDATE repositories
|
||||
SET
|
||||
repo_name = $1
|
||||
|
|
|
@ -14,54 +14,55 @@ import (
|
|||
|
||||
var _ store.RepoStore = (*RepoStoreSync)(nil)
|
||||
|
||||
// Returns a new RepoStoreSync.
|
||||
// NewRepoStoreSync returns a new RepoStoreSync.
|
||||
func NewRepoStoreSync(base *RepoStore) *RepoStoreSync {
|
||||
return &RepoStoreSync{base}
|
||||
}
|
||||
|
||||
// RepoStoreSync synronizes read and write access to the
|
||||
// RepoStoreSync synchronizes read and write access to the
|
||||
// repo store. This prevents race conditions when the database
|
||||
// type is sqlite3.
|
||||
type RepoStoreSync struct {
|
||||
base *RepoStore
|
||||
}
|
||||
|
||||
// Finds the repo by id.
|
||||
// Find the repo by id.
|
||||
func (s *RepoStoreSync) Find(ctx context.Context, id int64) (*types.Repository, error) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.Find(ctx, id)
|
||||
}
|
||||
|
||||
// Finds the repo by path.
|
||||
// FindByPath finds the repo by path.
|
||||
func (s *RepoStoreSync) FindByPath(ctx context.Context, path string) (*types.Repository, error) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.FindByPath(ctx, path)
|
||||
}
|
||||
|
||||
// Creates a new repo
|
||||
// Create a new repository.
|
||||
func (s *RepoStoreSync) Create(ctx context.Context, repo *types.Repository) error {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.Create(ctx, repo)
|
||||
}
|
||||
|
||||
// Moves an existing repo.
|
||||
func (s *RepoStoreSync) Move(ctx context.Context, userId int64, repoId int64, newSpaceId int64, newName string, keepAsAlias bool) (*types.Repository, error) {
|
||||
// Move an existing repo.
|
||||
func (s *RepoStoreSync) Move(ctx context.Context, userID int64, repoID int64, newSpaceID int64,
|
||||
newName string, keepAsAlias bool) (*types.Repository, error) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.Move(ctx, userId, repoId, newSpaceId, newName, keepAsAlias)
|
||||
return s.base.Move(ctx, userID, repoID, newSpaceID, newName, keepAsAlias)
|
||||
}
|
||||
|
||||
// Updates the repo details.
|
||||
// Update the repo details.
|
||||
func (s *RepoStoreSync) Update(ctx context.Context, repo *types.Repository) error {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.Update(ctx, repo)
|
||||
}
|
||||
|
||||
// Deletes the repo.
|
||||
// Delete the repository.
|
||||
func (s *RepoStoreSync) Delete(ctx context.Context, id int64) error {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
|
@ -69,34 +70,34 @@ func (s *RepoStoreSync) Delete(ctx context.Context, id int64) error {
|
|||
}
|
||||
|
||||
// Count of repos in a space.
|
||||
func (s *RepoStoreSync) Count(ctx context.Context, spaceId int64) (int64, error) {
|
||||
func (s *RepoStoreSync) Count(ctx context.Context, spaceID int64) (int64, error) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.Count(ctx, spaceId)
|
||||
return s.base.Count(ctx, spaceID)
|
||||
}
|
||||
|
||||
// List returns a list of repos in a space.
|
||||
func (s *RepoStoreSync) List(ctx context.Context, spaceId int64, opts *types.RepoFilter) ([]*types.Repository, error) {
|
||||
func (s *RepoStoreSync) List(ctx context.Context, spaceID int64, opts *types.RepoFilter) ([]*types.Repository, error) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.List(ctx, spaceId, opts)
|
||||
return s.base.List(ctx, spaceID, opts)
|
||||
}
|
||||
|
||||
// List returns a list of all paths of a repo.
|
||||
// ListAllPaths returns a list of all paths of a repo.
|
||||
func (s *RepoStoreSync) ListAllPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) {
|
||||
return s.base.ListAllPaths(ctx, id, opts)
|
||||
}
|
||||
|
||||
// Create an alias for a repo
|
||||
func (s *RepoStoreSync) CreatePath(ctx context.Context, repoId int64, params *types.PathParams) (*types.Path, error) {
|
||||
// CreatePath an alias for a repository.
|
||||
func (s *RepoStoreSync) CreatePath(ctx context.Context, repoID int64, params *types.PathParams) (*types.Path, error) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.CreatePath(ctx, repoId, params)
|
||||
return s.base.CreatePath(ctx, repoID, params)
|
||||
}
|
||||
|
||||
// Delete an alias of a repo
|
||||
func (s *RepoStoreSync) DeletePath(ctx context.Context, repoId int64, pathId int64) error {
|
||||
// DeletePath an alias of a repository.
|
||||
func (s *RepoStoreSync) DeletePath(ctx context.Context, repoID int64, pathID int64) error {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.DeletePath(ctx, repoId, pathId)
|
||||
return s.base.DeletePath(ctx, repoID, pathID)
|
||||
}
|
||||
|
|
|
@ -21,39 +21,39 @@ import (
|
|||
|
||||
var _ store.SpaceStore = (*SpaceStore)(nil)
|
||||
|
||||
// Returns a new SpaceStore.
|
||||
// NewSpaceStore returns a new SpaceStore.
|
||||
func NewSpaceStore(db *sqlx.DB) *SpaceStore {
|
||||
return &SpaceStore{db}
|
||||
}
|
||||
|
||||
// Implements a SpaceStore backed by a relational database.
|
||||
// SpaceStore implements a SpaceStore backed by a relational database.
|
||||
type SpaceStore struct {
|
||||
db *sqlx.DB
|
||||
}
|
||||
|
||||
// Finds the space by id.
|
||||
// Find the space by id.
|
||||
func (s *SpaceStore) Find(ctx context.Context, id int64) (*types.Space, error) {
|
||||
dst := new(types.Space)
|
||||
if err := s.db.GetContext(ctx, dst, spaceSelectById, id); err != nil {
|
||||
return nil, processSqlErrorf(err, "Select query failed")
|
||||
if err := s.db.GetContext(ctx, dst, spaceSelectByID, id); err != nil {
|
||||
return nil, processSQLErrorf(err, "Select query failed")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// Finds the space by path.
|
||||
// FindByPath finds the space by path.
|
||||
func (s *SpaceStore) FindByPath(ctx context.Context, path string) (*types.Space, error) {
|
||||
dst := new(types.Space)
|
||||
if err := s.db.GetContext(ctx, dst, spaceSelectByPath, path); err != nil {
|
||||
return nil, processSqlErrorf(err, "Select query failed")
|
||||
return nil, processSQLErrorf(err, "Select query failed")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// Creates a new space
|
||||
// Create a new space.
|
||||
func (s *SpaceStore) Create(ctx context.Context, space *types.Space) error {
|
||||
tx, err := s.db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to start a new transaction")
|
||||
return processSQLErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer func(tx *sqlx.Tx) {
|
||||
_ = tx.Rollback()
|
||||
|
@ -62,17 +62,18 @@ func (s *SpaceStore) Create(ctx context.Context, space *types.Space) error {
|
|||
// insert space first so we get id
|
||||
query, arg, err := s.db.BindNamed(spaceInsert, space)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to bind space object")
|
||||
return processSQLErrorf(err, "Failed to bind space object")
|
||||
}
|
||||
|
||||
if err = tx.QueryRow(query, arg...).Scan(&space.ID); err != nil {
|
||||
return processSqlErrorf(err, "Insert query failed")
|
||||
return processSQLErrorf(err, "Insert query failed")
|
||||
}
|
||||
|
||||
// Get path (get parent if needed)
|
||||
path := space.Name
|
||||
if space.ParentId > 0 {
|
||||
parentPath, err := FindPathTx(ctx, tx, enum.PathTargetTypeSpace, space.ParentId)
|
||||
if space.ParentID > 0 {
|
||||
var parentPath *types.Path
|
||||
parentPath, err = FindPathTx(ctx, tx, enum.PathTargetTypeSpace, space.ParentID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to find path of parent space")
|
||||
}
|
||||
|
@ -84,7 +85,7 @@ func (s *SpaceStore) Create(ctx context.Context, space *types.Space) error {
|
|||
// create path only once we know the id of the space
|
||||
p := &types.Path{
|
||||
TargetType: enum.PathTargetTypeSpace,
|
||||
TargetId: space.ID,
|
||||
TargetID: space.ID,
|
||||
IsAlias: false,
|
||||
Value: path,
|
||||
CreatedBy: space.CreatedBy,
|
||||
|
@ -98,7 +99,7 @@ func (s *SpaceStore) Create(ctx context.Context, space *types.Space) error {
|
|||
|
||||
// commit
|
||||
if err = tx.Commit(); err != nil {
|
||||
return processSqlErrorf(err, "Failed to commit transaction")
|
||||
return processSQLErrorf(err, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
// update path in space object
|
||||
|
@ -107,27 +108,29 @@ func (s *SpaceStore) Create(ctx context.Context, space *types.Space) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Moves an existing space.
|
||||
func (s *SpaceStore) Move(ctx context.Context, userId int64, spaceId int64, newParentId int64, newName string, keepAsAlias bool) (*types.Space, error) {
|
||||
// Move moves an existing space.
|
||||
func (s *SpaceStore) Move(ctx context.Context, userID int64, spaceID int64, newParentID int64, newName string,
|
||||
keepAsAlias bool) (*types.Space, error) {
|
||||
tx, err := s.db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, processSqlErrorf(err, "Failed to start a new transaction")
|
||||
return nil, processSQLErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer func(tx *sqlx.Tx) {
|
||||
_ = tx.Rollback()
|
||||
}(tx)
|
||||
|
||||
// always get currentpath (either it didn't change or we need to for validation)
|
||||
currentPath, err := FindPathTx(ctx, tx, enum.PathTargetTypeSpace, spaceId)
|
||||
currentPath, err := FindPathTx(ctx, tx, enum.PathTargetTypeSpace, spaceID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to find the primary path of the space")
|
||||
}
|
||||
|
||||
// get path of new parent if needed
|
||||
newPath := newName
|
||||
if newParentId > 0 {
|
||||
if newParentID > 0 {
|
||||
// get path of new parent space
|
||||
spacePath, err := FindPathTx(ctx, tx, enum.PathTargetTypeSpace, newParentId)
|
||||
var spacePath *types.Path
|
||||
spacePath, err = FindPathTx(ctx, tx, enum.PathTargetTypeSpace, newParentID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to find the primary path of the new parent space")
|
||||
}
|
||||
|
@ -147,10 +150,10 @@ func (s *SpaceStore) Move(ctx context.Context, userId int64, spaceId int64, newP
|
|||
|
||||
p := &types.Path{
|
||||
TargetType: enum.PathTargetTypeSpace,
|
||||
TargetId: spaceId,
|
||||
TargetID: spaceID,
|
||||
IsAlias: false,
|
||||
Value: newPath,
|
||||
CreatedBy: userId,
|
||||
CreatedBy: userID,
|
||||
Created: time.Now().UnixMilli(),
|
||||
Updated: time.Now().UnixMilli(),
|
||||
}
|
||||
|
@ -161,19 +164,19 @@ func (s *SpaceStore) Move(ctx context.Context, userId int64, spaceId int64, newP
|
|||
}
|
||||
|
||||
// Update the space itself
|
||||
if _, err := tx.ExecContext(ctx, spaceUpdateNameAndParentId, newName, newParentId, spaceId); err != nil {
|
||||
return nil, processSqlErrorf(err, "Query for renaming and updating the parent id failed")
|
||||
if _, err = tx.ExecContext(ctx, spaceUpdateNameAndParentID, newName, newParentID, spaceID); err != nil {
|
||||
return nil, processSQLErrorf(err, "Query for renaming and updating the parent id failed")
|
||||
}
|
||||
|
||||
// TODO: return space as part of rename operation
|
||||
dst := new(types.Space)
|
||||
if err = tx.GetContext(ctx, dst, spaceSelectById, spaceId); err != nil {
|
||||
return nil, processSqlErrorf(err, "Select query to get the space's latest state failed")
|
||||
if err = tx.GetContext(ctx, dst, spaceSelectByID, spaceID); err != nil {
|
||||
return nil, processSQLErrorf(err, "Select query to get the space's latest state failed")
|
||||
}
|
||||
|
||||
// commit
|
||||
if err = tx.Commit(); err != nil {
|
||||
return nil, processSqlErrorf(err, "Failed to commit transaction")
|
||||
return nil, processSQLErrorf(err, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
|
@ -183,11 +186,11 @@ func (s *SpaceStore) Move(ctx context.Context, userId int64, spaceId int64, newP
|
|||
func (s *SpaceStore) Update(ctx context.Context, space *types.Space) error {
|
||||
query, arg, err := s.db.BindNamed(spaceUpdate, space)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to bind space object")
|
||||
return processSQLErrorf(err, "Failed to bind space object")
|
||||
}
|
||||
|
||||
if _, err = s.db.ExecContext(ctx, query, arg...); err != nil {
|
||||
return processSqlErrorf(err, "Update query failed")
|
||||
return processSQLErrorf(err, "Update query failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -197,7 +200,7 @@ func (s *SpaceStore) Update(ctx context.Context, space *types.Space) error {
|
|||
func (s *SpaceStore) Delete(ctx context.Context, id int64) error {
|
||||
tx, err := s.db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to start a new transaction")
|
||||
return processSQLErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer func(tx *sqlx.Tx) {
|
||||
_ = tx.Rollback()
|
||||
|
@ -227,11 +230,11 @@ func (s *SpaceStore) Delete(ctx context.Context, id int64) error {
|
|||
|
||||
// delete the space
|
||||
if _, err = tx.Exec(spaceDelete, id); err != nil {
|
||||
return processSqlErrorf(err, "The delete query failed")
|
||||
return processSQLErrorf(err, "The delete query failed")
|
||||
}
|
||||
|
||||
if err = tx.Commit(); err != nil {
|
||||
return processSqlErrorf(err, "Failed to commit transaction")
|
||||
return processSQLErrorf(err, "Failed to commit transaction")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -242,7 +245,7 @@ func (s *SpaceStore) Count(ctx context.Context, id int64) (int64, error) {
|
|||
var count int64
|
||||
err := s.db.QueryRowContext(ctx, spaceCount, id).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, processSqlErrorf(err, "Failed executing count query")
|
||||
return 0, processSQLErrorf(err, "Failed executing count query")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
@ -257,7 +260,7 @@ func (s *SpaceStore) List(ctx context.Context, id int64, opts *types.SpaceFilter
|
|||
if opts.Sort == enum.SpaceAttrNone {
|
||||
err := s.db.SelectContext(ctx, &dst, spaceSelect, id, limit(opts.Size), offset(opts.Page, opts.Size))
|
||||
if err != nil {
|
||||
return nil, processSqlErrorf(err, "Failed executing default list query")
|
||||
return nil, processSQLErrorf(err, "Failed executing default list query")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -279,7 +282,7 @@ func (s *SpaceStore) List(ctx context.Context, id int64, opts *types.SpaceFilter
|
|||
stmt = stmt.OrderBy("space_created " + opts.Order.String())
|
||||
case enum.SpaceAttrUpdated:
|
||||
stmt = stmt.OrderBy("space_updated " + opts.Order.String())
|
||||
case enum.SpaceAttrId:
|
||||
case enum.SpaceAttrID:
|
||||
stmt = stmt.OrderBy("space_id " + opts.Order.String())
|
||||
case enum.SpaceAttrName:
|
||||
stmt = stmt.OrderBy("space_name " + opts.Order.String())
|
||||
|
@ -293,22 +296,22 @@ func (s *SpaceStore) List(ctx context.Context, id int64, opts *types.SpaceFilter
|
|||
}
|
||||
|
||||
if err = s.db.SelectContext(ctx, &dst, sql); err != nil {
|
||||
return nil, processSqlErrorf(err, "Failed executing custom list query")
|
||||
return nil, processSQLErrorf(err, "Failed executing custom list query")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// List returns a list of all paths of a space.
|
||||
// ListAllPaths returns a list of all paths of a space.
|
||||
func (s *SpaceStore) ListAllPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) {
|
||||
return ListPaths(ctx, s.db, enum.PathTargetTypeSpace, id, opts)
|
||||
}
|
||||
|
||||
// Create an alias for a space.
|
||||
func (s *SpaceStore) CreatePath(ctx context.Context, spaceId int64, params *types.PathParams) (*types.Path, error) {
|
||||
// CreatePath creates an alias for a space.
|
||||
func (s *SpaceStore) CreatePath(ctx context.Context, spaceID int64, params *types.PathParams) (*types.Path, error) {
|
||||
p := &types.Path{
|
||||
TargetType: enum.PathTargetTypeSpace,
|
||||
TargetId: spaceId,
|
||||
TargetID: spaceID,
|
||||
IsAlias: true,
|
||||
|
||||
// get remaining infor from params
|
||||
|
@ -321,9 +324,9 @@ func (s *SpaceStore) CreatePath(ctx context.Context, spaceId int64, params *type
|
|||
return p, CreateAliasPath(ctx, s.db, p)
|
||||
}
|
||||
|
||||
// Delete an alias of a space.
|
||||
func (s *SpaceStore) DeletePath(ctx context.Context, spaceId int64, pathId int64) error {
|
||||
return DeletePath(ctx, s.db, pathId)
|
||||
// DeletePath an alias of a space.
|
||||
func (s *SpaceStore) DeletePath(ctx context.Context, spaceID int64, pathID int64) error {
|
||||
return DeletePath(ctx, s.db, pathID)
|
||||
}
|
||||
|
||||
const spaceSelectBase = `
|
||||
|
@ -358,7 +361,7 @@ FROM spaces
|
|||
WHERE space_parentId = $1
|
||||
`
|
||||
|
||||
const spaceSelectById = spaceSelectBaseWithJoin + `
|
||||
const spaceSelectByID = spaceSelectBaseWithJoin + `
|
||||
WHERE space_id = $1
|
||||
`
|
||||
|
||||
|
@ -405,7 +408,7 @@ space_displayName = :space_displayName
|
|||
WHERE space_id = :space_id
|
||||
`
|
||||
|
||||
const spaceUpdateNameAndParentId = `
|
||||
const spaceUpdateNameAndParentID = `
|
||||
UPDATE spaces
|
||||
SET
|
||||
space_name = $1
|
||||
|
|
|
@ -14,54 +14,55 @@ import (
|
|||
|
||||
var _ store.SpaceStore = (*SpaceStoreSync)(nil)
|
||||
|
||||
// Returns a new SpaceStore.
|
||||
// NewSpaceStoreSync returns a new SpaceStore.
|
||||
func NewSpaceStoreSync(base *SpaceStore) *SpaceStoreSync {
|
||||
return &SpaceStoreSync{base}
|
||||
}
|
||||
|
||||
// SpaceStoreSync synronizes read and write access to the
|
||||
// SpaceStoreSync synchronizes read and write access to the
|
||||
// space store. This prevents race conditions when the database
|
||||
// type is sqlite3.
|
||||
type SpaceStoreSync struct {
|
||||
base *SpaceStore
|
||||
}
|
||||
|
||||
// Finds the space by id.
|
||||
// Find the space by id.
|
||||
func (s *SpaceStoreSync) Find(ctx context.Context, id int64) (*types.Space, error) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.Find(ctx, id)
|
||||
}
|
||||
|
||||
// Finds the space by path.
|
||||
// FindByPath find the space by path.
|
||||
func (s *SpaceStoreSync) FindByPath(ctx context.Context, path string) (*types.Space, error) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.FindByPath(ctx, path)
|
||||
}
|
||||
|
||||
// Creates a new space
|
||||
// Create a new space.
|
||||
func (s *SpaceStoreSync) Create(ctx context.Context, space *types.Space) error {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.Create(ctx, space)
|
||||
}
|
||||
|
||||
// Moves an existing space.
|
||||
func (s *SpaceStoreSync) Move(ctx context.Context, userId int64, spaceId int64, newParentId int64, newName string, keepAsAlias bool) (*types.Space, error) {
|
||||
// Move moves an existing space.
|
||||
func (s *SpaceStoreSync) Move(ctx context.Context, userID int64, spaceID int64, newParentID int64, newName string,
|
||||
keepAsAlias bool) (*types.Space, error) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.Move(ctx, userId, spaceId, newParentId, newName, keepAsAlias)
|
||||
return s.base.Move(ctx, userID, spaceID, newParentID, newName, keepAsAlias)
|
||||
}
|
||||
|
||||
// Updates the space details.
|
||||
// Update the space details.
|
||||
func (s *SpaceStoreSync) Update(ctx context.Context, space *types.Space) error {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.Update(ctx, space)
|
||||
}
|
||||
|
||||
// Deletes the space.
|
||||
// Delete the space.
|
||||
func (s *SpaceStoreSync) Delete(ctx context.Context, id int64) error {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
|
@ -82,21 +83,21 @@ func (s *SpaceStoreSync) List(ctx context.Context, id int64, opts *types.SpaceFi
|
|||
return s.base.List(ctx, id, opts)
|
||||
}
|
||||
|
||||
// List returns a list of all paths of a space.
|
||||
// ListAllPaths returns a list of all paths of a space.
|
||||
func (s *SpaceStoreSync) ListAllPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error) {
|
||||
return s.base.ListAllPaths(ctx, id, opts)
|
||||
}
|
||||
|
||||
// Create a path for a space.
|
||||
func (s *SpaceStoreSync) CreatePath(ctx context.Context, spaceId int64, params *types.PathParams) (*types.Path, error) {
|
||||
// CreatePath a path for a space.
|
||||
func (s *SpaceStoreSync) CreatePath(ctx context.Context, spaceID int64, params *types.PathParams) (*types.Path, error) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.CreatePath(ctx, spaceId, params)
|
||||
return s.base.CreatePath(ctx, spaceID, params)
|
||||
}
|
||||
|
||||
// Delete a path of a space.
|
||||
func (s *SpaceStoreSync) DeletePath(ctx context.Context, spaceId int64, pathId int64) error {
|
||||
// DeletePath a path of a space.
|
||||
func (s *SpaceStoreSync) DeletePath(ctx context.Context, spaceID int64, pathID int64) error {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
return s.base.DeletePath(ctx, spaceId, pathId)
|
||||
return s.base.DeletePath(ctx, spaceID, pathID)
|
||||
}
|
||||
|
|
|
@ -27,13 +27,16 @@ func Connect(driver, datasource string) (*sqlx.DB, error) {
|
|||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to open the db")
|
||||
}
|
||||
|
||||
dbx := sqlx.NewDb(db, driver)
|
||||
if err := pingDatabase(dbx); err != nil {
|
||||
if err = pingDatabase(dbx); err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to ping the db")
|
||||
}
|
||||
if err := setupDatabase(dbx); err != nil {
|
||||
|
||||
if err = setupDatabase(dbx); err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to setup the db")
|
||||
}
|
||||
|
||||
return dbx, nil
|
||||
}
|
||||
|
||||
|
@ -49,18 +52,18 @@ func Must(db *sqlx.DB, err error) *sqlx.DB {
|
|||
// helper function to ping the database with backoff to ensure
|
||||
// a connection can be established before we proceed with the
|
||||
// database setup and migration.
|
||||
func pingDatabase(db *sqlx.DB) (err error) {
|
||||
func pingDatabase(db *sqlx.DB) error {
|
||||
for i := 0; i < 30; i++ {
|
||||
err = db.Ping()
|
||||
if err == nil {
|
||||
return
|
||||
err := db.Ping()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
// helper function to setup the databsae by performing automated
|
||||
// helper function to setup the database by performing automated
|
||||
// database migration steps.
|
||||
func setupDatabase(db *sqlx.DB) error {
|
||||
return migrate.Migrate(db)
|
||||
|
|
|
@ -5,18 +5,16 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
var noContext = context.Background()
|
||||
|
||||
// connect opens a new test database connection.
|
||||
func connect() (*sqlx.DB, error) {
|
||||
var (
|
||||
|
@ -32,13 +30,28 @@ func connect() (*sqlx.DB, error) {
|
|||
|
||||
// seed seed the database state.
|
||||
func seed(db *sqlx.DB) error {
|
||||
_, _ = db.Exec("DELETE FROM executions")
|
||||
_, _ = db.Exec("DELETE FROM pipelines")
|
||||
_, _ = db.Exec("DELETE FROM users")
|
||||
_, _ = db.Exec("ALTER SEQUENCE users_user_id_seq RESTART WITH 1")
|
||||
_, _ = db.Exec("ALTER SEQUENCE pipelines_pipeline_id_seq RESTART WITH 1")
|
||||
_, _ = db.Exec("ALTER SEQUENCE executions_execution_id_seq RESTART WITH 1")
|
||||
return nil
|
||||
_, err := db.Exec("DELETE FROM executions")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = db.Exec("DELETE FROM pipelines")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = db.Exec("DELETE FROM users")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = db.Exec("ALTER SEQUENCE users_user_id_seq RESTART WITH 1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = db.Exec("ALTER SEQUENCE pipelines_pipeline_id_seq RESTART WITH 1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = db.Exec("ALTER SEQUENCE executions_execution_id_seq RESTART WITH 1")
|
||||
return err
|
||||
}
|
||||
|
||||
// unmarshal a testdata file.
|
||||
|
|
|
@ -34,7 +34,7 @@ type UserStore struct {
|
|||
func (s *UserStore) Find(ctx context.Context, id int64) (*types.User, error) {
|
||||
dst := new(types.User)
|
||||
if err := s.db.GetContext(ctx, dst, userSelectID, id); err != nil {
|
||||
return nil, processSqlErrorf(err, "Select by id query failed")
|
||||
return nil, processSQLErrorf(err, "Select by id query failed")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ func (s *UserStore) Find(ctx context.Context, id int64) (*types.User, error) {
|
|||
func (s *UserStore) FindEmail(ctx context.Context, email string) (*types.User, error) {
|
||||
dst := new(types.User)
|
||||
if err := s.db.GetContext(ctx, dst, userSelectEmail, email); err != nil {
|
||||
return nil, processSqlErrorf(err, "Select by email query failed")
|
||||
return nil, processSQLErrorf(err, "Select by email query failed")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -53,9 +53,8 @@ func (s *UserStore) FindKey(ctx context.Context, key string) (*types.User, error
|
|||
id, err := strconv.ParseInt(key, 10, 64)
|
||||
if err == nil {
|
||||
return s.Find(ctx, id)
|
||||
} else {
|
||||
return s.FindEmail(ctx, key)
|
||||
}
|
||||
return s.FindEmail(ctx, key)
|
||||
}
|
||||
|
||||
// List returns a list of users.
|
||||
|
@ -67,7 +66,7 @@ func (s *UserStore) List(ctx context.Context, opts *types.UserFilter) ([]*types.
|
|||
if opts.Sort == enum.UserAttrNone {
|
||||
err := s.db.SelectContext(ctx, &dst, userSelect, limit(opts.Size), offset(opts.Page, opts.Size))
|
||||
if err != nil {
|
||||
return nil, processSqlErrorf(err, "Failed executing default list query")
|
||||
return nil, processSQLErrorf(err, "Failed executing default list query")
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
@ -79,7 +78,7 @@ func (s *UserStore) List(ctx context.Context, opts *types.UserFilter) ([]*types.
|
|||
|
||||
switch opts.Sort {
|
||||
case enum.UserAttrCreated:
|
||||
// NOTE: string concatination is safe because the
|
||||
// NOTE: string concatenation is safe because the
|
||||
// order attribute is an enum and is not user-defined,
|
||||
// and is therefore not subject to injection attacks.
|
||||
stmt = stmt.OrderBy("user_id " + opts.Order.String())
|
||||
|
@ -87,7 +86,7 @@ func (s *UserStore) List(ctx context.Context, opts *types.UserFilter) ([]*types.
|
|||
stmt = stmt.OrderBy("user_updated " + opts.Order.String())
|
||||
case enum.UserAttrEmail:
|
||||
stmt = stmt.OrderBy("user_email " + opts.Order.String())
|
||||
case enum.UserAttrId:
|
||||
case enum.UserAttrID:
|
||||
stmt = stmt.OrderBy("user_id " + opts.Order.String())
|
||||
}
|
||||
|
||||
|
@ -97,7 +96,7 @@ func (s *UserStore) List(ctx context.Context, opts *types.UserFilter) ([]*types.
|
|||
}
|
||||
|
||||
if err = s.db.SelectContext(ctx, &dst, sql); err != nil {
|
||||
return nil, processSqlErrorf(err, "Failed executing custom list query")
|
||||
return nil, processSQLErrorf(err, "Failed executing custom list query")
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
|
@ -107,11 +106,11 @@ func (s *UserStore) List(ctx context.Context, opts *types.UserFilter) ([]*types.
|
|||
func (s *UserStore) Create(ctx context.Context, user *types.User) error {
|
||||
query, arg, err := s.db.BindNamed(userInsert, user)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to bind user object")
|
||||
return processSQLErrorf(err, "Failed to bind user object")
|
||||
}
|
||||
|
||||
if err = s.db.QueryRowContext(ctx, query, arg...).Scan(&user.ID); err != nil {
|
||||
return processSqlErrorf(err, "Insert query failed")
|
||||
return processSQLErrorf(err, "Insert query failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -121,11 +120,11 @@ func (s *UserStore) Create(ctx context.Context, user *types.User) error {
|
|||
func (s *UserStore) Update(ctx context.Context, user *types.User) error {
|
||||
query, arg, err := s.db.BindNamed(userUpdate, user)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to bind user object")
|
||||
return processSQLErrorf(err, "Failed to bind user object")
|
||||
}
|
||||
|
||||
if _, err = s.db.ExecContext(ctx, query, arg...); err != nil {
|
||||
return processSqlErrorf(err, "Update query failed")
|
||||
return processSQLErrorf(err, "Update query failed")
|
||||
}
|
||||
|
||||
return err
|
||||
|
@ -135,14 +134,14 @@ func (s *UserStore) Update(ctx context.Context, user *types.User) error {
|
|||
func (s *UserStore) Delete(ctx context.Context, user *types.User) error {
|
||||
tx, err := s.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return processSqlErrorf(err, "Failed to start a new transaction")
|
||||
return processSQLErrorf(err, "Failed to start a new transaction")
|
||||
}
|
||||
defer func(tx *sql.Tx) {
|
||||
_ = tx.Rollback()
|
||||
}(tx)
|
||||
// delete the user
|
||||
if _, err := tx.ExecContext(ctx, userDelete, user.ID); err != nil {
|
||||
return processSqlErrorf(err, "The delete query failed")
|
||||
if _, err = tx.ExecContext(ctx, userDelete, user.ID); err != nil {
|
||||
return processSQLErrorf(err, "The delete query failed")
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
@ -152,7 +151,7 @@ func (s *UserStore) Count(ctx context.Context) (int64, error) {
|
|||
var count int64
|
||||
err := s.db.QueryRowContext(ctx, userCount).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, processSqlErrorf(err, "Failed executing count query")
|
||||
return 0, processSQLErrorf(err, "Failed executing count query")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
|
|
@ -5,11 +5,15 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/harness/gitness/internal/store"
|
||||
"github.com/harness/gitness/types"
|
||||
|
||||
|
@ -17,7 +21,7 @@ import (
|
|||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
)
|
||||
|
||||
// user fields to ignore in test comparisons
|
||||
// user fields to ignore in test comparisons.
|
||||
var userIgnore = cmpopts.IgnoreFields(types.User{},
|
||||
"ID", "Salt", "Created", "Updated")
|
||||
|
||||
|
@ -27,26 +31,29 @@ func TestUser(t *testing.T) {
|
|||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer db.Close()
|
||||
if err := seed(db); err != nil {
|
||||
defer func(db *sqlx.DB) {
|
||||
_ = db.Close()
|
||||
}(db)
|
||||
if err = seed(db); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
store := NewUserStoreSync(NewUserStore(db))
|
||||
t.Run("create", testUserCreate(store))
|
||||
t.Run("duplicate", testUserDuplicate(store))
|
||||
t.Run("count", testUserCount(store))
|
||||
t.Run("find", testUserFind(store))
|
||||
t.Run("list", testUserList(store))
|
||||
t.Run("update", testUserUpdate(store))
|
||||
t.Run("delete", testUserDelete(store))
|
||||
userStoreSync := NewUserStoreSync(NewUserStore(db))
|
||||
t.Run("create", testUserCreate(userStoreSync))
|
||||
t.Run("duplicate", testUserDuplicate(userStoreSync))
|
||||
t.Run("count", testUserCount(userStoreSync))
|
||||
t.Run("find", testUserFind(userStoreSync))
|
||||
t.Run("list", testUserList(userStoreSync))
|
||||
t.Run("update", testUserUpdate(userStoreSync))
|
||||
t.Run("delete", testUserDelete(userStoreSync))
|
||||
}
|
||||
|
||||
// this test creates entries in the database and confirms
|
||||
// the primary keys were auto-incremented.
|
||||
func testUserCreate(store store.UserStore) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
vv := []*types.User{}
|
||||
if err := unmarshal("testdata/users.json", &vv); err != nil {
|
||||
t.Error(err)
|
||||
|
@ -57,7 +64,7 @@ func testUserCreate(store store.UserStore) func(t *testing.T) {
|
|||
// generate a deterministic token for each
|
||||
// entry based on the hash of the email.
|
||||
v.Salt = fmt.Sprintf("%x", v.Email)
|
||||
if err := store.Create(noContext, v); err != nil {
|
||||
if err := store.Create(ctx, v); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
@ -67,7 +74,7 @@ func testUserCreate(store store.UserStore) func(t *testing.T) {
|
|||
// create row 2
|
||||
v = vv[1]
|
||||
v.Salt = fmt.Sprintf("%x", v.Email)
|
||||
if err := store.Create(noContext, v); err != nil {
|
||||
if err := store.Create(ctx, v); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
@ -87,7 +94,7 @@ func testUserDuplicate(store store.UserStore) func(t *testing.T) {
|
|||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if err := store.Create(noContext, vv[0]); err == nil {
|
||||
if err := store.Create(context.Background(), vv[0]); err == nil {
|
||||
t.Errorf("Expect unique index violation")
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +104,7 @@ func testUserDuplicate(store store.UserStore) func(t *testing.T) {
|
|||
// and compares to the expected count.
|
||||
func testUserCount(store store.UserStore) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
got, err := store.Count(noContext)
|
||||
got, err := store.Count(context.Background())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -113,6 +120,7 @@ func testUserCount(store store.UserStore) func(t *testing.T) {
|
|||
// to ensure all columns are correctly mapped.
|
||||
func testUserFind(store store.UserStore) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
vv := []*types.User{}
|
||||
if err := unmarshal("testdata/users.json", &vv); err != nil {
|
||||
t.Error(err)
|
||||
|
@ -121,7 +129,7 @@ func testUserFind(store store.UserStore) func(t *testing.T) {
|
|||
want := vv[0]
|
||||
|
||||
t.Run("id", func(t *testing.T) {
|
||||
got, err := store.Find(noContext, 1)
|
||||
got, err := store.Find(ctx, 1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -133,7 +141,7 @@ func testUserFind(store store.UserStore) func(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("email", func(t *testing.T) {
|
||||
got, err := store.FindEmail(noContext, want.Email)
|
||||
got, err := store.FindEmail(ctx, want.Email)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -145,7 +153,7 @@ func testUserFind(store store.UserStore) func(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("email/nocase", func(t *testing.T) {
|
||||
got, err := store.FindEmail(noContext, strings.ToUpper(want.Email))
|
||||
got, err := store.FindEmail(ctx, strings.ToUpper(want.Email))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -157,7 +165,7 @@ func testUserFind(store store.UserStore) func(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("key/id", func(t *testing.T) {
|
||||
got, err := store.FindKey(noContext, "1")
|
||||
got, err := store.FindKey(ctx, "1")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -169,7 +177,7 @@ func testUserFind(store store.UserStore) func(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("key/email", func(t *testing.T) {
|
||||
got, err := store.FindKey(noContext, want.Email)
|
||||
got, err := store.FindKey(ctx, want.Email)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -192,7 +200,7 @@ func testUserList(store store.UserStore) func(t *testing.T) {
|
|||
t.Error(err)
|
||||
return
|
||||
}
|
||||
got, err := store.List(noContext, &types.UserFilter{Page: 0, Size: 100})
|
||||
got, err := store.List(context.Background(), &types.UserFilter{Page: 0, Size: 100})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -208,18 +216,19 @@ func testUserList(store store.UserStore) func(t *testing.T) {
|
|||
// the user and confirms the column was updated as expected.
|
||||
func testUserUpdate(store store.UserStore) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
before, err := store.Find(noContext, 1)
|
||||
ctx := context.Background()
|
||||
before, err := store.Find(ctx, 1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
before.Updated = time.Now().Unix()
|
||||
before.Authed = time.Now().Unix()
|
||||
if err := store.Update(noContext, before); err != nil {
|
||||
if err = store.Update(ctx, before); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
after, err := store.Find(noContext, 1)
|
||||
after, err := store.Find(ctx, 1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -237,16 +246,17 @@ func testUserUpdate(store store.UserStore) func(t *testing.T) {
|
|||
// a sql.ErrNoRows error.
|
||||
func testUserDelete(s store.UserStore) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
v, err := s.Find(noContext, 1)
|
||||
ctx := context.Background()
|
||||
v, err := s.Find(ctx, 1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if err := s.Delete(noContext, v); err != nil {
|
||||
if err = s.Delete(ctx, v); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if _, err := s.Find(noContext, 1); err != store.ErrResourceNotFound {
|
||||
if _, err = s.Find(ctx, 1); errors.Is(err, store.ErrResourceNotFound) {
|
||||
t.Errorf("Expected sql.ErrNoRows got %s", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,26 +33,27 @@ func offset(page, size int) int {
|
|||
if size == 0 {
|
||||
size = defaultLimit
|
||||
}
|
||||
page = page - 1
|
||||
page--
|
||||
return page * size
|
||||
}
|
||||
|
||||
// Logs the error and message, returns either the original error or a store equivalent if possible.
|
||||
func processSqlErrorf(err error, format string, args ...interface{}) error {
|
||||
func processSQLErrorf(err error, format string, args ...interface{}) error {
|
||||
// always log DB error (print formated message)
|
||||
log.Warn().Msgf("%s %s", fmt.Sprintf(format, args...), err)
|
||||
|
||||
// If it's a known error, return converted error instead.
|
||||
if err == sql.ErrNoRows {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return store.ErrResourceNotFound
|
||||
} else if isSqlUniqueConstraintError(err) {
|
||||
} else if isSQLUniqueConstraintError(err) {
|
||||
return store.ErrDuplicate
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func isSqlUniqueConstraintError(original error) bool {
|
||||
o3, ok := original.(sqlite3.Error)
|
||||
return ok && errors.Is(o3.ExtendedCode, sqlite3.ErrConstraintUnique)
|
||||
func isSQLUniqueConstraintError(original error) bool {
|
||||
err := sqlite3.Error{}
|
||||
ok := errors.As(original, &err)
|
||||
return ok && errors.Is(err.ExtendedCode, sqlite3.ErrConstraintUnique)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,11 @@ import (
|
|||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
// WireSet provides a wire set for this package
|
||||
const (
|
||||
postgres = "postgres"
|
||||
)
|
||||
|
||||
// WireSet provides a wire set for this package.
|
||||
var WireSet = wire.NewSet(
|
||||
ProvideDatabase,
|
||||
ProvideUserStore,
|
||||
|
@ -31,7 +35,7 @@ func ProvideDatabase(config *types.Config) (*sqlx.DB, error) {
|
|||
// ProvideUserStore provides a user store.
|
||||
func ProvideUserStore(db *sqlx.DB) store.UserStore {
|
||||
switch db.DriverName() {
|
||||
case "postgres":
|
||||
case postgres:
|
||||
return NewUserStore(db)
|
||||
default:
|
||||
return NewUserStoreSync(
|
||||
|
@ -43,7 +47,7 @@ func ProvideUserStore(db *sqlx.DB) store.UserStore {
|
|||
// ProvideSpaceStore provides a space store.
|
||||
func ProvideSpaceStore(db *sqlx.DB) store.SpaceStore {
|
||||
switch db.DriverName() {
|
||||
case "postgres":
|
||||
case postgres:
|
||||
return NewSpaceStore(db)
|
||||
default:
|
||||
return NewSpaceStoreSync(
|
||||
|
@ -55,7 +59,7 @@ func ProvideSpaceStore(db *sqlx.DB) store.SpaceStore {
|
|||
// ProvideRepoStore provides a repo store.
|
||||
func ProvideRepoStore(db *sqlx.DB) store.RepoStore {
|
||||
switch db.DriverName() {
|
||||
case "postgres":
|
||||
case postgres:
|
||||
return NewRepoStore(db)
|
||||
default:
|
||||
return NewRepoStoreSync(
|
||||
|
|
|
@ -7,14 +7,16 @@ package store
|
|||
import "errors"
|
||||
|
||||
var (
|
||||
ErrResourceNotFound = errors.New("Resource not found")
|
||||
ErrDuplicate = errors.New("Resource is a duplicate")
|
||||
ErrPathTooLong = errors.New("The path is too long")
|
||||
ErrPrimaryPathAlreadyExists = errors.New("Primary path already exists for resource.")
|
||||
ErrPrimaryPathRequired = errors.New("Path has to be primary.")
|
||||
ErrAliasPathRequired = errors.New("Path has to be an alias.")
|
||||
ErrPrimaryPathCantBeDeleted = errors.New("Primary path can't be deleted.")
|
||||
ErrNoChangeInRequestedMove = errors.New(("The requested move doesn't change anything."))
|
||||
ErrIllegalMoveCyclicHierarchy = errors.New(("The requested move is not permitted as it would cause a cyclic depdency."))
|
||||
ErrSpaceWithChildsCantBeDeleted = errors.New("The space can't be deleted as it still contains spaces or repos.")
|
||||
ErrResourceNotFound = errors.New("resource not found")
|
||||
ErrDuplicate = errors.New("resource is a duplicate")
|
||||
ErrPathTooLong = errors.New("the path is too long")
|
||||
ErrPrimaryPathAlreadyExists = errors.New("primary path already exists for resource")
|
||||
ErrPrimaryPathRequired = errors.New("path has to be primary")
|
||||
ErrAliasPathRequired = errors.New("path has to be an alias")
|
||||
ErrPrimaryPathCantBeDeleted = errors.New("primary path can't be deleted")
|
||||
ErrNoChangeInRequestedMove = errors.New("the requested move doesn't change anything")
|
||||
ErrIllegalMoveCyclicHierarchy = errors.New("the requested move is not permitted as it would cause a " +
|
||||
"cyclic depdency")
|
||||
ErrSpaceWithChildsCantBeDeleted = errors.New("the space can't be deleted as it still contains " +
|
||||
"spaces or repos")
|
||||
)
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/harness/gitness/internal/store"
|
||||
)
|
||||
|
||||
// WireSet provides a wire set for this package
|
||||
// WireSet provides a wire set for this package.
|
||||
var WireSet = wire.NewSet(
|
||||
New,
|
||||
wire.Bind(new(store.SystemStore), new(*SystemStore)),
|
||||
|
|
|
@ -41,22 +41,23 @@ type (
|
|||
|
||||
// SpaceStore defines the space data storage.
|
||||
SpaceStore interface {
|
||||
// Finds the space by id.
|
||||
// Find the space by id.
|
||||
Find(ctx context.Context, id int64) (*types.Space, error)
|
||||
|
||||
// Finds the space by its path.
|
||||
// FindByPath the space by its path.
|
||||
FindByPath(ctx context.Context, path string) (*types.Space, error)
|
||||
|
||||
// Creates a new space
|
||||
// Create creates a new space
|
||||
Create(ctx context.Context, space *types.Space) error
|
||||
|
||||
// Moves an existing space.
|
||||
Move(ctx context.Context, userId int64, spaceId int64, newParentId int64, newName string, keepAsAlias bool) (*types.Space, error)
|
||||
// Move moves an existing space.
|
||||
Move(ctx context.Context, userID int64, spaceID int64, newParentID int64, newName string,
|
||||
keepAsAlias bool) (*types.Space, error)
|
||||
|
||||
// Updates the space details.
|
||||
// Update updates the space details.
|
||||
Update(ctx context.Context, space *types.Space) error
|
||||
|
||||
// Deletes the space.
|
||||
// Delete deletes the space.
|
||||
Delete(ctx context.Context, id int64) error
|
||||
|
||||
// Count the child spaces of a space.
|
||||
|
@ -65,50 +66,51 @@ type (
|
|||
// List returns a list of child spaces in a space.
|
||||
List(ctx context.Context, id int64, opts *types.SpaceFilter) ([]*types.Space, error)
|
||||
|
||||
// List returns a list of all paths of a space.
|
||||
// ListAllPaths returns a list of all paths of a space.
|
||||
ListAllPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error)
|
||||
|
||||
// Create an alias for a space
|
||||
CreatePath(ctx context.Context, spaceId int64, params *types.PathParams) (*types.Path, error)
|
||||
// CreatePath create an alias for a space
|
||||
CreatePath(ctx context.Context, spaceID int64, params *types.PathParams) (*types.Path, error)
|
||||
|
||||
// Delete an alias of a space
|
||||
DeletePath(ctx context.Context, spaceId int64, pathId int64) error
|
||||
// DeletePath delete an alias of a space
|
||||
DeletePath(ctx context.Context, spaceID int64, pathID int64) error
|
||||
}
|
||||
|
||||
// RepoStore defines the repository data storage.
|
||||
RepoStore interface {
|
||||
// Finds the repo by id.
|
||||
// Find the repo by id.
|
||||
Find(ctx context.Context, id int64) (*types.Repository, error)
|
||||
|
||||
// Finds the repo by path.
|
||||
// FindByPath the repo by path.
|
||||
FindByPath(ctx context.Context, path string) (*types.Repository, error)
|
||||
|
||||
// Creates a new repo
|
||||
// Create a new repo
|
||||
Create(ctx context.Context, repo *types.Repository) error
|
||||
|
||||
// Moves an existing repo.
|
||||
Move(ctx context.Context, userId int64, repoId int64, newSpaceId int64, newName string, keepAsAlias bool) (*types.Repository, error)
|
||||
// Move moves an existing repo.
|
||||
Move(ctx context.Context, userID int64, repoID int64, newSpaceID int64, newName string,
|
||||
keepAsAlias bool) (*types.Repository, error)
|
||||
|
||||
// Updates the repo details.
|
||||
// Update the repo details.
|
||||
Update(ctx context.Context, repo *types.Repository) error
|
||||
|
||||
// Deletes the repo.
|
||||
// Delete the repo.
|
||||
Delete(ctx context.Context, id int64) error
|
||||
|
||||
// Count of repos in a space.
|
||||
Count(ctx context.Context, spaceId int64) (int64, error)
|
||||
Count(ctx context.Context, spaceID int64) (int64, error)
|
||||
|
||||
// List returns a list of repos in a space.
|
||||
List(ctx context.Context, spaceId int64, opts *types.RepoFilter) ([]*types.Repository, error)
|
||||
List(ctx context.Context, spaceID int64, opts *types.RepoFilter) ([]*types.Repository, error)
|
||||
|
||||
// List returns a list of all alias paths of a repo.
|
||||
// ListAllPaths returns a list of all alias paths of a repo.
|
||||
ListAllPaths(ctx context.Context, id int64, opts *types.PathFilter) ([]*types.Path, error)
|
||||
|
||||
// Create an alias for a repo
|
||||
CreatePath(ctx context.Context, repoId int64, params *types.PathParams) (*types.Path, error)
|
||||
// CreatePath an alias for a repo
|
||||
CreatePath(ctx context.Context, repoID int64, params *types.PathParams) (*types.Path, error)
|
||||
|
||||
// Delete an alias of a repo
|
||||
DeletePath(ctx context.Context, repoId int64, pathId int64) error
|
||||
// DeletePath delete an alias of a repo
|
||||
DeletePath(ctx context.Context, repoID int64, pathID int64) error
|
||||
}
|
||||
|
||||
// SystemStore defines internal system metadata storage.
|
||||
|
|
|
@ -80,7 +80,7 @@ func TestTokenNotExpired(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
token_, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
sub := token.Claims.(*Claims).Subject
|
||||
id, _ := strconv.ParseInt(sub, 10, 64)
|
||||
if id != 42 {
|
||||
|
@ -93,15 +93,14 @@ func TestTokenNotExpired(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
if claims, ok := token_.Claims.(*Claims); ok {
|
||||
if claims.ExpiresAt > 0 {
|
||||
if time.Now().Unix() > claims.ExpiresAt {
|
||||
t.Errorf("expect token not expired")
|
||||
}
|
||||
} else {
|
||||
t.Errorf("expect token expiration greater than zero")
|
||||
}
|
||||
} else {
|
||||
claims, ok := token.Claims.(*Claims)
|
||||
if !ok {
|
||||
t.Errorf("expect token claims from token")
|
||||
return
|
||||
}
|
||||
|
||||
if claims.ExpiresAt > 0 && time.Now().Unix() > claims.ExpiresAt {
|
||||
t.Errorf("expect token not expired")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
|
@ -35,135 +36,135 @@ func (m *MockClient) EXPECT() *MockClientMockRecorder {
|
|||
}
|
||||
|
||||
// Login mocks base method.
|
||||
func (m *MockClient) Login(arg0, arg1 string) (*types.Token, error) {
|
||||
func (m *MockClient) Login(arg0 context.Context, arg1, arg2 string) (*types.Token, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Login", arg0, arg1)
|
||||
ret := m.ctrl.Call(m, "Login", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(*types.Token)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Login indicates an expected call of Login.
|
||||
func (mr *MockClientMockRecorder) Login(arg0, arg1 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) Login(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Login", reflect.TypeOf((*MockClient)(nil).Login), arg0, arg1)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Login", reflect.TypeOf((*MockClient)(nil).Login), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// Register mocks base method.
|
||||
func (m *MockClient) Register(arg0, arg1 string) (*types.Token, error) {
|
||||
func (m *MockClient) Register(arg0 context.Context, arg1, arg2 string) (*types.Token, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Register", arg0, arg1)
|
||||
ret := m.ctrl.Call(m, "Register", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(*types.Token)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Register indicates an expected call of Register.
|
||||
func (mr *MockClientMockRecorder) Register(arg0, arg1 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) Register(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockClient)(nil).Register), arg0, arg1)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockClient)(nil).Register), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// Self mocks base method.
|
||||
func (m *MockClient) Self() (*types.User, error) {
|
||||
func (m *MockClient) Self(arg0 context.Context) (*types.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Self")
|
||||
ret := m.ctrl.Call(m, "Self", arg0)
|
||||
ret0, _ := ret[0].(*types.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Self indicates an expected call of Self.
|
||||
func (mr *MockClientMockRecorder) Self() *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) Self(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Self", reflect.TypeOf((*MockClient)(nil).Self))
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Self", reflect.TypeOf((*MockClient)(nil).Self), arg0)
|
||||
}
|
||||
|
||||
// Token mocks base method.
|
||||
func (m *MockClient) Token() (*types.Token, error) {
|
||||
func (m *MockClient) Token(arg0 context.Context) (*types.Token, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Token")
|
||||
ret := m.ctrl.Call(m, "Token", arg0)
|
||||
ret0, _ := ret[0].(*types.Token)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Token indicates an expected call of Token.
|
||||
func (mr *MockClientMockRecorder) Token() *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) Token(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Token", reflect.TypeOf((*MockClient)(nil).Token))
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Token", reflect.TypeOf((*MockClient)(nil).Token), arg0)
|
||||
}
|
||||
|
||||
// User mocks base method.
|
||||
func (m *MockClient) User(arg0 string) (*types.User, error) {
|
||||
func (m *MockClient) User(arg0 context.Context, arg1 string) (*types.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "User", arg0)
|
||||
ret := m.ctrl.Call(m, "User", arg0, arg1)
|
||||
ret0, _ := ret[0].(*types.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// User indicates an expected call of User.
|
||||
func (mr *MockClientMockRecorder) User(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) User(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "User", reflect.TypeOf((*MockClient)(nil).User), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "User", reflect.TypeOf((*MockClient)(nil).User), arg0, arg1)
|
||||
}
|
||||
|
||||
// UserCreate mocks base method.
|
||||
func (m *MockClient) UserCreate(arg0 *types.User) (*types.User, error) {
|
||||
func (m *MockClient) UserCreate(arg0 context.Context, arg1 *types.User) (*types.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UserCreate", arg0)
|
||||
ret := m.ctrl.Call(m, "UserCreate", arg0, arg1)
|
||||
ret0, _ := ret[0].(*types.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// UserCreate indicates an expected call of UserCreate.
|
||||
func (mr *MockClientMockRecorder) UserCreate(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) UserCreate(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserCreate", reflect.TypeOf((*MockClient)(nil).UserCreate), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserCreate", reflect.TypeOf((*MockClient)(nil).UserCreate), arg0, arg1)
|
||||
}
|
||||
|
||||
// UserDelete mocks base method.
|
||||
func (m *MockClient) UserDelete(arg0 string) error {
|
||||
func (m *MockClient) UserDelete(arg0 context.Context, arg1 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UserDelete", arg0)
|
||||
ret := m.ctrl.Call(m, "UserDelete", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UserDelete indicates an expected call of UserDelete.
|
||||
func (mr *MockClientMockRecorder) UserDelete(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) UserDelete(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserDelete", reflect.TypeOf((*MockClient)(nil).UserDelete), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserDelete", reflect.TypeOf((*MockClient)(nil).UserDelete), arg0, arg1)
|
||||
}
|
||||
|
||||
// UserList mocks base method.
|
||||
func (m *MockClient) UserList(arg0 types.Params) ([]*types.User, error) {
|
||||
func (m *MockClient) UserList(arg0 context.Context, arg1 types.Params) ([]types.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UserList", arg0)
|
||||
ret0, _ := ret[0].([]*types.User)
|
||||
ret := m.ctrl.Call(m, "UserList", arg0, arg1)
|
||||
ret0, _ := ret[0].([]types.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// UserList indicates an expected call of UserList.
|
||||
func (mr *MockClientMockRecorder) UserList(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) UserList(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserList", reflect.TypeOf((*MockClient)(nil).UserList), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserList", reflect.TypeOf((*MockClient)(nil).UserList), arg0, arg1)
|
||||
}
|
||||
|
||||
// UserUpdate mocks base method.
|
||||
func (m *MockClient) UserUpdate(arg0 string, arg1 *types.UserInput) (*types.User, error) {
|
||||
func (m *MockClient) UserUpdate(arg0 context.Context, arg1 string, arg2 *types.UserInput) (*types.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UserUpdate", arg0, arg1)
|
||||
ret := m.ctrl.Call(m, "UserUpdate", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(*types.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// UserUpdate indicates an expected call of UserUpdate.
|
||||
func (mr *MockClientMockRecorder) UserUpdate(arg0, arg1 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) UserUpdate(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserUpdate", reflect.TypeOf((*MockClient)(nil).UserUpdate), arg0, arg1)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserUpdate", reflect.TypeOf((*MockClient)(nil).UserUpdate), arg0, arg1, arg2)
|
||||
}
|
||||
|
|
|
@ -6,31 +6,26 @@ package types
|
|||
|
||||
import "github.com/harness/gitness/types/enum"
|
||||
|
||||
/*
|
||||
* Represents a permission check.
|
||||
*/
|
||||
// PermissionCheck represents a permission check.
|
||||
type PermissionCheck struct {
|
||||
Scope Scope
|
||||
Resource Resource
|
||||
Permission enum.Permission
|
||||
}
|
||||
|
||||
/*
|
||||
* Represents the resource of a permission check.
|
||||
* Note: Keep the name empty in case access is requested for all resources of that type.
|
||||
*/
|
||||
// Resource represents the resource of a permission check.
|
||||
// Note: Keep the name empty in case access is requested for all resources of that type.
|
||||
type Resource struct {
|
||||
Type enum.ResourceType
|
||||
Name string
|
||||
}
|
||||
|
||||
/*
|
||||
* Represents the scope of a permission check.
|
||||
* Notes:
|
||||
* - In case the permission check is for resource REPO, keep repo empty (repo is resource, not scope)
|
||||
* - In case the permission check is for resource SPACE, SpacePath is an ancestor of the space (space is resource, not scope)
|
||||
* - Repo isn't use as of now (will be useful once we add access control for repo child resources, e.g. branches)
|
||||
*/
|
||||
// Scope represents the scope of a permission check
|
||||
// Notes:
|
||||
// - In case the permission check is for resource REPO, keep repo empty (repo is resource, not scope)
|
||||
// - In case the permission check is for resource SPACE, SpacePath is an ancestor of the space (space is
|
||||
// resource, not scope)
|
||||
// - Repo isn't use as of now (will be useful once we add access control for repo child resources, e.g. branches).
|
||||
type Scope struct {
|
||||
SpacePath string
|
||||
Repo string
|
||||
|
|
|
@ -20,11 +20,18 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrNameLength = &CheckError{fmt.Sprintf("Name has to be between %d and %d in length.", minNameLength, maxNameLength)}
|
||||
ErrNameRegex = &CheckError{"Name has start with a letter and only contain the following [a-z0-9-_]."}
|
||||
ErrNameLength = &ValidationError{
|
||||
fmt.Sprintf("Name has to be between %d and %d in length.", minNameLength, maxNameLength),
|
||||
}
|
||||
ErrNameRegex = &ValidationError{"Name has start with a letter and only contain the following [a-z0-9-_]."}
|
||||
|
||||
ErrDisplayNameLength = &CheckError{fmt.Sprintf("Display name has to be between %d and %d in length.", minDisplayNameLength, maxDisplayNameLength)}
|
||||
ErrDisplayNameRegex = &CheckError{"Display name has start with a letter and only contain the following [a-zA-Z0-9-_ ]."}
|
||||
ErrDisplayNameLength = &ValidationError{
|
||||
fmt.Sprintf("Display name has to be between %d and %d in length.",
|
||||
minDisplayNameLength, maxDisplayNameLength),
|
||||
}
|
||||
ErrDisplayNameRegex = &ValidationError{
|
||||
"Display name has start with a letter and only contain the following [a-zA-Z0-9-_ ].",
|
||||
}
|
||||
)
|
||||
|
||||
// Name checks the provided name and returns an error in it isn't valid.
|
||||
|
|
|
@ -4,34 +4,34 @@
|
|||
|
||||
package check
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrAny = &CheckError{}
|
||||
ErrAny = &ValidationError{}
|
||||
)
|
||||
|
||||
/*
|
||||
* An error returned by check methods for any validation errors
|
||||
* WARNING: This error will be printed to the user as is!
|
||||
*/
|
||||
type CheckError struct {
|
||||
// ValidationError is error returned by check methods for any validation errors
|
||||
// WARNING: This error will be printed to the user as is!
|
||||
type ValidationError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e *CheckError) Error() string {
|
||||
func (e *ValidationError) Error() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
func (e *CheckError) Is(target error) bool {
|
||||
// If the caller is checking for any CheckError, return true
|
||||
if target == ErrAny {
|
||||
func (e *ValidationError) Is(target error) bool {
|
||||
// If the caller is checking for any ValidationError, return true
|
||||
if errors.Is(target, ErrAny) {
|
||||
return true
|
||||
}
|
||||
|
||||
// ensure it's the correct type
|
||||
v, ok := target.(*CheckError)
|
||||
if !ok {
|
||||
err := &ValidationError{}
|
||||
if !errors.As(target, &err) {
|
||||
return false
|
||||
}
|
||||
|
||||
// only the same if the message is the same
|
||||
return e.msg == v.msg
|
||||
return e.msg == err.msg
|
||||
}
|
||||
|
|
|
@ -19,21 +19,30 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrPathEmpty = &CheckError{"Path can't be empty."}
|
||||
ErrPathInvalidSize = &CheckError{fmt.Sprintf("A path has to be between %d and %d segments long (%d for spaces).", minPathSegments, maxPathSegments, maxPathSegmentsForSpace)}
|
||||
ErrEmptyPathSegment = &CheckError{"Empty segments are not allowed."}
|
||||
ErrPathCantBeginOrEndWithSeparator = &CheckError{fmt.Sprintf("Path can't start or end with the separator ('%s').", types.PathSeparator)}
|
||||
ErrPathDifferentTopLevelSpace = &CheckError{"Alias paths have to stay within the same top level space."}
|
||||
ErrTopLevelPathNotAllowed = &CheckError{"Top level alias paths are not allowed."}
|
||||
ErrPathEmpty = &ValidationError{
|
||||
"Path can't be empty.",
|
||||
}
|
||||
ErrPathInvalidSize = &ValidationError{
|
||||
fmt.Sprintf("A path has to be between %d and %d segments long (%d for spaces).",
|
||||
minPathSegments, maxPathSegments, maxPathSegmentsForSpace),
|
||||
}
|
||||
ErrEmptyPathSegment = &ValidationError{
|
||||
"Empty segments are not allowed.",
|
||||
}
|
||||
ErrPathCantBeginOrEndWithSeparator = &ValidationError{
|
||||
fmt.Sprintf("Path can't start or end with the separator ('%s').", types.PathSeparator),
|
||||
}
|
||||
ErrPathDifferentTopLevelSpace = &ValidationError{
|
||||
"Alias paths have to stay within the same top level space.",
|
||||
}
|
||||
ErrTopLevelPathNotAllowed = &ValidationError{
|
||||
"Top level alias paths are not allowed.",
|
||||
}
|
||||
)
|
||||
|
||||
/*
|
||||
* Path checks the provided path and returns an error in it isn't valid.
|
||||
*
|
||||
* NOTE: A repository path can be one deeper than a space path (as otherwise the space would be useless)
|
||||
*/
|
||||
// Path checks the provided path and returns an error in it isn't valid
|
||||
// NOTE: A repository path can be one deeper than a space path (as otherwise the space would be useless).
|
||||
func Path(path string, isSpace bool) error {
|
||||
|
||||
if path == "" {
|
||||
return ErrPathEmpty
|
||||
}
|
||||
|
@ -62,17 +71,16 @@ func Path(path string, isSpace bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
* Validates a PathParams object that is used to create a new path.
|
||||
*
|
||||
* NOTES:
|
||||
* - We don't allow top level alias paths
|
||||
* - An alias path has to stay within the same top level space
|
||||
*
|
||||
* IMPORTANT:
|
||||
* Technically there can be a racing condition when a space is being moved inbetween the validation and path creation.
|
||||
* But that is fine, as the path could've also been created a second earlier when it was still valid and would then still exist.
|
||||
*/
|
||||
// PathParams validates a PathParams object that is used to create a new path.
|
||||
// NOTES:
|
||||
// - We don't allow top level alias paths
|
||||
// - An alias path has to stay within the same top level space
|
||||
//
|
||||
// IMPORTANT:
|
||||
// - Technically there can be a racing condition when a space is being moved in between the validation and
|
||||
// path creation.
|
||||
// - But that is fine, as the path could've also been created a second earlier when it was still valid and would
|
||||
// then still exist.
|
||||
func PathParams(path *types.PathParams, currentPath string, isSpace bool) error {
|
||||
// ensure the path is valid
|
||||
if err := Path(path.Path, isSpace); err != nil {
|
||||
|
@ -93,11 +101,8 @@ func PathParams(path *types.PathParams, currentPath string, isSpace bool) error
|
|||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if the provided path is too long.
|
||||
*
|
||||
* NOTE: A repository path can be one deeper than a space path (as otherwise the space would be useless)
|
||||
*/
|
||||
// PathTooLong Checks if the provided path is too long.
|
||||
// NOTE: A repository path can be one deeper than a space path (as otherwise the space would be useless).
|
||||
func PathTooLong(path string, isSpace bool) bool {
|
||||
l := strings.Count(path, types.PathSeparator) + 1
|
||||
return (!isSpace && l > maxPathSegments) || (isSpace && l > maxPathSegmentsForSpace)
|
||||
|
|
|
@ -9,7 +9,9 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrRepositoryRequiresSpaceId = &CheckError{"SpaceId required - Repositories don't exist outside of a space."}
|
||||
ErrRepositoryRequiresSpaceID = &ValidationError{
|
||||
"SpaceID required - Repositories don't exist outside of a space.",
|
||||
}
|
||||
)
|
||||
|
||||
// Repo checks the provided repository and returns an error in it isn't valid.
|
||||
|
@ -25,8 +27,8 @@ func Repo(repo *types.Repository) error {
|
|||
}
|
||||
|
||||
// validate repo within a space
|
||||
if repo.SpaceId <= 0 {
|
||||
return ErrRepositoryRequiresSpaceId
|
||||
if repo.SpaceID <= 0 {
|
||||
return ErrRepositoryRequiresSpaceID
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -14,11 +14,15 @@ import (
|
|||
var (
|
||||
illegalRootSpaceNames = []string{"api"}
|
||||
|
||||
ErrRootSpaceNameNotAllowed = &CheckError{fmt.Sprintf("The following names are not allowed for a root space: %v", illegalRootSpaceNames)}
|
||||
ErrInvalidParentSpaceId = &CheckError{"Parent space ID has to be either zero for a root space or greater than zero for a child space."}
|
||||
ErrRootSpaceNameNotAllowed = &ValidationError{
|
||||
fmt.Sprintf("The following names are not allowed for a root space: %v", illegalRootSpaceNames),
|
||||
}
|
||||
ErrInvalidParentSpaceID = &ValidationError{
|
||||
"Parent space ID has to be either zero for a root space or greater than zero for a child space.",
|
||||
}
|
||||
)
|
||||
|
||||
// Repo checks the provided space and returns an error in it isn't valid.
|
||||
// Space checks the provided space and returns an error in it isn't valid.
|
||||
func Space(space *types.Space) error {
|
||||
// validate name
|
||||
if err := Name(space.Name); err != nil {
|
||||
|
@ -30,12 +34,12 @@ func Space(space *types.Space) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if space.ParentId < 0 {
|
||||
return ErrInvalidParentSpaceId
|
||||
if space.ParentID < 0 {
|
||||
return ErrInvalidParentSpaceID
|
||||
}
|
||||
|
||||
// root space specific validations
|
||||
if space.ParentId == 0 {
|
||||
if space.ParentID == 0 {
|
||||
for _, p := range illegalRootSpaceNames {
|
||||
if strings.HasPrefix(space.Name, p) {
|
||||
return ErrRootSpaceNameNotAllowed
|
||||
|
|
|
@ -18,15 +18,17 @@ const (
|
|||
var (
|
||||
// ErrEmailLen is returned when the email address
|
||||
// exceeds the maximum number of characters.
|
||||
ErrEmailLen = &CheckError{fmt.Sprintf("Email address has to be within %d and %d characters", minEmailLength, maxEmailLength)}
|
||||
ErrEmailLen = &ValidationError{
|
||||
fmt.Sprintf("Email address has to be within %d and %d characters", minEmailLength, maxEmailLength),
|
||||
}
|
||||
)
|
||||
|
||||
// User returns true if the User if valid.
|
||||
func User(user *types.User) (bool, error) {
|
||||
func User(user *types.User) error {
|
||||
// validate email
|
||||
l := len(user.Email)
|
||||
if l < minEmailLength || l > maxEmailLength {
|
||||
return false, ErrEmailLen
|
||||
return ErrEmailLen
|
||||
}
|
||||
return true, nil
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/harness/gitness/types"
|
||||
|
@ -14,20 +15,15 @@ func TestUser(t *testing.T) {
|
|||
tests := []struct {
|
||||
email string
|
||||
error error
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
email: "jane@gmail.com",
|
||||
valid: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
user := &types.User{Email: test.email}
|
||||
ok, err := User(user)
|
||||
if got, want := ok, test.valid; got != want {
|
||||
t.Errorf("Want user %s is valid %v, got %v", test.email, want, got)
|
||||
}
|
||||
if got, want := err, test.error; got != want {
|
||||
err := User(user)
|
||||
if got, want := err, test.error; !errors.Is(got, want) {
|
||||
t.Errorf("Want user %s error %v, got %v", test.email, want, got)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ type Config struct {
|
|||
Cors struct {
|
||||
AllowedOrigins []string `envconfig:"APP_CORS_ALLOWED_ORIGINS" default:"*"`
|
||||
AllowedMethods []string `envconfig:"APP_CORS_ALLOWED_METHODS" default:"GET,POST,PATCH,PUT,DELETE,OPTIONS"`
|
||||
AllowedHeaders []string `envconfig:"APP_CORS_ALLOWED_HEADERS" default:"Origin,Accept,Accept-Language,Authorization,Content-Type,Content-Language,X-Requested-With,X-Request-Id"`
|
||||
AllowedHeaders []string `envconfig:"APP_CORS_ALLOWED_HEADERS" default:"Origin,Accept,Accept-Language,Authorization,Content-Type,Content-Language,X-Requested-With,X-Request-Id"` //nolint:lll // struct tags can't be multiline
|
||||
ExposedHeaders []string `envconfig:"APP_CORS_EXPOSED_HEADERS" default:"Link"`
|
||||
AllowCredentials bool `envconfig:"APP_CORS_ALLOW_CREDENTIALS" default:"true"`
|
||||
MaxAge int `envconfig:"APP_CORS_MAX_AGE" default:"300"`
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
package enum
|
||||
|
||||
// Represents the different types of resources that can be guarded with permissions.
|
||||
// ResourceType represents the different types of resources that can be guarded with permissions.
|
||||
type ResourceType string
|
||||
|
||||
const (
|
||||
|
@ -13,36 +13,33 @@ const (
|
|||
// ResourceType_Branch ResourceType = "BRANCH"
|
||||
)
|
||||
|
||||
// Represents the available permissions
|
||||
// Permission represents the available permissions.
|
||||
type Permission string
|
||||
|
||||
const (
|
||||
// ----- SPACE -----
|
||||
/*
|
||||
----- SPACE -----
|
||||
*/
|
||||
PermissionSpaceCreate Permission = "space_create"
|
||||
PermissionSpaceView Permission = "space_view"
|
||||
PermissionSpaceEdit Permission = "space_edit"
|
||||
PermissionSpaceDelete Permission = "space_delete"
|
||||
)
|
||||
|
||||
// ----- REPOSITORY -----
|
||||
const (
|
||||
/*
|
||||
----- REPOSITORY -----
|
||||
*/
|
||||
PermissionRepoCreate Permission = "repository_create"
|
||||
PermissionRepoView Permission = "repository_view"
|
||||
PermissionRepoEdit Permission = "repository_edit"
|
||||
PermissionRepoDelete Permission = "repository_delete"
|
||||
|
||||
// ----- BRANCH -----
|
||||
// PermissionBranchCreate Permission = "branch_create"
|
||||
// PermissionBranchView Permission = "branch_view"
|
||||
// PermissionBranchEdit Permission = "branch_edit"
|
||||
// PermissionBranchDelete Permission = "branch_delete"
|
||||
)
|
||||
|
||||
// Represents the type of the entity requesting permission
|
||||
// PrincipalType represents the type of the entity requesting permission.
|
||||
type PrincipalType string
|
||||
|
||||
const (
|
||||
// Represents actions executed by a loged-in user
|
||||
// PrincipalTypeUser represents actions executed by a logged-in user.
|
||||
PrincipalTypeUser PrincipalType = "USER"
|
||||
|
||||
// Represents actions executed by an entity with an api key
|
||||
PrincipalTypeApiKey PrincipalType = "API_KEY"
|
||||
)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue