Restart.
Data foundation started, with updated guidelines and golangci-lint config.
This commit is contained in:
497
.golangci.yml
Normal file
497
.golangci.yml
Normal file
@@ -0,0 +1,497 @@
|
|||||||
|
# This file is licensed under the terms of the MIT license https://opensource.org/license/mit
|
||||||
|
# Copyright (c) 2021-2025 Marat Reymers
|
||||||
|
|
||||||
|
## Golden config for golangci-lint v2.6.2
|
||||||
|
#
|
||||||
|
# This is the best config for golangci-lint based on my experience and opinion.
|
||||||
|
# It is very strict, but not extremely strict.
|
||||||
|
# Feel free to adapt it to suit your needs.
|
||||||
|
# If this config helps you, please consider keeping a link to this file (see the next comment).
|
||||||
|
|
||||||
|
# Based on https://gist.github.com/maratori/47a4d00457a92aa426dbd48a18776322
|
||||||
|
|
||||||
|
version: "2"
|
||||||
|
|
||||||
|
issues:
|
||||||
|
# Maximum count of issues with the same text.
|
||||||
|
# Set to 0 to disable.
|
||||||
|
# Default: 3
|
||||||
|
max-same-issues: 50
|
||||||
|
|
||||||
|
# Exclude some lints for CLI programs under cmd/ (package main).
|
||||||
|
# The project allows fmt.Print* in command-line tools; keep forbidigo for libraries.
|
||||||
|
exclude-rules:
|
||||||
|
- path: ^cmd/
|
||||||
|
linters:
|
||||||
|
- forbidigo
|
||||||
|
- path: cmd/.*
|
||||||
|
linters:
|
||||||
|
- forbidigo
|
||||||
|
- path: .*/cmd/.*
|
||||||
|
linters:
|
||||||
|
- forbidigo
|
||||||
|
|
||||||
|
formatters:
|
||||||
|
enable:
|
||||||
|
- goimports # checks if the code and import statements are formatted according to the 'goimports' command
|
||||||
|
- golines # checks if code is formatted, and fixes long lines
|
||||||
|
|
||||||
|
## you may want to enable
|
||||||
|
#- gci # checks if code and import statements are formatted, with additional rules
|
||||||
|
#- gofmt # checks if the code is formatted according to 'gofmt' command
|
||||||
|
#- gofumpt # enforces a stricter format than 'gofmt', while being backwards compatible
|
||||||
|
#- swaggo # formats swaggo comments
|
||||||
|
|
||||||
|
# All settings can be found here https://github.com/golangci/golangci-lint/blob/HEAD/.golangci.reference.yml
|
||||||
|
settings:
|
||||||
|
goimports:
|
||||||
|
# A list of prefixes, which, if set, checks import paths
|
||||||
|
# with the given prefixes are grouped after 3rd-party packages.
|
||||||
|
# Default: []
|
||||||
|
local-prefixes:
|
||||||
|
- github.com/my/project
|
||||||
|
|
||||||
|
golines:
|
||||||
|
# Target maximum line length.
|
||||||
|
# Default: 100
|
||||||
|
max-len: 120
|
||||||
|
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- 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
|
||||||
|
- canonicalheader # checks whether net/http.Header uses canonical header
|
||||||
|
- copyloopvar # detects places where loop variables are copied (Go 1.22+)
|
||||||
|
- cyclop # checks function and package cyclomatic complexity
|
||||||
|
- depguard # checks if package imports are in a list of acceptable packages
|
||||||
|
- dupl # tool for code clone detection
|
||||||
|
- durationcheck # checks for two durations multiplied together
|
||||||
|
- errcheck # checking for unchecked errors, these unchecked errors can be critical bugs in some cases
|
||||||
|
- 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
|
||||||
|
- exhaustive # checks exhaustiveness of enum switch statements
|
||||||
|
- exptostd # detects functions from golang.org/x/exp/ that can be replaced by std functions
|
||||||
|
- fatcontext # detects nested contexts in loops
|
||||||
|
- forbidigo # forbids identifiers
|
||||||
|
- funcorder # checks the order of functions, methods, and constructors
|
||||||
|
- funlen # tool for detection of long functions
|
||||||
|
- gocheckcompilerdirectives # validates go compiler directive comments (//go:)
|
||||||
|
- gochecksumtype # checks exhaustiveness on Go "sum types"
|
||||||
|
- 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
|
||||||
|
- godoclint # checks Golang's documentation practice
|
||||||
|
- godot # checks if comments end in a period
|
||||||
|
- gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod
|
||||||
|
- gosec # inspects source code for security problems
|
||||||
|
- govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
|
||||||
|
- iface # checks the incorrect use of interfaces, helping developers avoid interface pollution
|
||||||
|
- ineffassign # detects when assignments to existing variables are not used
|
||||||
|
- intrange # finds places where for loops could make use of an integer range
|
||||||
|
- iotamixing # checks if iotas are being used in const blocks with other non-iota declarations
|
||||||
|
- loggercheck # checks key value pairs for common logger libraries (kitlog,klog,logr,zap)
|
||||||
|
- makezero # finds slice declarations with non-zero initial length
|
||||||
|
- mirror # reports wrong mirror patterns of bytes/strings usage
|
||||||
|
- mnd # detects magic numbers
|
||||||
|
- modernize # suggests simplifications to Go code, using modern language and library features
|
||||||
|
- musttag # enforces field tags in (un)marshaled structs
|
||||||
|
- 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
|
||||||
|
- nilnesserr # reports that it checks for err != nil, but it returns a different nil value error (powered by nilness and nilerr)
|
||||||
|
- 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
|
||||||
|
- perfsprint # checks that fmt.Sprintf can be replaced with a faster alternative
|
||||||
|
- predeclared # finds code that shadows one of Go's predeclared identifiers
|
||||||
|
- promlinter # checks Prometheus metrics naming via promlint
|
||||||
|
- protogetter # reports direct reads from proto message fields when getters should be used
|
||||||
|
- reassign # checks that package variables are not reassigned
|
||||||
|
- recvcheck # checks for receiver type consistency
|
||||||
|
- revive # fast, configurable, extensible, flexible, and beautiful linter for Go, drop-in replacement of golint
|
||||||
|
- rowserrcheck # checks whether Err of rows is checked successfully
|
||||||
|
- sloglint # ensure consistent code style when using log/slog
|
||||||
|
- spancheck # checks for mistakes with OpenTelemetry/Census spans
|
||||||
|
- sqlclosecheck # checks that sql.Rows and sql.Stmt are closed
|
||||||
|
- staticcheck # is a go vet on steroids, applying a ton of static analysis checks
|
||||||
|
- testableexamples # checks if examples are testable (have an expected output)
|
||||||
|
- testifylint # checks usage of github.com/stretchr/testify
|
||||||
|
- 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
|
||||||
|
- unqueryvet # detects SELECT * in SQL queries and SQL builders, encouraging explicit column selection
|
||||||
|
- unused # checks for unused constants, variables, functions and types
|
||||||
|
- usestdlibvars # detects the possibility to use variables/constants from the Go standard library
|
||||||
|
- usetesting # reports uses of functions with replacement inside the testing package
|
||||||
|
- wastedassign # finds wasted assignment statements
|
||||||
|
- whitespace # detects leading and trailing whitespace
|
||||||
|
|
||||||
|
## you may want to enable
|
||||||
|
#- arangolint # opinionated best practices for arangodb client
|
||||||
|
#- decorder # checks declaration order and count of types, constants, variables and functions
|
||||||
|
#- exhaustruct # [highly recommend to enable] checks if all structure fields are initialized
|
||||||
|
#- ginkgolinter # [if you use ginkgo/gomega] enforces standards of using ginkgo and gomega
|
||||||
|
#- godox # detects usage of FIXME, TODO and other keywords inside comments
|
||||||
|
#- goheader # checks is file header matches to pattern
|
||||||
|
#- inamedparam # [great idea, but too strict, need to ignore a lot of cases by default] reports interfaces with unnamed method parameters
|
||||||
|
#- interfacebloat # checks the number of methods inside an interface
|
||||||
|
#- ireturn # accept interfaces, return concrete types
|
||||||
|
#- noinlineerr # disallows inline error handling `if err := ...; err != nil {`
|
||||||
|
#- prealloc # [premature optimization, but can be used in some cases] finds slice declarations that could potentially be preallocated
|
||||||
|
#- tagalign # checks that struct tags are well aligned
|
||||||
|
#- 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
|
||||||
|
#- zerologlint # detects the wrong usage of zerolog that a user forgets to dispatch zerolog.Event
|
||||||
|
|
||||||
|
## disabled
|
||||||
|
#- containedctx # detects struct contained context.Context field
|
||||||
|
#- contextcheck # [too many false positives] checks the function whether use a non-inherited context
|
||||||
|
#- dogsled # checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
|
||||||
|
#- dupword # [useless without config] checks for duplicate words in the source code
|
||||||
|
#- err113 # [too strict] checks the errors handling expressions
|
||||||
|
#- 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
|
||||||
|
#- gomodguard # [use more powerful depguard] allow and block lists linter for direct Go module dependencies
|
||||||
|
#- gosmopolitan # reports certain i18n/l10n anti-patterns in your Go codebase
|
||||||
|
#- grouper # analyzes expression groups
|
||||||
|
#- importas # enforces consistent import aliases
|
||||||
|
#- lll # [replaced by golines] reports long lines
|
||||||
|
#- 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
|
||||||
|
#- wsl_v5 # [too strict and mostly code is not more readable] add or remove empty lines
|
||||||
|
|
||||||
|
# All settings can be found here https://github.com/golangci/golangci-lint/blob/HEAD/.golangci.reference.yml
|
||||||
|
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
|
||||||
|
|
||||||
|
depguard:
|
||||||
|
# Rules to apply.
|
||||||
|
#
|
||||||
|
# Variables:
|
||||||
|
# - File Variables
|
||||||
|
# Use an exclamation mark `!` to negate a variable.
|
||||||
|
# Example: `!$test` matches any file that is not a go test file.
|
||||||
|
#
|
||||||
|
# `$all` - matches all go files
|
||||||
|
# `$test` - matches all go test files
|
||||||
|
#
|
||||||
|
# - Package Variables
|
||||||
|
#
|
||||||
|
# `$gostd` - matches all of go's standard library (Pulled from `GOROOT`)
|
||||||
|
#
|
||||||
|
# Default (applies if no custom rules are defined): Only allow $gostd in all files.
|
||||||
|
rules:
|
||||||
|
"deprecated":
|
||||||
|
# List of file globs that will match this list of settings to compare against.
|
||||||
|
# By default, if a path is relative, it is relative to the directory where the golangci-lint command is executed.
|
||||||
|
# The placeholder '${base-path}' is substituted with a path relative to the mode defined with `run.relative-path-mode`.
|
||||||
|
# The placeholder '${config-path}' is substituted with a path relative to the configuration file.
|
||||||
|
# Default: $all
|
||||||
|
files:
|
||||||
|
- "$all"
|
||||||
|
# List of packages that are not allowed.
|
||||||
|
# Entries can be a variable (starting with $), a string prefix, or an exact match (if ending with $).
|
||||||
|
# Default: []
|
||||||
|
deny:
|
||||||
|
- pkg: github.com/golang/protobuf
|
||||||
|
desc: Use google.golang.org/protobuf instead, see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules
|
||||||
|
- pkg: github.com/satori/go.uuid
|
||||||
|
desc: Use github.com/google/uuid instead, satori's package is not maintained
|
||||||
|
- pkg: github.com/gofrs/uuid$
|
||||||
|
desc: Use github.com/gofrs/uuid/v5 or later, it was not a go module before v5
|
||||||
|
"non-test files":
|
||||||
|
files:
|
||||||
|
- "!$test"
|
||||||
|
deny:
|
||||||
|
- pkg: math/rand$
|
||||||
|
desc: Use math/rand/v2 instead, see https://go.dev/blog/randv2
|
||||||
|
"non-main files":
|
||||||
|
files:
|
||||||
|
- "!**/main.go"
|
||||||
|
deny:
|
||||||
|
- pkg: log$
|
||||||
|
desc: Use log/slog instead, see https://go.dev/blog/slog
|
||||||
|
|
||||||
|
embeddedstructfieldcheck:
|
||||||
|
# Checks that sync.Mutex and sync.RWMutex are not used as embedded fields.
|
||||||
|
# Default: false
|
||||||
|
forbid-mutex: true
|
||||||
|
|
||||||
|
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
|
||||||
|
exclude-functions:
|
||||||
|
- (*git.wntrmute.dev/kyle/goutils/sbuf.Buffer).Write
|
||||||
|
- git.wntrmute.dev/kyle/goutils/lib.Warn
|
||||||
|
- git.wntrmute.dev/kyle/goutils/lib.Warnx
|
||||||
|
- git.wntrmute.dev/kyle/goutils/lib.Err
|
||||||
|
- git.wntrmute.dev/kyle/goutils/lib.Errx
|
||||||
|
|
||||||
|
exhaustive:
|
||||||
|
# Program elements to check for exhaustiveness.
|
||||||
|
# Default: [ switch ]
|
||||||
|
check:
|
||||||
|
- switch
|
||||||
|
- map
|
||||||
|
|
||||||
|
exhaustruct:
|
||||||
|
# List of regular expressions to match type names that should be excluded from processing.
|
||||||
|
# Anonymous structs can be matched by '<anonymous>' alias.
|
||||||
|
# Has precedence over `include`.
|
||||||
|
# Each regular expression must match the full type name, including package path.
|
||||||
|
# For example, to match type `net/http.Cookie` regular expression should be `.*/http\.Cookie`,
|
||||||
|
# but not `http\.Cookie`.
|
||||||
|
# Default: []
|
||||||
|
exclude:
|
||||||
|
# std libs
|
||||||
|
- ^net/http.Client$
|
||||||
|
- ^net/http.Cookie$
|
||||||
|
- ^net/http.Request$
|
||||||
|
- ^net/http.Response$
|
||||||
|
- ^net/http.Server$
|
||||||
|
- ^net/http.Transport$
|
||||||
|
- ^net/url.URL$
|
||||||
|
- ^os/exec.Cmd$
|
||||||
|
- ^reflect.StructField$
|
||||||
|
# public libs
|
||||||
|
- ^github.com/Shopify/sarama.Config$
|
||||||
|
- ^github.com/Shopify/sarama.ProducerMessage$
|
||||||
|
- ^github.com/mitchellh/mapstructure.DecoderConfig$
|
||||||
|
- ^github.com/prometheus/client_golang/.+Opts$
|
||||||
|
- ^github.com/spf13/cobra.Command$
|
||||||
|
- ^github.com/spf13/cobra.CompletionOptions$
|
||||||
|
- ^github.com/stretchr/testify/mock.Mock$
|
||||||
|
- ^github.com/testcontainers/testcontainers-go.+Request$
|
||||||
|
- ^github.com/testcontainers/testcontainers-go.FromDockerfile$
|
||||||
|
- ^golang.org/x/tools/go/analysis.Analyzer$
|
||||||
|
- ^google.golang.org/protobuf/.+Options$
|
||||||
|
- ^gopkg.in/yaml.v3.Node$
|
||||||
|
# Allows empty structures in return statements.
|
||||||
|
# Default: false
|
||||||
|
allow-empty-returns: true
|
||||||
|
|
||||||
|
funcorder:
|
||||||
|
# Checks if the exported methods of a structure are placed before the non-exported ones.
|
||||||
|
# Default: true
|
||||||
|
struct-method: false
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
gochecksumtype:
|
||||||
|
# Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed.
|
||||||
|
# Default: true
|
||||||
|
default-signifies-exhaustive: false
|
||||||
|
|
||||||
|
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 found at https://go-critic.com/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
|
||||||
|
|
||||||
|
godoclint:
|
||||||
|
# List of rules to enable in addition to the default set.
|
||||||
|
# Default: empty
|
||||||
|
enable:
|
||||||
|
# Assert no unused link in godocs.
|
||||||
|
# https://github.com/godoc-lint/godoc-lint?tab=readme-ov-file#no-unused-link
|
||||||
|
- no-unused-link
|
||||||
|
|
||||||
|
gosec:
|
||||||
|
excludes:
|
||||||
|
- G104 # handled by errcheck
|
||||||
|
- G301
|
||||||
|
- G306
|
||||||
|
|
||||||
|
govet:
|
||||||
|
# Enable all analyzers.
|
||||||
|
# Default: false
|
||||||
|
enable-all: true
|
||||||
|
# Disable analyzers by name.
|
||||||
|
# Run `GL_DEBUG=govet golangci-lint run --enable=govet` to see default, all available analyzers, and enabled analyzers.
|
||||||
|
# Default: []
|
||||||
|
disable:
|
||||||
|
- fieldalignment # too strict
|
||||||
|
# Settings per analyzer.
|
||||||
|
settings:
|
||||||
|
shadow:
|
||||||
|
# Whether to be strict about shadowing; can be noisy.
|
||||||
|
# Default: false
|
||||||
|
strict: true
|
||||||
|
|
||||||
|
inamedparam:
|
||||||
|
# Skips check for interface methods with only a single parameter.
|
||||||
|
# Default: false
|
||||||
|
skip-single-param: true
|
||||||
|
|
||||||
|
mnd:
|
||||||
|
ignored-functions:
|
||||||
|
- args.Error
|
||||||
|
- flag.Arg
|
||||||
|
- flag.Duration.*
|
||||||
|
- flag.Float.*
|
||||||
|
- flag.Int.*
|
||||||
|
- flag.Uint.*
|
||||||
|
- os.Chmod
|
||||||
|
- os.Mkdir.*
|
||||||
|
- os.OpenFile
|
||||||
|
- os.WriteFile
|
||||||
|
- prometheus.ExponentialBuckets.*
|
||||||
|
- prometheus.LinearBuckets
|
||||||
|
ignored-numbers:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
- 4
|
||||||
|
- 8
|
||||||
|
|
||||||
|
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 the following linters from requiring an explanation.
|
||||||
|
# Default: []
|
||||||
|
allow-no-explanation: [ funlen, gocognit, golines ]
|
||||||
|
# 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
|
||||||
|
|
||||||
|
perfsprint:
|
||||||
|
# Optimizes into strings concatenation.
|
||||||
|
# Default: true
|
||||||
|
strconcat: false
|
||||||
|
|
||||||
|
reassign:
|
||||||
|
# Patterns for global variable names that are checked for reassignment.
|
||||||
|
# See https://github.com/curioswitch/go-reassign#usage
|
||||||
|
# Default: ["EOF", "Err.*"]
|
||||||
|
patterns:
|
||||||
|
- ".*"
|
||||||
|
|
||||||
|
rowserrcheck:
|
||||||
|
# database/sql is always checked.
|
||||||
|
# Default: []
|
||||||
|
packages:
|
||||||
|
- github.com/jmoiron/sqlx
|
||||||
|
|
||||||
|
sloglint:
|
||||||
|
# Enforce not using global loggers.
|
||||||
|
# Values:
|
||||||
|
# - "": disabled
|
||||||
|
# - "all": report all global loggers
|
||||||
|
# - "default": report only the default slog logger
|
||||||
|
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-global
|
||||||
|
# Default: ""
|
||||||
|
no-global: all
|
||||||
|
# Enforce using methods that accept a context.
|
||||||
|
# Values:
|
||||||
|
# - "": disabled
|
||||||
|
# - "all": report all contextless calls
|
||||||
|
# - "scope": report only if a context exists in the scope of the outermost function
|
||||||
|
# https://github.com/go-simpler/sloglint?tab=readme-ov-file#context-only
|
||||||
|
# Default: ""
|
||||||
|
context: scope
|
||||||
|
|
||||||
|
staticcheck:
|
||||||
|
# SAxxxx checks in https://staticcheck.dev/docs/configuration/options/#checks
|
||||||
|
# Example (to disable some checks): [ "all", "-SA1000", "-SA1001"]
|
||||||
|
# Default: ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022"]
|
||||||
|
checks:
|
||||||
|
- all
|
||||||
|
# Incorrect or missing package comment.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1000
|
||||||
|
- -ST1000
|
||||||
|
# Use consistent method receiver names.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1016
|
||||||
|
- -ST1016
|
||||||
|
# Omit embedded fields from selector expression.
|
||||||
|
# https://staticcheck.dev/docs/checks/#QF1008
|
||||||
|
- -QF1008
|
||||||
|
# We often explicitly enable old/deprecated ciphers for research.
|
||||||
|
- -SA1019
|
||||||
|
|
||||||
|
usetesting:
|
||||||
|
# Enable/disable `os.TempDir()` detections.
|
||||||
|
# Default: false
|
||||||
|
os-temp-dir: true
|
||||||
|
|
||||||
|
exclusions:
|
||||||
|
# Log a warning if an exclusion rule is unused.
|
||||||
|
# Default: false
|
||||||
|
warn-unused: true
|
||||||
|
# Predefined exclusion rules.
|
||||||
|
# Default: []
|
||||||
|
presets:
|
||||||
|
- std-error-handling
|
||||||
|
- common-false-positives
|
||||||
|
rules:
|
||||||
|
- path: 'main.go'
|
||||||
|
linters: [ forbidigo, mnd, reassign ]
|
||||||
|
- source: 'TODO'
|
||||||
|
linters: [ godot ]
|
||||||
|
- text: 'should have a package comment'
|
||||||
|
linters: [ revive ]
|
||||||
|
- text: 'exported \S+ \S+ should have comment( \(or a comment on this block\))? or be unexported'
|
||||||
|
linters: [ revive ]
|
||||||
|
- text: 'package comment should be of the form ".+"'
|
||||||
|
source: '// ?(nolint|TODO)'
|
||||||
|
linters: [ revive ]
|
||||||
|
- text: 'comment on exported \S+ \S+ should be of the form ".+"'
|
||||||
|
source: '// ?(nolint|TODO)'
|
||||||
|
linters: [ revive, staticcheck ]
|
||||||
|
- path: '_test\.go'
|
||||||
|
linters:
|
||||||
|
- bodyclose
|
||||||
|
- dupl
|
||||||
|
- errcheck
|
||||||
|
- funlen
|
||||||
|
- goconst
|
||||||
|
- gosec
|
||||||
|
- noctx
|
||||||
|
- reassign
|
||||||
|
- wrapcheck
|
||||||
@@ -24,7 +24,7 @@ apps that I write.
|
|||||||
the role named the same as a service account can issue tokens for that
|
the role named the same as a service account can issue tokens for that
|
||||||
service account.
|
service account.
|
||||||
- Admin users can also revoke tokens for a service account.
|
- Admin users can also revoke tokens for a service account.
|
||||||
- Service accounts (and users with the a role named the same as the
|
- Service accounts (and users with a role named the same as the
|
||||||
service account) can also retrieve Postgres database credentials for
|
service account) can also retrieve Postgres database credentials for
|
||||||
the service account.
|
the service account.
|
||||||
|
|
||||||
@@ -41,10 +41,13 @@ apps that I write.
|
|||||||
|
|
||||||
- The primary interface will be an REST API over HTTPS. TLS security is
|
- The primary interface will be an REST API over HTTPS. TLS security is
|
||||||
critical for this.
|
critical for this.
|
||||||
- There should be two command line tools associated with MCIAS:
|
- There should be a single command line program using cobra/viper. It offers the following subcommands:
|
||||||
- mciassrv is the authentication server.
|
- `db`: manage database credentials.
|
||||||
- mciasctl is the tool for admins to create and manage accounts, issue
|
- `role`: manage roles.
|
||||||
or revoke tokens, and manage postgres database credentials.
|
- `server`: run the server.
|
||||||
|
- `token`: obtain a token for a service account.
|
||||||
|
- `user`: manage users.
|
||||||
|
|
||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
|
|
||||||
|
|||||||
47
data/totp.go
Normal file
47
data/totp.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package data
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
twofactor "git.wntrmute.dev/kyle/goutils/twofactor"
|
||||||
|
)
|
||||||
|
|
||||||
|
const totpPeriod = 30
|
||||||
|
|
||||||
|
// builtinTOTPValidator delegates TOTP validation to goutils/twofactor.
|
||||||
|
type builtinTOTPValidator struct{}
|
||||||
|
|
||||||
|
func (builtinTOTPValidator) Validate(secret, code string, at time.Time, window int) bool {
|
||||||
|
if secret == "" || code == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Normalize secret similar to common authenticator apps: remove spaces and uppercase.
|
||||||
|
norm := strings.ToUpper(strings.ReplaceAll(secret, " ", ""))
|
||||||
|
norm = twofactor.Pad(norm)
|
||||||
|
otp, err := twofactor.NewGoogleTOTP(norm)
|
||||||
|
if err != nil || otp == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the base counter for the provided time (period 30s, start 0).
|
||||||
|
base := uint64(at.Unix()&unsignedMask64) / totpPeriod // #nosec G115 - masked off overflow
|
||||||
|
// Check +/- window steps.
|
||||||
|
for i := -window; i <= window; i++ {
|
||||||
|
var ctr uint64
|
||||||
|
if i < 0 {
|
||||||
|
// Guard underflow.
|
||||||
|
offs := uint64(-i)
|
||||||
|
if offs > base {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ctr = base - offs
|
||||||
|
} else {
|
||||||
|
ctr = base + uint64(i)
|
||||||
|
}
|
||||||
|
if otp.OATH.OTP(ctr) == code {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
14
data/types.go
Normal file
14
data/types.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package data
|
||||||
|
|
||||||
|
const (
|
||||||
|
unsignedMask64 = 0x7FFFFFFF
|
||||||
|
)
|
||||||
|
|
||||||
|
// DBCredentials holds generated database access parameters for a service account.
|
||||||
|
type DBCredentials struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Database string
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
}
|
||||||
160
data/user.go
Normal file
160
data/user.go
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
package data
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/subtle"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/scrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccountType distinguishes human users from system/service accounts.
|
||||||
|
type AccountType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
AccountHuman AccountType = "human"
|
||||||
|
AccountSystem AccountType = "system"
|
||||||
|
)
|
||||||
|
|
||||||
|
// User represents an identity in the system. It supports password and optional TOTP.
|
||||||
|
type User struct {
|
||||||
|
ID string
|
||||||
|
Username string
|
||||||
|
Type AccountType
|
||||||
|
Roles []string
|
||||||
|
|
||||||
|
// Password hashing material
|
||||||
|
pwdHash []byte
|
||||||
|
pwdSalt []byte
|
||||||
|
|
||||||
|
// Base32 encoded TOTP secret (RFC 3548). Empty means TOTP disabled.
|
||||||
|
TOTPSecret string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scrypt parameters. Chosen for interactive logins; can be tuned later.
|
||||||
|
const (
|
||||||
|
scryptN = 1 << 15 // 32768
|
||||||
|
scryptR = 8
|
||||||
|
scryptP = 1
|
||||||
|
keyLen = 32
|
||||||
|
saltLen = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetPassword hashes and stores the provided password using scrypt with a random salt.
|
||||||
|
func (u *User) SetPassword(password string) error {
|
||||||
|
if password == "" {
|
||||||
|
return errors.New("password cannot be empty")
|
||||||
|
}
|
||||||
|
salt := make([]byte, saltLen)
|
||||||
|
if _, err := rand.Read(salt); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dk, err := scrypt.Key([]byte(password), salt, scryptN, scryptR, scryptP, keyLen)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
u.pwdSalt = salt
|
||||||
|
u.pwdHash = dk
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckPassword verifies a plaintext password against the stored scrypt hash.
|
||||||
|
func (u *User) CheckPassword(password string) bool {
|
||||||
|
if len(u.pwdSalt) == 0 || len(u.pwdHash) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
dk, err := scrypt.Key([]byte(password), u.pwdSalt, scryptN, scryptR, scryptP, keyLen)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return subtle.ConstantTimeCompare(dk, u.pwdHash) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// PasswordHash returns a portable string representation of the scrypt hash and salt.
|
||||||
|
// The format is: scrypt:N:r:p:base64(salt):base64(hash).
|
||||||
|
func (u *User) PasswordHash() string {
|
||||||
|
if len(u.pwdSalt) == 0 || len(u.pwdHash) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return "scrypt:" +
|
||||||
|
strconv.Itoa(scryptN) + ":" + strconv.Itoa(scryptR) + ":" + strconv.Itoa(scryptP) + ":" +
|
||||||
|
base64.RawStdEncoding.EncodeToString(u.pwdSalt) + ":" +
|
||||||
|
base64.RawStdEncoding.EncodeToString(u.pwdHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
const passwordHashParts = 5
|
||||||
|
|
||||||
|
// LoadPasswordHash parses the value produced by PasswordHash and loads it into the user.
|
||||||
|
func (u *User) LoadPasswordHash(s string) error {
|
||||||
|
if s == "" {
|
||||||
|
u.pwdSalt = nil
|
||||||
|
u.pwdHash = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
const prefix = "scrypt:"
|
||||||
|
if len(s) < len(prefix) || s[:len(prefix)] != prefix {
|
||||||
|
return errors.New("unsupported password hash format")
|
||||||
|
}
|
||||||
|
parts := splitN(s[len(prefix):], ':', passwordHashParts)
|
||||||
|
if len(parts) != passwordHashParts {
|
||||||
|
return errors.New("invalid password hash")
|
||||||
|
}
|
||||||
|
// We currently ignore parsed N,r,p and use compiled constants to avoid variable-time params.
|
||||||
|
salt, err := base64.RawStdEncoding.DecodeString(parts[3])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hash, err := base64.RawStdEncoding.DecodeString(parts[4])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(hash) != keyLen {
|
||||||
|
return errors.New("invalid hash length")
|
||||||
|
}
|
||||||
|
u.pwdSalt = salt
|
||||||
|
u.pwdHash = hash
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyTOTP checks a TOTP code for the user's TOTP secret. If no secret is set, it fails.
|
||||||
|
// The window parameter defines the allowed step skew (+/- window steps).
|
||||||
|
func (u *User) VerifyTOTP(code string, at time.Time, window int) bool {
|
||||||
|
if u.TOTPSecret == "" || code == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return defaultTOTPValidator.Validate(u.TOTPSecret, code, at, window)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TOTPValidator abstracts TOTP verification to allow swapping implementations.
|
||||||
|
type TOTPValidator interface {
|
||||||
|
Validate(secret, code string, at time.Time, window int) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultTOTPValidator TOTPValidator = builtinTOTPValidator{}
|
||||||
|
|
||||||
|
// splitN is like strings.SplitN but without importing another package here.
|
||||||
|
func splitN(s string, sep rune, n int) []string {
|
||||||
|
if n <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := make([]string, 0, n)
|
||||||
|
start := 0
|
||||||
|
count := 1
|
||||||
|
for i, r := range s {
|
||||||
|
if r == sep {
|
||||||
|
out = append(out, s[start:i])
|
||||||
|
start = i + 1
|
||||||
|
count++
|
||||||
|
if count == n {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if start <= len(s) {
|
||||||
|
out = append(out, s[start:])
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
49
data/user_test.go
Normal file
49
data/user_test.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package data_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.wntrmute.dev/kyle/mcias/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPasswordSetAndCheck(t *testing.T) {
|
||||||
|
var u data.User
|
||||||
|
if err := u.SetPassword("s3cret!"); err != nil {
|
||||||
|
t.Fatalf("SetPassword error: %v", err)
|
||||||
|
}
|
||||||
|
if !u.CheckPassword("s3cret!") {
|
||||||
|
t.Fatal("expected password to verify")
|
||||||
|
}
|
||||||
|
if u.CheckPassword("wrong") {
|
||||||
|
t.Fatal("expected wrong password to fail")
|
||||||
|
}
|
||||||
|
// Round-trip hash string
|
||||||
|
hs := u.PasswordHash()
|
||||||
|
if hs == "" {
|
||||||
|
t.Fatal("expected non-empty password hash string")
|
||||||
|
}
|
||||||
|
var u2 data.User
|
||||||
|
if err := u2.LoadPasswordHash(hs); err != nil {
|
||||||
|
t.Fatalf("LoadPasswordHash error: %v", err)
|
||||||
|
}
|
||||||
|
if !u2.CheckPassword("s3cret!") {
|
||||||
|
t.Fatal("expected password to verify after LoadPasswordHash")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTOTPValidationKnownVector(t *testing.T) {
|
||||||
|
// From RFC 6238 test secret (base32): "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"
|
||||||
|
// Using T0=0, step=30. For SHA1, at 59s, code should be 94287082 -> 6-digit 287082.
|
||||||
|
u := data.User{TOTPSecret: "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"}
|
||||||
|
ts := time.Unix(59, 0)
|
||||||
|
if !u.VerifyTOTP("287082", ts, 0) {
|
||||||
|
t.Fatal("expected TOTP code to verify for known vector")
|
||||||
|
}
|
||||||
|
if u.VerifyTOTP("287082", ts.Add(30*time.Second), 0) {
|
||||||
|
t.Fatal("expected code to fail outside time step with zero window")
|
||||||
|
}
|
||||||
|
if !u.VerifyTOTP("287082", ts.Add(30*time.Second), 1) {
|
||||||
|
t.Fatal("expected code to verify within window=1")
|
||||||
|
}
|
||||||
|
}
|
||||||
38
go.mod
38
go.mod
@@ -1,37 +1,13 @@
|
|||||||
module git.wntrmute.dev/kyle/mcias
|
module git.wntrmute.dev/kyle/mcias
|
||||||
|
|
||||||
go 1.23.8
|
go 1.24.0
|
||||||
|
|
||||||
require github.com/gokyle/twofactor v1.0.1
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/squirrel v1.5.4 // indirect
|
git.wntrmute.dev/kyle/goutils v1.12.1
|
||||||
github.com/cloudflare/golz4 v0.0.0-20240921210912-c4df3fe31cbd // indirect
|
golang.org/x/crypto v0.44.0
|
||||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
)
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
|
||||||
github.com/golang-migrate/migrate/v4 v4.18.3 // indirect
|
require (
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
|
||||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
|
||||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.28 // indirect
|
|
||||||
github.com/oklog/ulid/v2 v2.1.0 // indirect
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
|
||||||
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
|
||||||
github.com/spf13/afero v1.12.0 // indirect
|
|
||||||
github.com/spf13/cast v1.7.1 // indirect
|
|
||||||
github.com/spf13/cobra v1.9.1 // indirect
|
|
||||||
github.com/spf13/pflag v1.0.6 // indirect
|
|
||||||
github.com/spf13/viper v1.20.1 // indirect
|
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
|
||||||
go.uber.org/atomic v1.11.0 // indirect
|
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
|
||||||
golang.org/x/crypto v0.38.0 // indirect
|
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
|
||||||
golang.org/x/text v0.25.0 // indirect
|
|
||||||
golang.org/x/time v0.12.0 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
rsc.io/qr v0.2.0 // indirect
|
rsc.io/qr v0.2.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
77
go.sum
77
go.sum
@@ -1,73 +1,8 @@
|
|||||||
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
|
git.wntrmute.dev/kyle/goutils v1.12.1 h1:Isho6iaaYW2bi0TU5avFwUOMiYWFqQhm73gROaEKDxU=
|
||||||
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
|
git.wntrmute.dev/kyle/goutils v1.12.1/go.mod h1:PtzS8SdvFz08hUuZ0MKJpogvUApbTTW27PJn1u7sI14=
|
||||||
github.com/cloudflare/golz4 v0.0.0-20240921210912-c4df3fe31cbd h1:r6CfhFeB1zJY9UVvmsDyFxwJIKXLcmps03FlEs460zI=
|
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
||||||
github.com/cloudflare/golz4 v0.0.0-20240921210912-c4df3fe31cbd/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
|
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
|
||||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
|
||||||
github.com/gokyle/twofactor v1.0.1 h1:uRhvx0S4Hb82RPIDALnf7QxbmPL49LyyaCkJDpWx+Ek=
|
|
||||||
github.com/gokyle/twofactor v1.0.1/go.mod h1:4gxzH1eaE/F3Pct/sCDNOylP0ClofUO5j4XZN9tKtLE=
|
|
||||||
github.com/golang-migrate/migrate/v4 v4.18.3 h1:EYGkoOsvgHHfm5U/naS1RP/6PL/Xv3S4B/swMiAmDLs=
|
|
||||||
github.com/golang-migrate/migrate/v4 v4.18.3/go.mod h1:99BKpIi6ruaaXRM1A77eqZ+FWPQ3cfRa+ZVy5bmWMaY=
|
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
|
||||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
|
|
||||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
|
|
||||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
|
|
||||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
|
||||||
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
|
|
||||||
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
|
|
||||||
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
|
||||||
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
|
|
||||||
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
|
|
||||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
|
||||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
|
||||||
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
|
|
||||||
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
|
|
||||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
|
||||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
|
||||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
|
||||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
|
||||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
|
||||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
||||||
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
|
|
||||||
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
|
||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
|
||||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
|
||||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
|
||||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
|
||||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
|
||||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
|
||||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
|
||||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
|
||||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
|
||||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
|
||||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
|
||||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
|
||||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
|
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
|
||||||
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
|
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
|
||||||
|
|||||||
42
schema.sql
42
schema.sql
@@ -1,42 +0,0 @@
|
|||||||
CREATE TABLE users (
|
|
||||||
id text primary key,
|
|
||||||
created integer,
|
|
||||||
user text not null,
|
|
||||||
password blob not null,
|
|
||||||
salt blob not null
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE tokens (
|
|
||||||
id text primary key,
|
|
||||||
uid text not null,
|
|
||||||
token text not null,
|
|
||||||
expires integer default 0,
|
|
||||||
FOREIGN KEY(uid) REFERENCES user(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE database (
|
|
||||||
id text primary key,
|
|
||||||
host text not null,
|
|
||||||
port integer default 5432,
|
|
||||||
name text not null,
|
|
||||||
user text not null,
|
|
||||||
password text not null
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE registrations (
|
|
||||||
id text primary key,
|
|
||||||
code text not null
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE roles (
|
|
||||||
id text primary key,
|
|
||||||
role text not null
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE user_roles (
|
|
||||||
id text primary key,
|
|
||||||
uid text not null,
|
|
||||||
rid text not null,
|
|
||||||
FOREIGN KEY(uid) REFERENCES user(id),
|
|
||||||
FOREIGN KEY(rid) REFERENCES roles(id)
|
|
||||||
);
|
|
||||||
Reference in New Issue
Block a user