feat: included server
This commit is contained in:
parent
907b7a5b34
commit
9b05ca26f3
20 changed files with 733 additions and 235 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +1,4 @@
|
|||
machinelock-*
|
||||
.DS_Store
|
||||
start.sh
|
||||
db.sqlite
|
18
README.md
18
README.md
|
@ -1,11 +1,19 @@
|
|||
# Machine Lock Manager
|
||||
|
||||
Kleines Stück Software um die Machinelocks einfacher zu managen.
|
||||
Server für die Maschinen Locks. Aktuell wird im CTDO die gruppe admin benötigt um zugriff auf die laufende software zu erhalten.
|
||||
|
||||
Nutzung:
|
||||
## Flags
|
||||
|
||||
Entweder als CLI parameter anzugeben, oder aber auch als environment variable, dann in CAPS und mit \_ anstatt -.
|
||||
|
||||
```bash
|
||||
./machinelock-darwin-arm --token="VAULTTOKEN"
|
||||
--machine-token token den die maschinen nutzen um sich an der api zu authentifizieren
|
||||
--port port für den webserver
|
||||
--cookie-secret secret für den cookie store, wenn leer wird einer random generiert bei jedem start
|
||||
--oauth-client-id oauth client id obviously
|
||||
--oauth-client-secret und das secret..
|
||||
--oauth-endpoint der endpunkt vom oauth
|
||||
--base-url und die base url wo das tool erreichbar ist, wird benutzt um den richtigen pfad für die oauth return url zu generieren
|
||||
--db db connect string (with sqlite it will be the path to the sqlite file, with psql it will be the dsn string)
|
||||
--db-type type of db, currently supported is sqlite and psql
|
||||
```
|
||||
|
||||
Anschließend dann auf http://localhost:3000 gehen.
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault-client-go"
|
||||
)
|
||||
|
||||
var Client *vault.Client
|
||||
|
||||
func Init(token string) {
|
||||
var err error
|
||||
Client, err = vault.New(vault.WithAddress("https://vault.ctdo.de"), vault.WithRequestTimeout(30*time.Second))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := Client.SetToken(token); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
47
config/main.go
Normal file
47
config/main.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var (
|
||||
Port int
|
||||
DbType string
|
||||
Db string
|
||||
CookieSecret string
|
||||
MachineToken string
|
||||
OauthEndpoint string
|
||||
OauthClientID string
|
||||
OauthClientSecret string
|
||||
BaseUrl string
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.String("machine-token", "", "Token with wich machines will authenticate")
|
||||
flag.Int("port", 3000, "Port to listen on")
|
||||
flag.String("db-type", "sqlite", "Database type: either sqlite or psql")
|
||||
flag.String("db", "db.sqlite", "Connect string for db")
|
||||
flag.String("cookie-secret", "", "Secret for the cookie store, leave empty for random secret")
|
||||
flag.String("oauth-client-id", "", "Oauth2 Client ID")
|
||||
flag.String("oauth-client-secret", "", "Oauth2 Client Secret")
|
||||
flag.String("oauth-endpoint", "", "Authentik Endpoint URL")
|
||||
flag.String("base-url", "http://localhost:3000", "Base URL where this will be hosted")
|
||||
_ = viper.BindPFlags(flag.CommandLine)
|
||||
|
||||
flag.Parse()
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
viper.AutomaticEnv()
|
||||
|
||||
Port = viper.GetInt("port")
|
||||
CookieSecret = viper.GetString("cookie-secret")
|
||||
MachineToken = viper.GetString("machine-token")
|
||||
OauthClientID = viper.GetString("oauth-client-id")
|
||||
OauthClientSecret = viper.GetString("oauth-client-secret")
|
||||
OauthEndpoint = viper.GetString("oauth-endpoint")
|
||||
BaseUrl = viper.GetString("base-url")
|
||||
Db = viper.GetString("db")
|
||||
DbType = viper.GetString("db-type")
|
||||
}
|
61
cookies/main.go
Normal file
61
cookies/main.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package cookies
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"git.ctdo.de/ctdo/machinelock-manager/config"
|
||||
"git.ctdo.de/ctdo/machinelock-manager/templates"
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-contrib/sessions/cookie"
|
||||
"github.com/gin-gonic/gin"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
func Init(r *gin.Engine) {
|
||||
var secret []byte
|
||||
if config.CookieSecret != "" {
|
||||
secret = []byte(config.CookieSecret)
|
||||
} else {
|
||||
secret = uuid.NewV4().Bytes()
|
||||
}
|
||||
store := cookie.NewStore(secret)
|
||||
session := sessions.Sessions("machinelock", store)
|
||||
r.Use(session)
|
||||
}
|
||||
|
||||
func SetAuth(c *gin.Context, status bool) {
|
||||
session := sessions.Default(c)
|
||||
if status {
|
||||
session.Set("auth", "ok") // logged in and in correct group to have access for this
|
||||
} else {
|
||||
session.Set("auth", "nok") // logged in but not in correct group to access this aka forbidden
|
||||
}
|
||||
session.Options(sessions.Options{
|
||||
MaxAge: 3600 * 24 * 7, // 7 tage
|
||||
Path: "/",
|
||||
})
|
||||
session.Save()
|
||||
}
|
||||
|
||||
func Logout(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
session.Clear()
|
||||
}
|
||||
|
||||
func CheckAuth(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
if session.Get("auth") == nil {
|
||||
c.Redirect(http.StatusFound, "/auth") // redirect to login
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if session.Get("auth") == "nok" {
|
||||
templates.Templates.ExecuteTemplate(c.Writer, "forbidden", gin.H{})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if session.Get("auth") != "ok" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
}
|
20
db/base.go
Normal file
20
db/base.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
uuid "github.com/satori/go.uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Base struct {
|
||||
ID *uuid.UUID `gorm:"type:uuid;primary_key" json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (b *Base) BeforeCreate(_ *gorm.DB) error {
|
||||
id := uuid.NewV4()
|
||||
b.ID = &id
|
||||
return nil
|
||||
}
|
19
db/machine.go
Normal file
19
db/machine.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
uuid "github.com/satori/go.uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Machine struct {
|
||||
Base
|
||||
Name string `json:"name" form:"name"`
|
||||
Slug string `json:"slug" form:"slug"`
|
||||
Tokens []*Token `json:"tokens" gorm:"many2many:token_machines;"`
|
||||
}
|
||||
|
||||
func (m *Machine) BeforeCreate(_ *gorm.DB) error {
|
||||
id := uuid.NewV4()
|
||||
m.ID = &id
|
||||
return nil
|
||||
}
|
42
db/main.go
Normal file
42
db/main.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"git.ctdo.de/ctdo/machinelock-manager/config"
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
var DB *gorm.DB
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
switch config.DbType {
|
||||
case "sqlite":
|
||||
DB, err = gorm.Open(sqlite.Open(config.Db), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
})
|
||||
case "psql":
|
||||
DB, err = gorm.Open(postgres.Open(config.Db), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
slog.Error("failed to connect database", "error", err.Error())
|
||||
panic(0)
|
||||
}
|
||||
migrate()
|
||||
}
|
||||
func migrate() {
|
||||
err := DB.AutoMigrate(
|
||||
&Token{},
|
||||
&Machine{},
|
||||
)
|
||||
if err != nil {
|
||||
slog.Error(err.Error())
|
||||
panic(0)
|
||||
}
|
||||
}
|
7
db/token.go
Normal file
7
db/token.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package db
|
||||
|
||||
type Token struct {
|
||||
ID string `gorm:"primary_key" json:"id" form:"id"`
|
||||
Nick string `json:"nick" form:"nick"`
|
||||
Machines []*Machine `json:"machines" gorm:"many2many:token_machines;"`
|
||||
}
|
46
go.mod
46
go.mod
|
@ -1,22 +1,38 @@
|
|||
module example.com/henne
|
||||
module git.ctdo.de/ctdo/machinelock-manager
|
||||
|
||||
go 1.23.3
|
||||
|
||||
require github.com/hashicorp/vault-client-go v0.4.3
|
||||
require (
|
||||
github.com/coreos/go-oidc/v3 v3.11.0
|
||||
golang.org/x/oauth2 v0.21.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/google/uuid v1.4.0 // indirect
|
||||
github.com/gorilla/context v1.1.2 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/gorilla/sessions v1.2.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.5 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
|
@ -26,6 +42,7 @@ require (
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
|
@ -37,25 +54,28 @@ require (
|
|||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/sqlite v1.23.1 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/sessions v1.0.1
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.19.0
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
gorm.io/driver/postgres v1.5.11
|
||||
gorm.io/gorm v1.25.12
|
||||
)
|
||||
|
|
119
go.sum
119
go.sum
|
@ -6,17 +6,34 @@ github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/
|
|||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI=
|
||||
github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/sessions v1.0.1 h1:3hsJyNs7v7N8OtelFmYXFrulAf6zSR7nW/putcPEHxI=
|
||||
github.com/gin-contrib/sessions v1.0.1/go.mod h1:ouxSFM24/OgIud5MJYQJLpy6AwxQ5EYO9yLhbtObGkM=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
|
||||
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
|
@ -25,36 +42,51 @@ github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBEx
|
|||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
|
||||
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
|
||||
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/vault-client-go v0.4.3 h1:zG7STGVgn/VK6rnZc0k8PGbfv2x/sJExRKHSUg3ljWc=
|
||||
github.com/hashicorp/vault-client-go v0.4.3/go.mod h1:4tDw7Uhq5XOxS1fO+oMtotHL7j4sB9cp0T7U6m4FzDY=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
@ -64,14 +96,20 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
|||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
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.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
|
@ -86,14 +124,13 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
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/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
|
@ -108,31 +145,43 @@ go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTV
|
|||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y=
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314=
|
||||
gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
|
273
main.go
273
main.go
|
@ -1,84 +1,223 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"strconv"
|
||||
|
||||
"example.com/henne/client"
|
||||
"example.com/henne/templates"
|
||||
"git.ctdo.de/ctdo/machinelock-manager/config"
|
||||
"git.ctdo.de/ctdo/machinelock-manager/cookies"
|
||||
"git.ctdo.de/ctdo/machinelock-manager/db"
|
||||
"git.ctdo.de/ctdo/machinelock-manager/oidc"
|
||||
"git.ctdo.de/ctdo/machinelock-manager/templates"
|
||||
"github.com/gin-gonic/gin"
|
||||
flag "github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.String("token", "", "Vault Token to use")
|
||||
_ = viper.BindPFlags(flag.CommandLine)
|
||||
|
||||
flag.Parse()
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
viper.AutomaticEnv()
|
||||
client.Init(viper.GetString("token"))
|
||||
router := gin.New()
|
||||
router.GET("/", index)
|
||||
router.GET("/bootstrap.min.css", bs)
|
||||
router.POST("/", save)
|
||||
router.Run("[::]:3000")
|
||||
cookies.Init(router)
|
||||
authGrp := router.Group("/")
|
||||
authGrp.Use(cookies.CheckAuth)
|
||||
{
|
||||
authGrp.GET("/", index) // overview machine / token connection
|
||||
authGrp.GET("/machines", machines) // machine listing
|
||||
authGrp.POST("/tokens", createToken) // create a new rfid token
|
||||
authGrp.POST("/machines", createMachine) // create a new machine
|
||||
authGrp.POST("/tokens/:id", editToken) // edit a token (only nick and allowed machines)
|
||||
authGrp.POST("/machines/:id", editMachine) // edit a machine
|
||||
authGrp.GET("/machines/:id/delete", deleteMachine) // deletes a machine
|
||||
authGrp.GET("/tokens/:id/delete", deleteToken) // deletes a token
|
||||
|
||||
}
|
||||
router.GET("/bootstrap.min.css", bs) // static bootstrap file serving
|
||||
router.GET("/auth", oidc.HandleRedirect) // oidc redirect handler
|
||||
router.GET("/auth/success", oidc.HandleOauthCallback) // oauth flow callback
|
||||
router.GET("/machine/:slug", GetMachine) // machine api to get allowed tokens
|
||||
router.GET("/logout", logout)
|
||||
router.GET("/forbidden", forbidden)
|
||||
router.Run("[::]:" + strconv.Itoa(config.Port))
|
||||
}
|
||||
|
||||
func getUsers() (Users, error) {
|
||||
users := map[string]User{}
|
||||
ctx := context.Background()
|
||||
l, err := client.Client.List(ctx, "/maschinenlock")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if keys, ok := l.Data["keys"]; ok {
|
||||
if list, ok := keys.([]interface{}); ok {
|
||||
for _, s := range list {
|
||||
u := User{}
|
||||
u.Get(s.(string))
|
||||
users[s.(string)] = u
|
||||
}
|
||||
}
|
||||
}
|
||||
return users, err
|
||||
}
|
||||
func bs(ctx *gin.Context) {
|
||||
ctx.FileFromFS("templates/bootstrap.min.css", http.FS(templates.Res))
|
||||
func forbidden(c *gin.Context) {
|
||||
templates.Templates.ExecuteTemplate(c.Writer, "forbidden", gin.H{})
|
||||
}
|
||||
|
||||
func index(ctx *gin.Context) {
|
||||
users, err := getUsers()
|
||||
if err != nil {
|
||||
log.Printf("%v", err)
|
||||
ctx.String(http.StatusInternalServerError, "text", "Internal Error")
|
||||
return
|
||||
}
|
||||
templates.Templates.ExecuteTemplate(ctx.Writer, "index", users)
|
||||
func logout(c *gin.Context) {
|
||||
cookies.Logout(c)
|
||||
c.Redirect(http.StatusFound, "/")
|
||||
}
|
||||
|
||||
func save(ctx *gin.Context) {
|
||||
var form User
|
||||
if err := ctx.ShouldBind(&form); err != nil {
|
||||
log.Print(err)
|
||||
ctx.String(http.StatusInternalServerError, "could not bind data")
|
||||
return
|
||||
}
|
||||
err := form.Save(form.ID)
|
||||
if err != nil {
|
||||
log.Printf("%v", err)
|
||||
ctx.String(http.StatusInternalServerError, "text", "Internal Error")
|
||||
return
|
||||
}
|
||||
|
||||
users, err := getUsers()
|
||||
if err != nil {
|
||||
log.Printf("%v", err)
|
||||
ctx.String(http.StatusInternalServerError, "text", "Internal Error")
|
||||
return
|
||||
}
|
||||
templates.Templates.ExecuteTemplate(ctx.Writer, "index", users)
|
||||
func bs(c *gin.Context) {
|
||||
c.FileFromFS("templates/bootstrap.min.css", http.FS(templates.Res))
|
||||
}
|
||||
|
||||
func returnInternalError(c *gin.Context, err error) {
|
||||
slog.Error("an error occured", "error", err)
|
||||
c.String(http.StatusInternalServerError, "text", "Internal Error")
|
||||
}
|
||||
|
||||
func index(c *gin.Context) {
|
||||
var tokens []db.Token
|
||||
if err := db.DB.Preload("Machines").Find(&tokens).Error; err != nil {
|
||||
returnInternalError(c, err)
|
||||
return
|
||||
}
|
||||
var machines []db.Machine
|
||||
if err := db.DB.Find(&machines).Error; err != nil {
|
||||
returnInternalError(c, err)
|
||||
return
|
||||
}
|
||||
templates.Templates.ExecuteTemplate(c.Writer, "index", gin.H{
|
||||
"Tokens": tokens,
|
||||
"Machines": machines,
|
||||
})
|
||||
}
|
||||
|
||||
func machines(c *gin.Context) {
|
||||
var machines []db.Machine
|
||||
if err := db.DB.Find(&machines).Error; err != nil {
|
||||
returnInternalError(c, err)
|
||||
return
|
||||
}
|
||||
templates.Templates.ExecuteTemplate(c.Writer, "machines", machines)
|
||||
}
|
||||
func createMachine(c *gin.Context) {
|
||||
var form db.Machine
|
||||
if err := c.ShouldBind(&form); err != nil {
|
||||
returnInternalError(c, err)
|
||||
return
|
||||
}
|
||||
machine := db.Machine{
|
||||
Name: form.Name,
|
||||
Slug: form.Slug,
|
||||
}
|
||||
if err := db.DB.Create(&machine).Error; err != nil {
|
||||
returnInternalError(c, err)
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusFound, "/machines")
|
||||
}
|
||||
func editMachine(c *gin.Context) {
|
||||
var machine db.Machine
|
||||
if err := db.DB.Where("id = ?", c.Param("id")).First(&machine).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
c.String(http.StatusNotFound, "gibts nich")
|
||||
return
|
||||
}
|
||||
returnInternalError(c, err)
|
||||
return
|
||||
}
|
||||
var form db.Machine
|
||||
if err := c.ShouldBind(&form); err != nil {
|
||||
returnInternalError(c, err)
|
||||
return
|
||||
}
|
||||
machine.Name = form.Name
|
||||
machine.Slug = form.Slug
|
||||
db.DB.Save(&machine)
|
||||
c.Redirect(http.StatusFound, "/machines")
|
||||
}
|
||||
func deleteMachine(c *gin.Context) {
|
||||
if err := db.DB.Where("id = ?", c.Param("id")).Delete(db.Machine{}).Error; err != nil {
|
||||
returnInternalError(c, err)
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusFound, "/machines")
|
||||
}
|
||||
func deleteToken(c *gin.Context) {
|
||||
if err := db.DB.Where("id = ?", c.Param("id")).Delete(db.Token{}).Error; err != nil {
|
||||
returnInternalError(c, err)
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusFound, "/")
|
||||
}
|
||||
|
||||
func createToken(c *gin.Context) {
|
||||
var form db.Token
|
||||
if err := c.ShouldBind(&form); err != nil {
|
||||
returnInternalError(c, err)
|
||||
return
|
||||
}
|
||||
token := db.Token{
|
||||
Nick: form.Nick,
|
||||
ID: form.ID,
|
||||
}
|
||||
if err := db.DB.Create(&token).Error; err != nil {
|
||||
returnInternalError(c, err)
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusFound, "/")
|
||||
}
|
||||
|
||||
func editToken(c *gin.Context) {
|
||||
var token db.Token
|
||||
if err := db.DB.Where("id = ?", c.Param("id")).First(&token).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
c.String(http.StatusNotFound, "gibts nich")
|
||||
return
|
||||
}
|
||||
returnInternalError(c, err)
|
||||
return
|
||||
}
|
||||
var form struct {
|
||||
Nick string `form:"nick"`
|
||||
Machine map[string]string `form:"machine"`
|
||||
}
|
||||
if err := c.ShouldBind(&form); err != nil {
|
||||
returnInternalError(c, err)
|
||||
return
|
||||
}
|
||||
form.Machine = c.PostFormMap("machine")
|
||||
db.DB.Model(&token).Association("Machines").Clear()
|
||||
token.Nick = form.Nick
|
||||
token.Machines = make([]*db.Machine, 0)
|
||||
for id := range form.Machine {
|
||||
var m db.Machine
|
||||
if err := db.DB.Where("id = ?", id).First(&m).Error; err == nil {
|
||||
token.Machines = append(token.Machines, &m)
|
||||
} else {
|
||||
slog.Error(err.Error())
|
||||
}
|
||||
}
|
||||
if err := db.DB.Omit("Machines.*").Save(&token).Error; err != nil {
|
||||
slog.Error(err.Error())
|
||||
}
|
||||
c.Redirect(http.StatusFound, "/")
|
||||
}
|
||||
|
||||
func GetMachine(c *gin.Context) {
|
||||
if config.MachineToken == "" {
|
||||
c.JSON(http.StatusServiceUnavailable, gin.H{
|
||||
"error": "no machine auth token is set, skipping response for security reasons",
|
||||
})
|
||||
return
|
||||
}
|
||||
if c.Request.Header.Get("Authorization") == fmt.Sprintf("Bearer %s", config.MachineToken) {
|
||||
var machine db.Machine
|
||||
if err := db.DB.Where("slug = ?", c.Param("slug")).Preload("Tokens").First(&machine).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"error": "not found",
|
||||
})
|
||||
return
|
||||
}
|
||||
slog.Error(err.Error())
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "internal error",
|
||||
})
|
||||
return
|
||||
}
|
||||
allowedTokens := make([]string, 0)
|
||||
for _, t := range machine.Tokens {
|
||||
allowedTokens = append(allowedTokens, t.ID)
|
||||
}
|
||||
c.JSON(http.StatusOK, allowedTokens)
|
||||
} else {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"error": "unauthorized",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
83
oidc/main.go
Normal file
83
oidc/main.go
Normal file
|
@ -0,0 +1,83 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"slices"
|
||||
|
||||
"git.ctdo.de/ctdo/machinelock-manager/config"
|
||||
"git.ctdo.de/ctdo/machinelock-manager/cookies"
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var provider *oidc.Provider
|
||||
var oauth2Config oauth2.Config
|
||||
|
||||
func init() {
|
||||
ctx := context.Background()
|
||||
var err error
|
||||
provider, err = oidc.NewProvider(ctx, config.OauthEndpoint)
|
||||
if err != nil {
|
||||
slog.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
oauth2Config = oauth2.Config{
|
||||
ClientID: config.OauthClientID,
|
||||
ClientSecret: config.OauthClientSecret,
|
||||
RedirectURL: config.BaseUrl + "/auth/success",
|
||||
Endpoint: provider.Endpoint(),
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func HandleRedirect(ctx *gin.Context) {
|
||||
ctx.Redirect(http.StatusFound, oauth2Config.AuthCodeURL("machinelock"))
|
||||
}
|
||||
|
||||
func HandleOauthCallback(ctx *gin.Context) {
|
||||
verifier := provider.Verifier(&oidc.Config{ClientID: oauth2Config.ClientID})
|
||||
|
||||
oauth2Token, err := oauth2Config.Exchange(ctx, ctx.Query("code"))
|
||||
if err != nil {
|
||||
slog.Error(err.Error())
|
||||
ctx.String(http.StatusInternalServerError, "internal error check logs")
|
||||
return
|
||||
}
|
||||
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
|
||||
if !ok {
|
||||
ctx.String(http.StatusInternalServerError, "internal error check logs")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse and verify ID Token payload.
|
||||
idToken, err := verifier.Verify(ctx, rawIDToken)
|
||||
if err != nil {
|
||||
slog.Error(err.Error())
|
||||
ctx.String(http.StatusInternalServerError, "internal error check logs")
|
||||
return
|
||||
}
|
||||
// Extract custom claims
|
||||
var claims struct {
|
||||
Groups []string `json:"groups"`
|
||||
}
|
||||
if err := idToken.Claims(&claims); err != nil {
|
||||
slog.Error(err.Error())
|
||||
ctx.String(http.StatusInternalServerError, "internal error check logs")
|
||||
return
|
||||
}
|
||||
|
||||
if slices.Contains(claims.Groups, "admin") {
|
||||
cookies.SetAuth(ctx, true)
|
||||
slog.Info("auth success")
|
||||
ctx.Redirect(http.StatusFound, "/")
|
||||
} else {
|
||||
cookies.SetAuth(ctx, false)
|
||||
slog.Info("auth failure")
|
||||
ctx.Redirect(http.StatusFound, "/forbidden")
|
||||
}
|
||||
}
|
|
@ -4,6 +4,9 @@ import (
|
|||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.ctdo.de/ctdo/machinelock-manager/db"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
func For(start, end int) <-chan int {
|
||||
|
@ -45,5 +48,16 @@ func Dict(values ...interface{}) (map[string]interface{}, error) {
|
|||
}
|
||||
return dict, nil
|
||||
}
|
||||
func IsAllowed(token db.Token, id *uuid.UUID) bool {
|
||||
if id == nil {
|
||||
return false
|
||||
}
|
||||
for _, m := range token.Machines {
|
||||
if m.ID != nil && uuid.Equal(*m.ID, *id) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var FunctionMap = map[string]interface{}{"For": For, "Price": Price, "Now": Now, "Add": Add, "Dict": Dict}
|
||||
var FunctionMap = map[string]interface{}{"For": For, "Price": Price, "Now": Now, "Add": Add, "Dict": Dict, "IsAllowed": IsAllowed}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"strings"
|
||||
"text/template"
|
||||
|
||||
"example.com/henne/template_functions"
|
||||
"git.ctdo.de/ctdo/machinelock-manager/template_functions"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
13
templates/templates/forbidden.gohtml
Normal file
13
templates/templates/forbidden.gohtml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Machine Lock Permissions</title>
|
||||
<link href="/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
</head>
|
||||
<body>
|
||||
<p>Du hast keine Berechtigung diese Seiten zu sehen. Bist du in der "admin" Gruppe?</p>
|
||||
<a href="/logout">Logout</a>
|
||||
</body>
|
||||
</html>
|
16
templates/templates/header.gohtml
Normal file
16
templates/templates/header.gohtml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<a class="navbar-brand" href="#">Machine Locks</a>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/">Start</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/machines">Maschinen</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/logout">Logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
|
@ -7,6 +7,7 @@
|
|||
<link href="/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
</head>
|
||||
<body>
|
||||
{{template "header"}}
|
||||
<div class="container">
|
||||
<h1 class="mt-4 mb-3">Machine Lock Permissions</h1>
|
||||
<table class="table table-striped align-middle">
|
||||
|
@ -15,35 +16,41 @@
|
|||
|
||||
<th>User</th>
|
||||
<th style="min-width:120px">RFID ID</th>
|
||||
<th>Bandsäge</th>
|
||||
<th>Lasercutter</th>
|
||||
<th>3D Drucker</th>
|
||||
<th>Shapeoko</th>
|
||||
<th>Bohrmaschine</th>
|
||||
<th>Drehbank</th>
|
||||
<th>Folienplotter</th>
|
||||
{{range .Machines}}
|
||||
<th style="writing-mode:vertical-rl">{{.Name}}</th>
|
||||
{{end}}
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $key, $element := .}}
|
||||
<form action="/" method="post">
|
||||
{{range .Tokens}}
|
||||
{{$token := .}}
|
||||
<form action="/tokens/{{.ID}}" method="post">
|
||||
<tr>
|
||||
<td><input type="text" class="form-control" value="{{$element.User}}" name="user"></td>
|
||||
<td>{{$key}}</td>
|
||||
<td><input type="checkbox" class="form-check-input" name="bandsaege" value="1" {{if eq $element.Bandsaege "1"}}checked{{end}}/></td>
|
||||
<td><input type="checkbox" class="form-check-input" name="lasercutter" value="1" {{if eq $element.Lasercutter "1" }}checked{{end}} /></td>
|
||||
<td><input type="checkbox" class="form-check-input" name="dreiddrucker" value="1" {{if eq $element.Dreiddrucker "1" }}checked{{end}} /></td>
|
||||
<td><input type="checkbox" class="form-check-input" name="shapeoko" value="1" {{if eq $element.Shapeoko "1" }}checked{{end}} /></td>
|
||||
<td><input type="checkbox" class="form-check-input" name="bohrmaschine" value="1" {{if eq $element.Bohrmaschine "1" }}checked{{end}} /></td>
|
||||
<td><input type="checkbox" class="form-check-input" name="drehbank" value="1" {{if eq $element.Drehbank "1" }}checked{{end}} /></td>
|
||||
<td><input type="checkbox" class="form-check-input" name="folienplotter" value="1" {{if eq $element.Folienplotter "1" }}checked{{end}} /></td>
|
||||
<td><input type="hidden" name="id" value="{{$key}}"/> <input type="submit" class="btn btn-sm btn-success" value="Speichern" /></td>
|
||||
<td><input type="text" class="form-control" value="{{.Nick}}" name="nick"></td>
|
||||
<td>{{.ID}}</td>
|
||||
{{range $.Machines}}
|
||||
<td><input type="checkbox" class="form-check-input" name="machine[{{.ID}}]" value="1" {{if IsAllowed $token .ID}}checked{{end}}/></td>
|
||||
{{end}}
|
||||
<td><input type="submit" class="btn btn-sm btn-success" value="Speichern" /><a href="/tokens/{{.ID}}/delete" class="btn btn-sm btn-danger">Löschen</a></td>
|
||||
</tr>
|
||||
</form>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
<h2>Neuer Token</h2>
|
||||
<form action="/tokens" method="post">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Nick</label>
|
||||
<input type="text" class="form-control" name="nick" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">RFID ID</label>
|
||||
<input type="text" class="form-control" name="id" pattern="^[A-Z0-9]{2}-[A-Z0-9]{2}-[A-Z0-9]{2}-[A-Z0-9]{2}$">
|
||||
<div class="form-text">Nur A-Z 0-9 und - zugelassen</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
48
templates/templates/machines.gohtml
Normal file
48
templates/templates/machines.gohtml
Normal file
|
@ -0,0 +1,48 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Machine Lock Permissions</title>
|
||||
<link href="/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
</head>
|
||||
<body>
|
||||
{{template "header"}}
|
||||
<div class="container">
|
||||
<h1 class="mt-4 mb-3">Maschinen</h1>
|
||||
<table class="table table-striped align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Slug</th>
|
||||
<th style="width:180px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .}}
|
||||
<form action="/machines/{{.ID}}" method="post">
|
||||
<tr>
|
||||
<td><input type="text" class="form-control" value="{{.Name}}" name="name"></td>
|
||||
<td><input type="text" class="form-control" value="{{.Slug}}" name="slug"></td>
|
||||
<td><input type="hidden" name="id" value="{{.ID}}"/> <input type="submit" class="btn btn-sm btn-primary" value="Speichern" /><a href="/machines/{{.ID}}/delete" class="btn btn-sm btn-danger">Löschen</a></td>
|
||||
</tr>
|
||||
</form>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
<h2>Neue Maschine</h2>
|
||||
<form action="/machines" method="post">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Name</label>
|
||||
<input type="text" class="form-control" name="name">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Slug</label>
|
||||
<input type="text" class="form-control" name="slug" pattern="[a-z0-9\-]*">
|
||||
<div class="form-text">Nur a-z 0-9 und - zugelassen</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
76
type.go
76
type.go
|
@ -1,76 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"example.com/henne/client"
|
||||
)
|
||||
|
||||
type Users = map[string]User
|
||||
|
||||
type User struct {
|
||||
ID string `json:"id" form:"id"`
|
||||
User string `json:"user" form:"user"`
|
||||
Bandsaege string `json:"mlock-bandsaege" form:"bandsaege"`
|
||||
Bohrmaschine string `json:"mlock-bohrmaschine" form:"bohrmaschine"`
|
||||
Drehbank string `json:"mlock-drehbank" form:"drehbank"`
|
||||
Dreiddrucker string `json:"mlock-dreiddrucker" form:"dreiddrucker"`
|
||||
Folienplotter string `json:"mlock-folienplotter" form:"folienplotter"`
|
||||
Lasercutter string `json:"mlock-lasercutter" form:"lasercutter"`
|
||||
Shapeoko string `json:"mlock-shapeoko" form:"shapeoko"`
|
||||
}
|
||||
|
||||
func (u *User) Get(id string) {
|
||||
ctx := context.Background()
|
||||
h, err := client.Client.Read(ctx, "/maschinenlock/"+id)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for i, d := range h.Data {
|
||||
data := d.(string)
|
||||
switch i {
|
||||
case "user":
|
||||
u.User = data
|
||||
case "mlock-bandsaege":
|
||||
u.Bandsaege = parseBool(data)
|
||||
case "mlock-bohrmaschine":
|
||||
u.Bohrmaschine = parseBool(data)
|
||||
case "mlock-drehbank":
|
||||
u.Drehbank = parseBool(data)
|
||||
case "mlock-dreiddrucker":
|
||||
u.Dreiddrucker = parseBool(data)
|
||||
case "mlock-folienplotter":
|
||||
u.Folienplotter = parseBool(data)
|
||||
case "mlock-lasercutter":
|
||||
u.Lasercutter = parseBool(data)
|
||||
case "mlock-shapeoko":
|
||||
u.Shapeoko = parseBool(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseBool(data string) string {
|
||||
if data == "1" {
|
||||
return data
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (u *User) Save(id string) error {
|
||||
ctx := context.Background()
|
||||
_, err := client.Client.Write(ctx, "/maschinenlock/"+id, map[string]interface{}{
|
||||
"user": u.User,
|
||||
"mlock-bandsaege": u.Bandsaege,
|
||||
"mlock-bohrmaschine": u.Bohrmaschine,
|
||||
"mlock-drehbank": u.Drehbank,
|
||||
"mlock-dreiddrucker": u.Dreiddrucker,
|
||||
"mlock-folienplotter": u.Folienplotter,
|
||||
"mlock-lasercutter": u.Lasercutter,
|
||||
"mlock-shapeoko": u.Shapeoko,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in a new issue