So many updates I can't keep up
This commit is contained in:
parent
739fea417e
commit
2a75a2089b
|
@ -1,16 +1,16 @@
|
|||
#!/bin/bash
|
||||
url='http://localhost:7073/auth/mfa-disable'
|
||||
url='https://ap.summer.test:8443/v1/marigold/auth/mfa-disable'
|
||||
accjson='Accept: application/json'
|
||||
ctjson='Content-Type: application/json'
|
||||
bear='Authorization: Bearer'
|
||||
a=$(curl -sS -X POST "$url" -H "$accjson")
|
||||
a=$(curl -ksS -X POST "$url" -H "$accjson")
|
||||
access=$(cat "acc.json" | jq '.access_token' -r)
|
||||
refresh=$(cat "acc.json" | jq '.refresh_token' -r)
|
||||
|
||||
read -p "MFA: " mfa
|
||||
echo
|
||||
|
||||
a=$(echo "{\"mfa\":\"$mfa\"}" | curl -vsS -X POST "$url" -H "$accjson" -H "$ctjson" -H "$bear $access" --data @-)
|
||||
a=$(echo "{\"mfa\":\"$mfa\"}" | curl -kvsS -X POST "$url" -H "$accjson" -H "$ctjson" -H "$bear $access" --data @-)
|
||||
unset -v mfa
|
||||
|
||||
echo "Output: $a"
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
#!/bin/bash
|
||||
url='http://localhost:7073/auth/mfa'
|
||||
url='https://api.summer.test:8443/v1/marigold/auth/mfa'
|
||||
accjson='Accept: application/json'
|
||||
ctjson='Content-Type: application/json'
|
||||
bear='Authorization: Bearer'
|
||||
a=$(curl -sS -X POST "$url" -H "$accjson")
|
||||
a=$(curl -ksS -X POST "$url" -H "$accjson")
|
||||
access=$(cat "acc.json" | jq '.access_token' -r)
|
||||
refresh=$(cat "acc.json" | jq '.refresh_token' -r)
|
||||
|
||||
read -p "Digits: " digits
|
||||
echo
|
||||
|
||||
a=$(jo digits=$digits | curl -sS -X POST "$url" -H "$accjson" -H "$ctjson" -H "$bear $access" --data @-)
|
||||
a=$(jo digits=$digits | curl -ksS -X POST "$url" -H "$accjson" -H "$ctjson" -H "$bear $access" --data @-)
|
||||
unset -v digits
|
||||
qr=$(echo "$a" | jq '.qr' -r)
|
||||
token=$(echo "$a" | jq '.token' -r)
|
||||
|
@ -21,7 +21,7 @@ echo "Saved 'qr.html' with an embedded image"
|
|||
read -p "MFA: " mfa
|
||||
echo
|
||||
|
||||
a=$(echo "{\"mfa\":\"$mfa\"}" | curl -vsS -X POST "$url" -H "$accjson" -H "$ctjson" -H "$bear $token" --data @-)
|
||||
a=$(echo "{\"mfa\":\"$mfa\"}" | curl -kvsS -X POST "$url" -H "$accjson" -H "$ctjson" -H "$bear $token" --data @-)
|
||||
unset -v mfa
|
||||
|
||||
echo "Output: $a"
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#!/bin/bash
|
||||
url='http://localhost:7073/auth/login'
|
||||
url='https://api.summer.test:8443/v1/marigold/auth/login'
|
||||
accjson='Accept: application/json'
|
||||
ctjson='Content-Type: application/json'
|
||||
bear='Authorization: Bearer'
|
||||
a=$(curl -sS -X POST "$url" -H "$accjson")
|
||||
a=$(curl -ksS -X POST "$url" -H "$accjson")
|
||||
next=$(echo "$a" | jq '.next' -r)
|
||||
token=$(echo "$a" | jq '.token' -r)
|
||||
|
||||
|
@ -13,7 +13,7 @@ if [[ "$next" = "login" ]]; then
|
|||
echo
|
||||
|
||||
unset -v a
|
||||
a=$(jo email="$em" password="$pw" | curl -sS -X POST "$url" -H "$accjson" -H "$ctjson" -H "$bear $token" --data @-)
|
||||
a=$(jo email="$em" password="$pw" | curl -ksS -X POST "$url" -H "$accjson" -H "$ctjson" -H "$bear $token" --data @-)
|
||||
unset -v pw
|
||||
next=$(echo "$a" | jq '.next' -r)
|
||||
token=$(echo "$a" | jq '.token' -r)
|
||||
|
@ -23,7 +23,7 @@ if [[ "$next" = "login" ]]; then
|
|||
echo
|
||||
|
||||
unset -v a
|
||||
a=$(echo "{\"code\":\"$ecode\"}" | curl -sS -X POST "$url" -H "$accjson" -H "$ctjson" -H "$bear $token" --data @-)
|
||||
a=$(echo "{\"code\":\"$ecode\"}" | curl -ksS -X POST "$url" -H "$accjson" -H "$ctjson" -H "$bear $token" --data @-)
|
||||
unset -v ecode
|
||||
next=$(echo "$a" | jq '.next' -r)
|
||||
token=$(echo "$a" | jq '.token' -r)
|
||||
|
@ -34,7 +34,7 @@ if [[ "$next" = "login" ]]; then
|
|||
echo
|
||||
|
||||
unset -v a
|
||||
a=$(echo "{\"mfa\":\"$mfa\"}" | curl -sS -X POST "$url" -H "$accjson" -H "$ctjson" -H "$bear $token" --data @-)
|
||||
a=$(echo "{\"mfa\":\"$mfa\"}" | curl -ksS -X POST "$url" -H "$accjson" -H "$ctjson" -H "$bear $token" --data @-)
|
||||
unset -v mfa
|
||||
next=$(echo "$a" | jq '.next' -r)
|
||||
token=$(echo "$a" | jq '.token' -r)
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"code.mrmelon54.com/melon/summer/cmd/azalea/servers"
|
||||
"code.mrmelon54.com/melon/summer/pkg/api"
|
||||
)
|
||||
|
||||
type AzaleaConfig struct {
|
||||
Database string `yaml:"database"`
|
||||
Listen ListenConfig `yaml:"listen"`
|
||||
Auth api.AuthConfig `yaml:"apiAuth"`
|
||||
ApiDomain string `yaml:"apiDomain"`
|
||||
Database string `yaml:"database"`
|
||||
Listen ListenConfig `yaml:"listen"`
|
||||
Auth api.AuthConfig[servers.PermBaseConfig] `yaml:"apiAuth"`
|
||||
ApiDomain string `yaml:"apiDomain"`
|
||||
}
|
||||
|
||||
type ListenConfig struct {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package quick_cert
|
||||
|
||||
import (
|
||||
"code.mrmelon54.com/melon/summer/pkg/tables/certificate"
|
||||
"code.mrmelon54.com/melon/summer/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"xorm.io/xorm"
|
||||
|
@ -10,4 +12,38 @@ func TestQuickCertDebug(t *testing.T) {
|
|||
db, err := xorm.NewEngine("sqlite3", ":memory")
|
||||
assert.NoError(t, err)
|
||||
QuickCert(db)
|
||||
|
||||
var c []certificate.Certificate
|
||||
var c2 []certificate.CertificateData
|
||||
var c3 []certificate.CertificateDomain
|
||||
assert.NoError(t, db.Find(&c))
|
||||
assert.NoError(t, db.Find(&c2))
|
||||
assert.NoError(t, db.Find(&c3))
|
||||
|
||||
assert.Equal(t, 1, len(c))
|
||||
assert.Equal(t, 1, len(c2))
|
||||
assert.Equal(t, 2, len(c3))
|
||||
|
||||
c[0].Id = 0
|
||||
assert.Equal(t, certificate.Certificate{
|
||||
Owner: 1,
|
||||
LetsEncrypt: 1,
|
||||
Namesilo: 1,
|
||||
AutoRenew: utils.PBool(false),
|
||||
Active: utils.PBool(true),
|
||||
Renewing: utils.PBool(false),
|
||||
RenewFailed: utils.PBool(false),
|
||||
}, c[0])
|
||||
c3[0].DomainId = 0
|
||||
c3[0].CertId = 0
|
||||
c3[1].DomainId = 0
|
||||
c3[1].CertId = 0
|
||||
assert.Equal(t, certificate.CertificateDomain{
|
||||
Domain: "summer.test",
|
||||
Wildcard: utils.PBool(false),
|
||||
}, c3[0])
|
||||
assert.Equal(t, certificate.CertificateDomain{
|
||||
Domain: "summer.test",
|
||||
Wildcard: utils.PBool(true),
|
||||
}, c3[1])
|
||||
}
|
||||
|
|
|
@ -12,11 +12,17 @@ import (
|
|||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func NewApiServer(listen string, db *xorm.Engine, verify mjwt.Provider, perm api.AuthConfig) *http.Server {
|
||||
type PermBaseConfig struct {
|
||||
HttpService string `yaml:"httpService"`
|
||||
HttpRedirect string `yaml:"httpRedirect"`
|
||||
ApiRoute string `yaml:"apiRoute"`
|
||||
}
|
||||
|
||||
func NewApiServer(listen string, db *xorm.Engine, verify mjwt.Provider, perm api.AuthConfig[PermBaseConfig]) *http.Server {
|
||||
router := mux.NewRouter()
|
||||
api.HandleCrudDatabase[web.HttpService](router, db, nil, api.AuthPermCheck(verify, perm.Perm["httpService"], perm.SudoPerm), "/http-services", "/http-service/{id}")
|
||||
api.HandleCrudDatabase[web.HttpRedirect](router, db, nil, api.AuthPermCheck(verify, perm.Perm["httpRedirect"], perm.SudoPerm), "/http-redirects", "/http-redirect/{id}")
|
||||
api.HandleCrudDatabase[web.ApiRoute](router, db, nil, api.AuthPermCheck(verify, perm.Perm["apiRoute"], perm.SudoPerm), "/api-routes", "/api-route/{id}")
|
||||
api.HandleCrudDatabase[web.HttpService](router, db, nil, nil, api.AuthPermCheck(verify, perm.PermBase.HttpService, perm.SudoPerm), "/http-services", "/http-service/{id}")
|
||||
api.HandleCrudDatabase[web.HttpRedirect](router, db, nil, nil, api.AuthPermCheck(verify, perm.PermBase.HttpRedirect, perm.SudoPerm), "/http-redirects", "/http-redirect/{id}")
|
||||
api.HandleCrudDatabase[web.ApiRoute](router, db, nil, nil, api.AuthPermCheck(verify, perm.PermBase.ApiRoute, perm.SudoPerm), "/api-routes", "/api-route/{id}")
|
||||
|
||||
s := &http.Server{
|
||||
Addr: listen,
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mrmelon54/favicon"
|
||||
"github.com/sethvargo/go-limiter/httplimit"
|
||||
|
@ -71,6 +72,23 @@ func NewHttpsServer(listen string, db *xorm.Engine, reverseProxy *httputil.Rever
|
|||
func setupHttpsRouter(router *mux.Router, db *xorm.Engine, reverseProxy *httputil.ReverseProxy, apiDomain string) {
|
||||
faviconColor := favicon.NewColor()
|
||||
|
||||
cors := handlers.CORS(
|
||||
handlers.AllowCredentials(),
|
||||
handlers.AllowedOrigins([]string{"*"}),
|
||||
handlers.AllowedHeaders([]string{"Content-Type", "Authorization"}),
|
||||
handlers.AllowedMethods([]string{
|
||||
http.MethodGet,
|
||||
http.MethodHead,
|
||||
http.MethodPost,
|
||||
http.MethodPut,
|
||||
http.MethodPatch,
|
||||
http.MethodDelete,
|
||||
http.MethodConnect,
|
||||
http.MethodOptions,
|
||||
http.MethodTrace,
|
||||
}),
|
||||
)
|
||||
|
||||
router.Use(setupRateLimiter())
|
||||
router.HandleFunc("/favicon.{ext:(?:ico|png|svg)}", utils.CachedPage("1", func(rw http.ResponseWriter, req *http.Request) {
|
||||
if !web.CheckDomainInDb(db, req.Host) {
|
||||
|
@ -107,7 +125,7 @@ func setupHttpsRouter(router *mux.Router, db *xorm.Engine, reverseProxy *httputi
|
|||
rw.WriteHeader(http.StatusOK)
|
||||
_, _ = rw.Write(b)
|
||||
}))
|
||||
router.NotFoundHandler = http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
router.PathPrefix("/").HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
if !web.CheckDomainInDb(db, req.Host) {
|
||||
http.Error(rw, http.StatusText(http.StatusTeapot), http.StatusTeapot)
|
||||
return
|
||||
|
@ -115,7 +133,9 @@ func setupHttpsRouter(router *mux.Router, db *xorm.Engine, reverseProxy *httputi
|
|||
|
||||
if hostDomain, hostPort, ok := utils.SplitDomainPort(req.Host, 443); ok {
|
||||
if hostDomain == apiDomain {
|
||||
handleApiRoute(rw, req, db, reverseProxy)
|
||||
cors(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
handleApiRoute(rw, req, db, reverseProxy)
|
||||
})).ServeHTTP(rw, req)
|
||||
return
|
||||
}
|
||||
if handleHttpService(rw, req, db, hostDomain, reverseProxy) {
|
||||
|
@ -160,9 +180,6 @@ func handleApiRoute(rw http.ResponseWriter, req *http.Request, db *xorm.Engine,
|
|||
req.Close = true
|
||||
req.RequestURI = "/" + pPath
|
||||
req.URL.Path = "/" + pPath
|
||||
|
||||
// Allow all origins to access the API
|
||||
rw.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
reverseProxy.ServeHTTP(rw, req)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -151,3 +151,7 @@ func (c CertDbProvider) Delete(id uint64) error {
|
|||
_, err := c.db.Where("id = ?", id).Update(&certificate.Certificate{Active: utils.PBool(false)})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c CertDbProvider) Id(id string) (uint64, error) {
|
||||
return api.DefaultIdMap(id)
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package api
|
||||
|
||||
type AuthConfig struct {
|
||||
Public string `yaml:"public"`
|
||||
Perm map[string]uint32 `yaml:"perm"`
|
||||
SudoPerm []uint32 `yaml:"sudoPerm"`
|
||||
}
|
|
@ -9,11 +9,16 @@ import (
|
|||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func NewApiServer(listen string, db *xorm.Engine, renewal *renewal.Renewal, verify mjwt.Provider, perm AuthConfig) *http.Server {
|
||||
type PermBaseConfig struct {
|
||||
Certificate string `yaml:"certificate"`
|
||||
CertificatePair string `yaml:"certificatePair"`
|
||||
}
|
||||
|
||||
func NewApiServer(listen string, db *xorm.Engine, renewal *renewal.Renewal, verify mjwt.Provider, perm api.AuthConfig[PermBaseConfig]) *http.Server {
|
||||
router := mux.NewRouter()
|
||||
handleGetPublic(router, db, "/domain-certificate/{domain}")
|
||||
api.HandleCrud[CertDbItem](router, NewCertDbProvider(db, renewal), api.AuthPermCheck(verify, perm.Perm["certificate"], perm.SudoPerm), "/certificates", "/certificate/{id}")
|
||||
handleGetPrivate(router, db, api.AuthPermCheck(verify, perm.Perm["certificatePair"], perm.SudoPerm), "/certificate-pair/{domain}")
|
||||
api.HandleCrud[CertDbItem](router, NewCertDbProvider(db, renewal), "/certificates", "/certificate/{id}")
|
||||
handleGetPrivate(router, db, api.AuthPermCheck(verify, perm.PermBase.CertificatePair, perm.SudoPerm), "/certificate-pair/{domain}")
|
||||
|
||||
return api.RunApiServer(listen, router)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"code.mrmelon54.com/melon/summer/cmd/buttercup/api"
|
||||
api2 "code.mrmelon54.com/melon/summer/cmd/buttercup/api"
|
||||
"code.mrmelon54.com/melon/summer/pkg/api"
|
||||
"code.mrmelon54.com/melon/summer/pkg/renewal"
|
||||
)
|
||||
|
||||
type ButtercupConfig struct {
|
||||
Database string `yaml:"database"`
|
||||
Listen string `yaml:"listen"`
|
||||
Renewal renewal.Config `yaml:"renewal"`
|
||||
ApiAuth api.AuthConfig `yaml:"apiAuth"`
|
||||
Database string `yaml:"database"`
|
||||
Listen string `yaml:"listen"`
|
||||
Renewal renewal.Config `yaml:"renewal"`
|
||||
ApiAuth api.AuthConfig[api2.PermBaseConfig] `yaml:"apiAuth"`
|
||||
}
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
package main
|
||||
|
||||
import "code.mrmelon54.com/melon/summer/pkg/mjwt"
|
||||
|
||||
type MarigoldConfig struct {
|
||||
Database string `yaml:"database"`
|
||||
Listen string `yaml:"listen"`
|
||||
Auth AuthConfig `yaml:"auth"`
|
||||
Database string `yaml:"database"`
|
||||
Listen string `yaml:"listen"`
|
||||
ApiAuth ApiAuthConfig `yaml:"apiAuth"`
|
||||
}
|
||||
|
||||
type AuthConfig struct {
|
||||
Issuer string `yaml:"issuer"`
|
||||
Key string `yaml:"key"`
|
||||
Public string `yaml:"public"`
|
||||
type ApiAuthConfig struct {
|
||||
Issuer string `yaml:"issuer"`
|
||||
Key string `yaml:"key"`
|
||||
Public string `yaml:"public"`
|
||||
PermBase PermBaseConfig `yaml:"permBase"`
|
||||
SudoPerm mjwt.PermStorage `yaml:"sudoPerm"`
|
||||
}
|
||||
|
||||
type PermBaseConfig struct {
|
||||
User string `yaml:"user"`
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
quickUser "code.mrmelon54.com/melon/summer/cmd/marigold/quick-user"
|
||||
accountServer "code.mrmelon54.com/melon/summer/pkg/account-server"
|
||||
"code.mrmelon54.com/melon/summer/pkg/api"
|
||||
"code.mrmelon54.com/melon/summer/pkg/cli"
|
||||
loginChecker "code.mrmelon54.com/melon/summer/pkg/login-checker"
|
||||
"code.mrmelon54.com/melon/summer/pkg/mjwt"
|
||||
"code.mrmelon54.com/melon/summer/pkg/mjwt/auth"
|
||||
oauthServer "code.mrmelon54.com/melon/summer/pkg/oauth-server"
|
||||
"code.mrmelon54.com/melon/summer/pkg/tables/oauth"
|
||||
"code.mrmelon54.com/melon/summer/pkg/tables/user"
|
||||
|
@ -51,11 +53,12 @@ type Marigold struct {
|
|||
func (marigold *Marigold) Init(runner *cli.Runner) {
|
||||
marigold.runner = runner
|
||||
utils.Check("[Marigold.Init()] Failed to sync tables", runner.Database.Sync(usedTables...))
|
||||
quickUser.QuickUser(runner.Database)
|
||||
|
||||
var privKey *rsa.PrivateKey
|
||||
file, err := os.ReadFile(marigold.conf.Auth.Key)
|
||||
file, err := os.ReadFile(marigold.conf.ApiAuth.Key)
|
||||
if os.IsNotExist(err) {
|
||||
privKey = generateNewKeys(marigold.conf.Auth)
|
||||
privKey = generateNewKeys(marigold.conf.ApiAuth)
|
||||
file = pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(privKey),
|
||||
|
@ -66,12 +69,21 @@ func (marigold *Marigold) Init(runner *cli.Runner) {
|
|||
privKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
utils.Check("[Marigold.Init()] Failed to parse token signing key", err)
|
||||
}
|
||||
marigold.signer = mjwt.NewMJwtSigner(marigold.conf.Auth.Issuer, privKey)
|
||||
marigold.signer = mjwt.NewMJwtSigner(marigold.conf.ApiAuth.Issuer, privKey)
|
||||
marigold.checkLogin = loginChecker.NewLoginChecker(runner.Database, marigold.signer)
|
||||
|
||||
router := mux.NewRouter()
|
||||
marigold.auth = accountServer.NewAccountServer(router.PathPrefix("/auth").Subrouter(), runner.Database, marigold.signer)
|
||||
marigold.oauth = oauthServer.NewOAuthServer(router.PathPrefix("/oauth").Subrouter(), runner.Database, marigold.signer)
|
||||
api.HandleCrudDatabase[user.User](router, runner.Database, nil, func(id string) (uint64, error) {
|
||||
if id == "@me" {
|
||||
// Verify as mfa flow token
|
||||
_, b, err := mjwt.ExtractClaims[auth.AccessTokenClaims](marigold.signer, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}, api.AuthPermCheck(marigold.signer, marigold.conf.ApiAuth.PermBase.User, marigold.conf.ApiAuth.SudoPerm), "/users", "/user/{id}")
|
||||
|
||||
marigold.server = api.RunApiServer(marigold.conf.Listen, router)
|
||||
}
|
||||
|
@ -80,7 +92,7 @@ func (marigold *Marigold) Destroy() {
|
|||
_ = marigold.server.Close()
|
||||
}
|
||||
|
||||
func generateNewKeys(auth AuthConfig) *rsa.PrivateKey {
|
||||
func generateNewKeys(auth ApiAuthConfig) *rsa.PrivateKey {
|
||||
fmt.Println("[Marigold.Init()] Generating new RSA private key")
|
||||
key, err := rsa.GenerateKey(rand.New(rand.NewSource(time.Now().UnixNano())), 4096)
|
||||
utils.Check("[Marigold.Init()] Failed to generate new RSA private key", err)
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
//go:build DEBUG
|
||||
|
||||
package quick_user
|
||||
|
||||
import (
|
||||
accountServer "code.mrmelon54.com/melon/summer/pkg/account-server"
|
||||
"code.mrmelon54.com/melon/summer/pkg/tables/user"
|
||||
"code.mrmelon54.com/melon/summer/pkg/utils"
|
||||
"log"
|
||||
"time"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func QuickUser(db *xorm.Engine) {
|
||||
log.Println("[QuickUser()] Generating development admin user for testing marigold")
|
||||
|
||||
count, err := db.Count(&user.User{})
|
||||
utils.Check("[QuickUser()] Failed to count users:", err)
|
||||
if count != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// This password is meant to be visible for debugging purposes
|
||||
pw, err := accountServer.HashPassword("Password123!")
|
||||
utils.Check("[QuickUser()] Failed to hash password:", err)
|
||||
|
||||
_, err = db.Insert(&user.User{
|
||||
Email: "john@example.com",
|
||||
Username: "john",
|
||||
Password: pw,
|
||||
Visibility: utils.PByte(1),
|
||||
Gender: utils.PString("male"),
|
||||
Pronouns: utils.PByte(1),
|
||||
Birthday: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
|
||||
DisplayName: "John Doe",
|
||||
LastOnline: time.Now(),
|
||||
MfaActive: utils.PBool(false),
|
||||
EmailVerified: utils.PBool(true),
|
||||
EnableRefresh: utils.PBool(true),
|
||||
})
|
||||
utils.Check("[QuickUser()] Failed to add user:", err)
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
//go:build !DEBUG
|
||||
|
||||
package quick_user
|
||||
|
||||
import "xorm.io/xorm"
|
||||
|
||||
func QuickUser(_ *xorm.Engine) {}
|
|
@ -3,7 +3,12 @@ package main
|
|||
import "code.mrmelon54.com/melon/summer/pkg/api"
|
||||
|
||||
type RoseConfig struct {
|
||||
Database string `yaml:"database"`
|
||||
Listen string `yaml:"listen"`
|
||||
Auth api.AuthConfig `yaml:"apiAuth"`
|
||||
Database string `yaml:"database"`
|
||||
Listen string `yaml:"listen"`
|
||||
ApiAuth api.AuthConfig[PermBaseConfig] `yaml:"apiAuth"`
|
||||
}
|
||||
|
||||
type PermBaseConfig struct {
|
||||
TcpManager string `yaml:"tcpManager"`
|
||||
UdpManager string `yaml:"udpManager"`
|
||||
}
|
||||
|
|
|
@ -35,12 +35,14 @@ func (rose *Rose) Init(runner *cli.Runner) {
|
|||
rose.runner = runner
|
||||
utils.Check("[Rose.Init()] Failed to sync tables", runner.Database.Sync(usedTables...))
|
||||
var err error
|
||||
rose.verify, err = mjwt.NewMJwtVerifierFromFile(rose.conf.Auth.Public)
|
||||
rose.verify, err = mjwt.NewMJwtVerifierFromFile(rose.conf.ApiAuth.Public)
|
||||
utils.Check("[Rose.Init()] Failed to load verifier public key", err)
|
||||
|
||||
router := mux.NewRouter()
|
||||
rose.tcpApi = tcp.NewTcpManager(router.PathPrefix("/tcp").Subrouter(), runner.Database, rose.verify, rose.conf.Auth)
|
||||
rose.udpApi = udp.NewUdpManager(router.PathPrefix("/udp").Subrouter(), runner.Database, rose.verify, rose.conf.Auth)
|
||||
rose.tcpApi = tcp.NewTcpManager(runner.Database)
|
||||
rose.udpApi = udp.NewUdpManager(runner.Database)
|
||||
api.HandleCrudDatabase[web.TcpRedirect](router, runner.Database, rose.tcpApi, nil, api.AuthPermCheck(rose.verify, rose.conf.ApiAuth.PermBase.TcpManager, rose.conf.ApiAuth.SudoPerm), "/tcp", "/tcp/{id}")
|
||||
api.HandleCrudDatabase[web.UdpRedirect](router, runner.Database, rose.udpApi, nil, api.AuthPermCheck(rose.verify, rose.conf.ApiAuth.PermBase.UdpManager, rose.conf.ApiAuth.SudoPerm), "/udp", "/udp/{id}")
|
||||
rose.server = api.RunApiServer(rose.conf.Listen, router)
|
||||
}
|
||||
|
||||
|
|
9
go.mod
9
go.mod
|
@ -9,6 +9,7 @@ require (
|
|||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/handlers v1.5.1
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/mrmelon54/favicon v0.0.0-20220830075604-72b3eafe69b9
|
||||
github.com/pkg/errors v0.9.1
|
||||
|
@ -32,6 +33,7 @@ require (
|
|||
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dsnet/compress v0.0.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.1 // indirect
|
||||
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81 // indirect
|
||||
github.com/goccy/go-json v0.8.1 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
|
@ -52,14 +54,7 @@ require (
|
|||
github.com/tdewolff/canvas v0.0.0-20220224205349-b6bbc6a34308 // indirect
|
||||
github.com/tdewolff/minify/v2 v2.10.0 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.5.27 // indirect
|
||||
github.com/tidwall/btree v1.4.2 // indirect
|
||||
github.com/tidwall/buntdb v1.2.10 // indirect
|
||||
github.com/tidwall/gjson v1.14.3 // indirect
|
||||
github.com/tidwall/grect v0.1.4 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tidwall/rtred v0.1.2 // indirect
|
||||
github.com/tidwall/tinyqueue v0.1.1 // indirect
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.0 // indirect
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57 // indirect
|
||||
|
|
30
go.sum
30
go.sum
|
@ -34,6 +34,7 @@ github.com/adrg/xdg v0.3.0/go.mod h1:7I2hH/IT30IsupOpKZ5ue7/qNi3CoKzD6tL3HwpaRMQ
|
|||
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
||||
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/ajstarks/svgo v0.0.0-20210923152817-c3b6e2f0c527/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
|
@ -43,6 +44,7 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
|
|||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
|
@ -102,7 +104,10 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
|||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
|
@ -113,6 +118,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
|||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8=
|
||||
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-acme/lego/v4 v4.8.0 h1:XienkuT6ZKHe0DE/LXeGP4ZY+ft+7ZMlqtiJ7XJs2pI=
|
||||
|
@ -210,13 +216,17 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
|||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
|
@ -243,6 +253,7 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
|
|||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
|
@ -302,6 +313,7 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
|
|||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
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/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||
|
@ -314,6 +326,7 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL
|
|||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U=
|
||||
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
|
@ -368,6 +381,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
|||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
|
||||
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
||||
github.com/mrmelon54/favicon v0.0.0-20220830075604-72b3eafe69b9 h1:37A6XlY1rNqbnD2ksPtHSHIUobNaFsBI1zDbuH/saFw=
|
||||
github.com/mrmelon54/favicon v0.0.0-20220830075604-72b3eafe69b9/go.mod h1:11VXnD2+bF1vuLcw4K/2vCTz3atY0kf0kD5iA3Hop9s=
|
||||
|
@ -473,6 +487,7 @@ github.com/sec51/qrcode v0.0.0-20160126144534-b7779abbcaf1 h1:CI9zS8HvMiibvXM/F3
|
|||
github.com/sec51/qrcode v0.0.0-20160126144534-b7779abbcaf1/go.mod h1:uPm44Rj3uXSSOvmKmoeRuAUNUgwH2JHW5KIzqFFS/j4=
|
||||
github.com/sec51/twofactor v1.0.0 h1:1BTbzPhyMyB0YvcWxgNxEkI7WDNsBLvR+z699YWGMC8=
|
||||
github.com/sec51/twofactor v1.0.0/go.mod h1:CjtKwpvQSs9SYzLUsRH7gML+TgKeIofT8uxoy7RTLQI=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sethvargo/go-limiter v0.7.2 h1:FgC4N7RMpV5gMrUdda15FaFTkQ/L4fEqM7seXMs4oO8=
|
||||
github.com/sethvargo/go-limiter v0.7.2/go.mod h1:C0kbSFbiriE5k2FFOe18M1YZbAR2Fiwf72uGu0CXCcU=
|
||||
|
@ -485,6 +500,8 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
|
|||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
|
@ -522,17 +539,14 @@ github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
|
|||
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8=
|
||||
github.com/tidwall/btree v1.4.2 h1:PpkaieETJMUxYNADsjgtNRcERX7mGc/GP2zp/r5FM3g=
|
||||
github.com/tidwall/btree v1.4.2/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE=
|
||||
github.com/tidwall/buntdb v1.1.2/go.mod h1:xAzi36Hir4FarpSHyfuZ6JzPJdjRZ8QlLZSntE2mqlI=
|
||||
github.com/tidwall/buntdb v1.2.10 h1:U/ebfkmYPBnyiNZIirUiWFcxA/mgzjbKlyPynFsPtyM=
|
||||
github.com/tidwall/buntdb v1.2.10/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU=
|
||||
github.com/tidwall/gjson v1.3.4/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
|
||||
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
|
||||
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb/go.mod h1:lKYYLFIr9OIgdgrtgkZ9zgRxRdvPYsExnYBsEAd8W5M=
|
||||
github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg=
|
||||
github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q=
|
||||
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
|
@ -540,27 +554,33 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV
|
|||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8=
|
||||
github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ=
|
||||
github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e/go.mod h1:/h+UnNGt0IhNNJLkGikcdcJqm66zGD/uJGMRxK/9+Ao=
|
||||
github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563/go.mod h1:mLqSmt7Dv/CNneF2wfcChfN1rvapyQr01LGKnKex0DQ=
|
||||
github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE=
|
||||
github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.34.0 h1:d3AAQJ2DRcxJYHm7OXNXtXt2as1vMDfxeIcFvhmGGm4=
|
||||
github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.0 h1:tY2slqVQ6bN+yHSnDYwZebLQFkphK4WNrVwnt7CJZ2I=
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.0/go.mod h1:yx7MvAVNcP/kN9lKXM/NTce4au4DFN99j6i1OwDclNA=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
||||
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
|
||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
passwordPattern = regexp.MustCompile("^{8,32}$")
|
||||
passwordPattern = regexp.MustCompile("^.{8,32}$")
|
||||
passwordHasDigit = regexp.MustCompile("\\d")
|
||||
passwordHasSymbol = regexp.MustCompile("\\W")
|
||||
|
||||
|
|
|
@ -44,12 +44,12 @@ func NewAccountServer(router *mux.Router, db *xorm.Engine, signer mjwt.Provider)
|
|||
flows: make(map[string]*LoginFlow),
|
||||
mFlow: &sync.RWMutex{},
|
||||
}
|
||||
router.HandleFunc("/login", s.handlePostLogin)
|
||||
router.HandleFunc("/refresh", s.handlePostRefresh)
|
||||
router.HandleFunc("/password-reset", s.handlePostPasswordReset)
|
||||
router.HandleFunc("/password-email", s.handlePostPasswordEmail)
|
||||
router.HandleFunc("/mfa", s.handleMfaCreate)
|
||||
router.HandleFunc("/mfa-disable", s.handleMfaDisable)
|
||||
router.HandleFunc("/login", s.handlePostLogin).Methods(http.MethodPost)
|
||||
router.HandleFunc("/refresh", s.handlePostRefresh).Methods(http.MethodPost)
|
||||
router.HandleFunc("/password-reset", s.handlePostPasswordReset).Methods(http.MethodPost)
|
||||
router.HandleFunc("/password-email", s.handlePostPasswordEmail).Methods(http.MethodPost)
|
||||
router.HandleFunc("/mfa", s.handleMfaCreate).Methods(http.MethodPost)
|
||||
router.HandleFunc("/mfa-disable", s.handleMfaDisable).Methods(http.MethodPost)
|
||||
return s
|
||||
}
|
||||
|
||||
|
@ -64,15 +64,15 @@ func (a *AccountServer) LoadMfaTotp(u *user.User) (*twofactor.Totp, error) {
|
|||
|
||||
func (a *AccountServer) outputUserTokens(rw http.ResponseWriter, u *user.User) {
|
||||
// Get perms list
|
||||
var perms []user.LinkUserPerm
|
||||
err := a.db.Where("user_id = ?", u.Id).Find(&perms)
|
||||
var perms []user.ExtendUserPerm
|
||||
err := a.db.Where("user_id = ?", u.Id).Join("INNER", &user.Perm{}, "link_user_perm.perm_id = perm.id").Find(&perms)
|
||||
if api.GenericErrorMsg[AccountServer](rw, err, http.StatusInternalServerError, "Internal Server Error") {
|
||||
return
|
||||
}
|
||||
|
||||
storage := mjwt.EmptyPermStorage()
|
||||
for i := range perms {
|
||||
storage.Set(uint32(perms[i].PermId))
|
||||
storage := mjwt.NewPermStorage()
|
||||
for _, i := range perms {
|
||||
storage.Set(i.Perm.Name)
|
||||
}
|
||||
|
||||
// Generate access and refresh tokens
|
||||
|
@ -118,7 +118,15 @@ func (a *AccountServer) handlePostLogin(rw http.ResponseWriter, req *http.Reques
|
|||
|
||||
// Verify mjwt
|
||||
_, b, err := mjwt.ExtractClaims[flow.LoginFlowClaims](a.signer, token)
|
||||
if api.GenericErrorMsg[AccountServer](rw, err, http.StatusInternalServerError, "Token Not Valid") {
|
||||
if err != nil {
|
||||
// Verify as access token
|
||||
_, z, err := mjwt.ExtractClaims[auth.AccessTokenClaims](a.signer, token)
|
||||
if api.GenericErrorMsg[AccountServer](rw, err, http.StatusUnauthorized, "Token Not Valid") {
|
||||
return
|
||||
}
|
||||
|
||||
// The request was sent to verify an existing access token and was successful
|
||||
a.outputFlowStep(rw, z.ID, "done", z.Claims.UserId)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -145,12 +153,12 @@ func (a *AccountServer) handlePostLogin(rw http.ResponseWriter, req *http.Reques
|
|||
case "login":
|
||||
// Check email matches pattern
|
||||
if !emailPattern.MatchString(in.Email) {
|
||||
api.GenericErrorMsg[AccountServer](rw, api.ErrParamWrongType, http.StatusBadRequest, "Invalid input")
|
||||
api.GenericErrorMsg[AccountServer](rw, api.ErrParamWrongType, http.StatusBadRequest, "Invalid email input")
|
||||
return
|
||||
}
|
||||
// Check password matches pattern
|
||||
if !ValidatePassword(in.Password) {
|
||||
api.GenericErrorMsg[AccountServer](rw, api.ErrParamWrongType, http.StatusBadRequest, "Invalid input")
|
||||
api.GenericErrorMsg[AccountServer](rw, api.ErrParamWrongType, http.StatusBadRequest, "Invalid password input")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -444,7 +452,7 @@ func (a *AccountServer) handleGetUserInfo(rw http.ResponseWriter, req *http.Requ
|
|||
return
|
||||
}
|
||||
|
||||
z := user.CheckSpecificPerms(a.db, b.Claims.Perms, []string{"super:user", "user:info", "user:email", "user:profile"})
|
||||
z := user.CheckSpecificPerms(b.Claims.Perms, []string{"super:user", "user:info", "user:email", "user:profile"})
|
||||
if z[0] {
|
||||
z[1] = true
|
||||
z[2] = true
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"code.mrmelon54.com/melon/summer/pkg/mjwt"
|
||||
"code.mrmelon54.com/melon/summer/pkg/mjwt/auth"
|
||||
"code.mrmelon54.com/melon/summer/pkg/utils"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
@ -68,33 +66,6 @@ func GenericErrorMsg[T any](rw http.ResponseWriter, err error, code int, msg str
|
|||
return true
|
||||
}
|
||||
|
||||
func AuthPermCheck(verify mjwt.Provider, perm uint32, sudoPerm []uint32) func(req *http.Request) error {
|
||||
return func(req *http.Request) error {
|
||||
token := utils.GetBearerToken(req)
|
||||
if token == "" {
|
||||
return jwt.ErrTokenMalformed
|
||||
}
|
||||
|
||||
// Verify as mfa flow token
|
||||
_, b, err := mjwt.ExtractClaims[auth.AccessTokenClaims](verify, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check perms
|
||||
perms := b.Claims.Perms
|
||||
if perms.Get(perm) {
|
||||
return nil
|
||||
}
|
||||
for _, j := range sudoPerm {
|
||||
if perms.Get(j) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return jwt.ErrTokenInvalidClaims
|
||||
}
|
||||
}
|
||||
|
||||
func RunApiServer(listen string, router *mux.Router) *http.Server {
|
||||
s := &http.Server{
|
||||
Addr: listen,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package api
|
||||
|
||||
type AuthConfig struct {
|
||||
Public string `yaml:"public"`
|
||||
Perm map[string]uint32 `yaml:"perm"`
|
||||
SudoPerm []uint32 `yaml:"sudoPerm"`
|
||||
import "code.mrmelon54.com/melon/summer/pkg/mjwt"
|
||||
|
||||
type AuthConfig[T any] struct {
|
||||
Public string `yaml:"public"`
|
||||
SudoPerm mjwt.PermStorage `yaml:"sudoPerm"`
|
||||
PermBase T `yaml:"permBase"`
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"code.mrmelon54.com/melon/summer/pkg/mjwt"
|
||||
"github.com/gorilla/mux"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
|
@ -23,6 +24,7 @@ type crudDatabaseMapper[T CrudProviderDatabase[T]] struct {
|
|||
db *xorm.Engine
|
||||
mapped T
|
||||
detect CrudDetector[T]
|
||||
idMap func(id string) (uint64, error)
|
||||
}
|
||||
|
||||
func (c crudDatabaseMapper[T]) Find() ([]T, error) {
|
||||
|
@ -72,7 +74,18 @@ func (c crudDatabaseMapper[T]) Delete(id uint64) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func HandleCrudDatabase[T CrudProviderDatabase[T]](router *mux.Router, engine *xorm.Engine, detector CrudDetector[T], checkAuth func(req *http.Request) error, findPath, itemPath string) {
|
||||
m := crudDatabaseMapper[T]{db: engine, detect: detector}
|
||||
HandleCrud[T](router, m, checkAuth, findPath, itemPath)
|
||||
func (c crudDatabaseMapper[T]) Id(id string) (uint64, error) {
|
||||
if c.idMap != nil {
|
||||
return c.idMap(id)
|
||||
}
|
||||
return DefaultIdMap(id)
|
||||
}
|
||||
|
||||
func DefaultIdMap(id string) (uint64, error) {
|
||||
return strconv.ParseUint(id, 10, 64)
|
||||
}
|
||||
|
||||
func HandleCrudDatabase[T CrudProviderDatabase[T]](router *mux.Router, engine *xorm.Engine, detector CrudDetector[T], idMap func(id string) (uint64, error), verify mjwt.Provider, findPath, itemPath string) {
|
||||
m := crudDatabaseMapper[T]{db: engine, detect: detector, idMap: idMap}
|
||||
HandleCrud[T](router, m, findPath, itemPath)
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"code.mrmelon54.com/melon/summer/pkg/mjwt"
|
||||
"code.mrmelon54.com/melon/summer/pkg/mjwt/auth"
|
||||
"code.mrmelon54.com/melon/summer/pkg/utils"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/gorilla/mux"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type CrudProvider[T any] interface {
|
||||
|
@ -15,34 +18,52 @@ type CrudProvider[T any] interface {
|
|||
Put(id uint64, item T) error
|
||||
Patch(id uint64, item T) error
|
||||
Delete(id uint64) error
|
||||
Id(id string, req *http.Request, claims auth.AccessTokenClaims) (uint64, error)
|
||||
}
|
||||
|
||||
type crudHandler[T any] struct {
|
||||
provider CrudProvider[T]
|
||||
verify mjwt.Provider
|
||||
permBase string
|
||||
sudoPerm mjwt.PermStorage
|
||||
}
|
||||
|
||||
func HandleCrud[T any](router *mux.Router, provider CrudProvider[T], checkAuth func(req *http.Request) error, findPath, itemPath string) {
|
||||
z := &crudHandler[T]{provider: provider}
|
||||
h := func(handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
fmt.Println("Running CRUD auth check")
|
||||
err := checkAuth(req)
|
||||
if GenericErrorMsg[T](rw, err, http.StatusForbidden, "Out of Scope") {
|
||||
return
|
||||
}
|
||||
handler.ServeHTTP(rw, req)
|
||||
})
|
||||
func HandleCrud[T any](router *mux.Router, provider CrudProvider[T], verify mjwt.Provider, permBase string, sudoPerm mjwt.PermStorage, findPath, itemPath string) {
|
||||
z := &crudHandler[T]{provider: provider, verify: verify, permBase: permBase, sudoPerm: sudoPerm}
|
||||
router.HandleFunc(findPath, z.handleFind).Methods(http.MethodGet)
|
||||
router.HandleFunc(findPath, z.handleAdd).Methods(http.MethodPost)
|
||||
router.HandleFunc(itemPath, z.handleGet).Methods(http.MethodGet)
|
||||
router.HandleFunc(itemPath, z.handlePut).Methods(http.MethodPut)
|
||||
router.HandleFunc(itemPath, z.handlePatch).Methods(http.MethodPatch)
|
||||
router.HandleFunc(itemPath, z.handleDelete).Methods(http.MethodDelete)
|
||||
}
|
||||
|
||||
func (c *crudHandler[T]) checkAuth(req *http.Request, permSuffix string) (claims auth.AccessTokenClaims, err error) {
|
||||
token := utils.GetBearerToken(req)
|
||||
if token == "" {
|
||||
return claims, jwt.ErrTokenMalformed
|
||||
}
|
||||
router.Handle(findPath, h(http.HandlerFunc(z.handleFind))).Methods(http.MethodGet)
|
||||
router.Handle(findPath, h(http.HandlerFunc(z.handleAdd))).Methods(http.MethodPost)
|
||||
router.Handle(itemPath, h(http.HandlerFunc(z.handleGet))).Methods(http.MethodGet)
|
||||
router.Handle(itemPath, h(http.HandlerFunc(z.handlePut))).Methods(http.MethodPut)
|
||||
router.Handle(itemPath, h(http.HandlerFunc(z.handlePatch))).Methods(http.MethodPatch)
|
||||
router.Handle(itemPath, h(http.HandlerFunc(z.handleDelete))).Methods(http.MethodDelete)
|
||||
|
||||
// Verify as mfa flow token
|
||||
_, b, err := mjwt.ExtractClaims[auth.AccessTokenClaims](c.verify, token)
|
||||
if err != nil {
|
||||
return claims, err
|
||||
}
|
||||
|
||||
// Check perms
|
||||
claims = b.Claims
|
||||
if claims.Perms.Has(c.permBase+":"+permSuffix) || claims.Perms.OneOf(c.sudoPerm) {
|
||||
return claims, nil
|
||||
}
|
||||
return claims, jwt.ErrTokenInvalidClaims
|
||||
}
|
||||
|
||||
func (h *crudHandler[T]) handleFind(rw http.ResponseWriter, req *http.Request) {
|
||||
a, err := h.provider.Find()
|
||||
func (c *crudHandler[T]) handleFind(rw http.ResponseWriter, req *http.Request) {
|
||||
_, err := c.checkAuth(req, "find")
|
||||
if GenericErrorMsg[T](rw, err, http.StatusForbidden, "403 Forbidden") {
|
||||
return
|
||||
}
|
||||
a, err := c.provider.Find()
|
||||
if GenericErrorMsg[T](rw, err, http.StatusInternalServerError, "Find error") {
|
||||
return
|
||||
}
|
||||
|
@ -54,13 +75,17 @@ func (h *crudHandler[T]) handleFind(rw http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *crudHandler[T]) handleAdd(rw http.ResponseWriter, req *http.Request) {
|
||||
func (c *crudHandler[T]) handleAdd(rw http.ResponseWriter, req *http.Request) {
|
||||
_, err := c.checkAuth(req, "add")
|
||||
if GenericErrorMsg[T](rw, err, http.StatusForbidden, "403 Forbidden") {
|
||||
return
|
||||
}
|
||||
var t T
|
||||
err := json.NewDecoder(req.Body).Decode(&t)
|
||||
err = json.NewDecoder(req.Body).Decode(&t)
|
||||
if GenericErrorMsg[T](rw, err, http.StatusBadRequest, "Decode error") {
|
||||
return
|
||||
}
|
||||
t, err = h.provider.Add(t)
|
||||
t, err = c.provider.Add(t)
|
||||
if GenericErrorMsg[T](rw, err, http.StatusInternalServerError, "Add error") {
|
||||
return
|
||||
}
|
||||
|
@ -72,19 +97,23 @@ func (h *crudHandler[T]) handleAdd(rw http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *crudHandler[T]) handleGet(rw http.ResponseWriter, req *http.Request) {
|
||||
func (c *crudHandler[T]) handleGet(rw http.ResponseWriter, req *http.Request) {
|
||||
claims, err := c.checkAuth(req, "get")
|
||||
if GenericErrorMsg[T](rw, err, http.StatusForbidden, "403 Forbidden") {
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(req)
|
||||
s, ok := vars["id"]
|
||||
if !ok {
|
||||
GenericErrorMsg[T](rw, ErrParamMissing, http.StatusBadRequest, "Parameter error")
|
||||
return
|
||||
}
|
||||
id, err := strconv.ParseUint(s, 10, 64)
|
||||
id, err := c.provider.Id(s, req, claims)
|
||||
if err != nil {
|
||||
GenericErrorMsg[T](rw, fmt.Errorf("parameter error: %w", err), http.StatusBadRequest, "Parameter error")
|
||||
return
|
||||
}
|
||||
t, found, err := h.provider.Get(id)
|
||||
t, found, err := c.provider.Get(id)
|
||||
if GenericErrorMsg[T](rw, err, http.StatusInternalServerError, "Get error") {
|
||||
return
|
||||
}
|
||||
|
@ -100,14 +129,18 @@ func (h *crudHandler[T]) handleGet(rw http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *crudHandler[T]) handlePut(rw http.ResponseWriter, req *http.Request) {
|
||||
func (c *crudHandler[T]) handlePut(rw http.ResponseWriter, req *http.Request) {
|
||||
claims, err := c.checkAuth(req, "put")
|
||||
if GenericErrorMsg[T](rw, err, http.StatusForbidden, "403 Forbidden") {
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(req)
|
||||
s, ok := vars["id"]
|
||||
if !ok {
|
||||
GenericErrorMsg[T](rw, ErrParamMissing, http.StatusBadRequest, "Parameter error")
|
||||
return
|
||||
}
|
||||
id, err := strconv.ParseUint(s, 10, 64)
|
||||
id, err := c.provider.Id(s, req, claims)
|
||||
if err != nil {
|
||||
GenericErrorMsg[T](rw, fmt.Errorf("parameter error: %w", err), http.StatusBadRequest, "Parameter error")
|
||||
return
|
||||
|
@ -117,21 +150,25 @@ func (h *crudHandler[T]) handlePut(rw http.ResponseWriter, req *http.Request) {
|
|||
if GenericErrorMsg[T](rw, err, http.StatusBadRequest, "Decode error") {
|
||||
return
|
||||
}
|
||||
err = h.provider.Put(id, t)
|
||||
err = c.provider.Put(id, t)
|
||||
if GenericErrorMsg[T](rw, err, http.StatusInternalServerError, "Put error") {
|
||||
return
|
||||
}
|
||||
rw.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (h *crudHandler[T]) handlePatch(rw http.ResponseWriter, req *http.Request) {
|
||||
func (c *crudHandler[T]) handlePatch(rw http.ResponseWriter, req *http.Request) {
|
||||
claims, err := c.checkAuth(req, "patch")
|
||||
if GenericErrorMsg[T](rw, err, http.StatusForbidden, "403 Forbidden") {
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(req)
|
||||
s, ok := vars["id"]
|
||||
if !ok {
|
||||
GenericErrorMsg[T](rw, ErrParamMissing, http.StatusBadRequest, "Parameter error")
|
||||
return
|
||||
}
|
||||
id, err := strconv.ParseUint(s, 10, 64)
|
||||
id, err := c.provider.Id(s, req, claims)
|
||||
if err != nil {
|
||||
GenericErrorMsg[T](rw, fmt.Errorf("parameter error: %w", err), http.StatusBadRequest, "Parameter error")
|
||||
return
|
||||
|
@ -141,26 +178,30 @@ func (h *crudHandler[T]) handlePatch(rw http.ResponseWriter, req *http.Request)
|
|||
if GenericErrorMsg[T](rw, err, http.StatusBadRequest, "Decode error") {
|
||||
return
|
||||
}
|
||||
err = h.provider.Patch(id, t)
|
||||
err = c.provider.Patch(id, t)
|
||||
if GenericErrorMsg[T](rw, err, http.StatusInternalServerError, "Patch error") {
|
||||
return
|
||||
}
|
||||
rw.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (h *crudHandler[T]) handleDelete(rw http.ResponseWriter, req *http.Request) {
|
||||
func (c *crudHandler[T]) handleDelete(rw http.ResponseWriter, req *http.Request) {
|
||||
claims, err := c.checkAuth(req, "delete")
|
||||
if GenericErrorMsg[T](rw, err, http.StatusForbidden, "403 Forbidden") {
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(req)
|
||||
s, ok := vars["id"]
|
||||
if !ok {
|
||||
GenericErrorMsg[T](rw, ErrParamMissing, http.StatusBadRequest, "Parameter error")
|
||||
return
|
||||
}
|
||||
id, err := strconv.ParseUint(s, 10, 64)
|
||||
id, err := c.provider.Id(s, req, claims)
|
||||
if err != nil {
|
||||
GenericErrorMsg[T](rw, fmt.Errorf("parameter error: %w", err), http.StatusBadRequest, "Parameter error")
|
||||
return
|
||||
}
|
||||
err = h.provider.Delete(id)
|
||||
err = c.provider.Delete(id)
|
||||
if GenericErrorMsg[T](rw, err, http.StatusInternalServerError, "Delete error") {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package auth
|
|||
|
||||
import (
|
||||
"code.mrmelon54.com/melon/summer/pkg/mjwt"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -21,23 +20,3 @@ func CreateAccessToken(p mjwt.Provider, sub, id string, userId uint64, perms mjw
|
|||
Perms: perms,
|
||||
})
|
||||
}
|
||||
func (a AccessTokenClaims) MarshalJSON() ([]byte, error) {
|
||||
b := make(map[string]any)
|
||||
b["uid"] = a.UserId
|
||||
b["per"] = a.Perms
|
||||
return json.Marshal(b)
|
||||
}
|
||||
|
||||
func (a AccessTokenClaims) UnmarshalJSON(bytes []byte) error {
|
||||
type z struct {
|
||||
Uid uint64
|
||||
Per []uint32
|
||||
}
|
||||
var b z
|
||||
if err := json.Unmarshal(bytes, &b); err != nil {
|
||||
return err
|
||||
}
|
||||
a.UserId = b.Uid
|
||||
a.Perms = mjwt.ParsePermStorage(b.Per)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package flow
|
|||
|
||||
import (
|
||||
"code.mrmelon54.com/melon/summer/pkg/mjwt"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -21,23 +20,3 @@ func CreateOAuthFlowToken(p mjwt.Provider, sub, id string, loginId uint64, scope
|
|||
Scopes: scopes,
|
||||
})
|
||||
}
|
||||
func (o OAuthFlowClaims) MarshalJSON() ([]byte, error) {
|
||||
a := make(map[string]any)
|
||||
a["lid"] = o.LoginId
|
||||
a["sco"] = o.Scopes.Dump()
|
||||
return json.Marshal(a)
|
||||
}
|
||||
|
||||
func (o OAuthFlowClaims) UnmarshalJSON(bytes []byte) error {
|
||||
type z struct {
|
||||
Lid uint64
|
||||
Sco []uint32
|
||||
}
|
||||
var a z
|
||||
if err := json.Unmarshal(bytes, &a); err != nil {
|
||||
return err
|
||||
}
|
||||
o.LoginId = a.Lid
|
||||
o.Scopes = mjwt.ParsePermStorage(a.Sco)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,10 +1,79 @@
|
|||
package mjwt
|
||||
|
||||
import (
|
||||
bitStorage "code.mrmelon54.com/melon/summer/pkg/bit-storage"
|
||||
"encoding/json"
|
||||
"gopkg.in/yaml.v3"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type PermStorage bitStorage.BitStorage[uint32]
|
||||
type PermStorage map[string]struct{}
|
||||
|
||||
func EmptyPermStorage() PermStorage { return bitStorage.EmptyInfiniteU32() }
|
||||
func ParsePermStorage(v []uint32) PermStorage { return bitStorage.ParseInfiniteU32(v) }
|
||||
func NewPermStorage() PermStorage { return make(PermStorage) }
|
||||
func ParsePermStorage(a []string) PermStorage {
|
||||
p := NewPermStorage()
|
||||
p.prepare(a)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *PermStorage) Set(perm string) {
|
||||
(*p)[perm] = struct{}{}
|
||||
}
|
||||
|
||||
func (p *PermStorage) Clear(perm string) {
|
||||
delete(*p, perm)
|
||||
}
|
||||
|
||||
func (p *PermStorage) Has(perm string) bool {
|
||||
_, ok := (*p)[perm]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (p *PermStorage) OneOf(o PermStorage) bool {
|
||||
for i, _ := range o {
|
||||
if o.Has(i) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PermStorage) dump() []string {
|
||||
var a []string
|
||||
for i := range *p {
|
||||
a = append(a, i)
|
||||
}
|
||||
sort.Strings(a)
|
||||
return a
|
||||
}
|
||||
|
||||
func (p *PermStorage) prepare(a []string) {
|
||||
for _, i := range a {
|
||||
p.Set(i)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PermStorage) MarshalJSON() ([]byte, error) { return json.Marshal(p.dump()) }
|
||||
|
||||
func (p *PermStorage) UnmarshalJSON(bytes []byte) error {
|
||||
*p = make(PermStorage)
|
||||
var a []string
|
||||
err := json.Unmarshal(bytes, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.prepare(a)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PermStorage) MarshalYAML() (interface{}, error) { return yaml.Marshal(p.dump()) }
|
||||
|
||||
func (p *PermStorage) UnmarshalYAML(value *yaml.Node) error {
|
||||
*p = make(PermStorage)
|
||||
var a []string
|
||||
err := value.Decode(&a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.prepare(a)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"github.com/google/uuid"
|
||||
"github.com/gorilla/mux"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"xorm.io/xorm"
|
||||
|
@ -120,12 +119,7 @@ func (o *OAuthServer) handleOAuthAuthorize(rw http.ResponseWriter, req *http.Req
|
|||
rScopes := strings.FieldsFunc(in.Scope, func(r rune) bool {
|
||||
return strings.ContainsRune(" +,", r)
|
||||
})
|
||||
rScopeUint := make([]uint32, 0)
|
||||
for i := range rScopes {
|
||||
a, _ := strconv.ParseUint(rScopes[i], 10, 32)
|
||||
rScopeUint[i] = uint32(a)
|
||||
}
|
||||
scopes := mjwt.ParsePermStorage(rScopeUint)
|
||||
scopes := mjwt.ParsePermStorage(rScopes)
|
||||
|
||||
flowToken, err := flow.CreateOAuthFlowToken(o.signer, fmt.Sprint(in.ClientId), uuid.NewString(), b.Claims.UserId, scopes, time.Minute*10)
|
||||
if api.GenericErrorMsg[OAuthServer](rw, api.ErrNoError, http.StatusBadRequest, "Failed to sign token") {
|
||||
|
|
|
@ -3,8 +3,6 @@ package user
|
|||
import (
|
||||
"code.mrmelon54.com/melon/summer/pkg/mjwt"
|
||||
"code.mrmelon54.com/melon/summer/pkg/utils"
|
||||
"log"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type Perm struct {
|
||||
|
@ -31,23 +29,10 @@ func (p Perm) ClearForNew() Perm {
|
|||
return p
|
||||
}
|
||||
|
||||
func CheckSpecificPerms(db *xorm.Engine, storage mjwt.PermStorage, v []string) []bool {
|
||||
func CheckSpecificPerms(storage mjwt.PermStorage, v []string) []bool {
|
||||
z := make([]bool, len(v))
|
||||
y := make(map[string]int)
|
||||
for i := range v {
|
||||
y[v[i]] = i
|
||||
}
|
||||
|
||||
var k []Perm
|
||||
err := db.In("key", v).Find(&k)
|
||||
if err != nil {
|
||||
log.Println("[CheckSpecificPerms()] Database error:", err)
|
||||
return z
|
||||
}
|
||||
|
||||
for i := range k {
|
||||
j := y[k[i].Key]
|
||||
z[j] = storage.Get(uint32(k[i].Id))
|
||||
z[i] = storage.Has(v[i])
|
||||
}
|
||||
return z
|
||||
}
|
||||
|
|
|
@ -4,3 +4,8 @@ type LinkUserPerm struct {
|
|||
UserId uint64 `json:"user_id"`
|
||||
PermId uint64 `json:"perm_id"`
|
||||
}
|
||||
|
||||
type ExtendUserPerm struct {
|
||||
User User `xorm:"extends"`
|
||||
Perm Perm `xorm:"extends"`
|
||||
}
|
||||
|
|
|
@ -57,4 +57,19 @@ type User struct {
|
|||
EmailVerified *bool `json:"email_verified"`
|
||||
EmailToken string `json:"-"`
|
||||
EnableRefresh *bool `json:"-"`
|
||||
Enabled *bool `json:"enabled"`
|
||||
}
|
||||
|
||||
func (u User) GetId() uint64 {
|
||||
return u.Id
|
||||
}
|
||||
|
||||
func (u User) SetEnabled(v bool) User {
|
||||
u.Enabled = utils.PBool(v)
|
||||
return u
|
||||
}
|
||||
|
||||
func (u User) ClearForNew() User {
|
||||
u.Id = 0
|
||||
return u
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"code.mrmelon54.com/melon/summer/pkg/api"
|
||||
"code.mrmelon54.com/melon/summer/pkg/mjwt"
|
||||
"code.mrmelon54.com/melon/summer/pkg/tables/web"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
|
@ -19,14 +17,12 @@ type Manager struct {
|
|||
db *xorm.Engine
|
||||
}
|
||||
|
||||
func NewTcpManager(router *mux.Router, db *xorm.Engine, verify mjwt.Provider, perm api.AuthConfig) *Manager {
|
||||
func NewTcpManager(db *xorm.Engine) *Manager {
|
||||
m := &Manager{
|
||||
mutex: &sync.RWMutex{},
|
||||
servers: make(map[uint64]*Proxy),
|
||||
router: router,
|
||||
db: db,
|
||||
}
|
||||
api.HandleCrudDatabase[web.TcpRedirect](router, db, m, api.AuthPermCheck(verify, perm.Perm["tcpManager"], perm.SudoPerm), "/", "/{id}")
|
||||
return m
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package udp
|
||||
|
||||
import (
|
||||
"code.mrmelon54.com/melon/summer/pkg/api"
|
||||
"code.mrmelon54.com/melon/summer/pkg/mjwt"
|
||||
"code.mrmelon54.com/melon/summer/pkg/tables/web"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
|
@ -19,14 +17,12 @@ type Manager struct {
|
|||
db *xorm.Engine
|
||||
}
|
||||
|
||||
func NewUdpManager(router *mux.Router, db *xorm.Engine, verify mjwt.Provider, perm api.AuthConfig) *Manager {
|
||||
func NewUdpManager(db *xorm.Engine) *Manager {
|
||||
m := &Manager{
|
||||
mutex: &sync.RWMutex{},
|
||||
servers: make(map[uint64]*Proxy),
|
||||
router: router,
|
||||
db: db,
|
||||
}
|
||||
api.HandleCrudDatabase[web.UdpRedirect](router, db, m, api.AuthPermCheck(verify, perm.Perm["udpManager"], perm.SudoPerm), "/", "/{id}")
|
||||
return m
|
||||
}
|
||||
|
||||
|
|
Reference in New Issue