diff --git a/.gitignore b/.gitignore index 44c8f3c..7be2177 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ config.yaml .gitignore .gitignore +mouthpiece +test.db diff --git a/config/auth_model.conf b/config/auth_model.conf new file mode 100644 index 0000000..8003e9c --- /dev/null +++ b/config/auth_model.conf @@ -0,0 +1,16 @@ +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub, obj, act + +[role_definition] +g = _, _ +g2 = _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.act + diff --git a/frontend/index.html b/frontend/login/index.html similarity index 100% rename from frontend/index.html rename to frontend/login/index.html diff --git a/frontend/main.js b/frontend/login/main.js similarity index 96% rename from frontend/main.js rename to frontend/login/main.js index ad4fa1a..dd9e290 100644 --- a/frontend/main.js +++ b/frontend/login/main.js @@ -71,9 +71,9 @@ function login(prov) { }); } -function loginAnonymously(username) { - return fetch( - `/auth/anonymous/login?id=auth-example&user=${encodeURIComponent(username)}` +function loginAnonymously(username, password) { + return req( + `/auth/direct/login?id=auth-example&user=${encodeURIComponent(username)}&passwd=${encodeURIComponent(password)}` ); } @@ -89,7 +89,7 @@ function loginViaEmailToken(token) { return req(`/auth/email/login?token=${token}`); } -const validUsernameRegex = /^[a-zA-Z][\w ]+$/; +const validUsernameRegex = /[^@]+@[^\.]+\..+/; function getUsernameInvalidReason(username) { if (username.length < 3) return "Username must be at least 3 characters long"; @@ -120,6 +120,11 @@ function getAnonymousLoginForm(onSubmit) { input.placeholder = "Username"; input.className = "anon-form__input"; + const pass = document.createElement("input"); + pass.type = "text"; + pass.placeholder = "Password"; + pass.className = "anon-form__input"; + const submit = document.createElement("input"); submit.type = "submit"; submit.value = "Log in"; @@ -143,11 +148,12 @@ function getAnonymousLoginForm(onSubmit) { }); form.appendChild(input); + form.appendChild(pass); form.appendChild(submit); form.addEventListener("submit", e => { e.preventDefault(); - onSubmit(input.value); + onSubmit(input.value, pass.value); }); return form; @@ -330,7 +336,7 @@ function getLoginLinks() { return getProviders().then(providers => providers.map(prov => { let a; - if (prov === "anonymous") { + if (prov === "direct") { a = document.createElement("span"); a.dataset.provider = prov; a.className = "login__prov"; @@ -355,8 +361,8 @@ function getLoginLinks() { } }); - const form = getAnonymousLoginForm(username => { - loginAnonymously(username) + const form = getAnonymousLoginForm((username, password) => { + loginAnonymously(username, password) .then(() => { window.location.replace(window.location.href); }) diff --git a/frontend/style.css b/frontend/login/style.css similarity index 100% rename from frontend/style.css rename to frontend/login/style.css diff --git a/go.mod b/go.mod index 3431ef2..ef7d696 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,8 @@ require ( ) require ( + github.com/casbin/casbin/v2 v2.37.4 + github.com/casbin/gorm-adapter/v3 v3.7.4 github.com/go-chi/chi v4.1.2+incompatible github.com/go-pkgz/auth v1.19.0 github.com/go-pkgz/repeater v1.1.3 @@ -29,27 +31,43 @@ require ( require ( cloud.google.com/go/compute v1.2.0 // indirect github.com/Jeffail/gabs/v2 v2.6.1 // indirect + github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect github.com/andybalholm/brotli v1.0.4 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/danielgtaylor/casing v0.0.0-20210126043903-4e55e6373ac3 // indirect + github.com/denisenkom/go-mssqldb v0.12.0 // indirect github.com/dghubble/oauth1 v0.7.1 // indirect github.com/fasthttp/router v1.4.10 // indirect github.com/fatih/color v1.13.0 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect + github.com/glebarez/go-sqlite v1.16.0 // indirect + github.com/glebarez/sqlite v1.4.3 // indirect github.com/go-oauth2/oauth2/v4 v4.4.3 // indirect github.com/go-playground/validator/v10 v10.11.0 // indirect + github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/goccy/go-json v0.9.10 // indirect github.com/goccy/go-yaml v1.9.5 // indirect + github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect + github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/graphql-go/graphql v0.8.0 // indirect github.com/graphql-go/handler v0.2.3 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.12.1 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.3.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect + github.com/jackc/pgtype v1.11.0 // indirect + github.com/jackc/pgx/v4 v4.16.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/klauspost/compress v1.15.0 // indirect @@ -64,6 +82,7 @@ require ( github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pelletier/go-toml v1.9.4 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect github.com/spf13/afero v1.8.2 // indirect github.com/spf13/cast v1.4.1 // indirect @@ -98,6 +117,14 @@ require ( gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gorm.io/driver/mysql v1.3.3 // indirect + gorm.io/driver/postgres v1.3.4 // indirect + gorm.io/driver/sqlserver v1.3.2 // indirect + gorm.io/plugin/dbresolver v1.1.0 // indirect + modernc.org/libc v1.15.1 // indirect + modernc.org/mathutil v1.4.1 // indirect + modernc.org/memory v1.0.7 // indirect + modernc.org/sqlite v1.16.0 // indirect ) replace github.com/danielgtaylor/huma => github.com/Fishwaldo/huma v1.8.1-0.20220803065139-017c8e9c60a9 diff --git a/go.sum b/go.sum index e23ba15..904d2f8 100644 --- a/go.sum +++ b/go.sum @@ -53,6 +53,9 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -60,6 +63,9 @@ github.com/Fishwaldo/huma v1.8.1-0.20220803065139-017c8e9c60a9 h1:1ebycopF7/+jEE github.com/Fishwaldo/huma v1.8.1-0.20220803065139-017c8e9c60a9/go.mod h1:2iZhNWUuIywghcke8CRLOBv/AaHN/ZUbjL4Yb1mWlzs= github.com/Jeffail/gabs/v2 v2.6.1 h1:wwbE6nTQTwIMsMxzi6XFQQYRZ6wDc1mSdxoAN+9U4Gk= github.com/Jeffail/gabs/v2 v2.6.1/go.mod h1:xCn81vdHKxFUuWWAaD5jCTQDNPBMh5pPs9IJ+NcziBI= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= @@ -89,6 +95,10 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/casbin/casbin/v2 v2.37.4 h1:RWSKPjaZ8JlOBlcW1bI/FTII8OPxvQ9jVy9JwyNL6DQ= +github.com/casbin/casbin/v2 v2.37.4/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= +github.com/casbin/gorm-adapter/v3 v3.7.4 h1:B8NYKBon149qyE/TciDwdNQU37apM0wAUWuQX1PyR+M= +github.com/casbin/gorm-adapter/v3 v3.7.4/go.mod h1:7mwHmC2phiw6N4gDWlzi+c4DUX7zaVmQC/hINsRgBDg= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -110,6 +120,7 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -129,10 +140,14 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go. github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= github.com/deepmap/oapi-codegen v1.11.0/go.mod h1:k+ujhoQGxmQYBZBbxhOZNZf4j08qv5mC+OH+fFTnKxM= +github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA= +github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU= github.com/dghubble/oauth1 v0.7.1 h1:JjbOVSVVkms9A4h/sTQy5Jb2nFuAAVb2qVYgenJPyrE= github.com/dghubble/oauth1 v0.7.1/go.mod h1:0eEzON0UY/OLACQrmnjgJjmvCGXzjBCsZqL1kWDXtF0= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -167,6 +182,10 @@ github.com/getkin/kin-openapi v0.94.0/go.mod h1:LWZfzOd7PRy8GJ1dJ6mCU6tNdSfOwRac github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= +github.com/glebarez/go-sqlite v1.16.0 h1:h28rHued+hGof3fNLksBcLwz/a71fiGZ/eIJHK0SsLI= +github.com/glebarez/go-sqlite v1.16.0/go.mod h1:i8/JtqoqzBAFkrUTxbQFkQ05odCOds3j7NlDaXjqiPY= +github.com/glebarez/sqlite v1.4.3 h1:ZABNo+2YIau8F8sZ7Qh/1h/ZnlSUMHFGD4zJKPval7A= +github.com/glebarez/sqlite v1.4.3/go.mod h1:FcJlwP9scnxlQ5zxyl0+bn/qFjYcqG4eRvKYhs39QAQ= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= @@ -209,6 +228,8 @@ github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2B github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= @@ -220,12 +241,18 @@ github.com/goccy/go-json v0.9.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG github.com/goccy/go-yaml v1.9.5 h1:Eh/+3uk9kLxG4koCX6lRMAPS1OaMSAi+FJcya0INdB0= github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4= +github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -366,8 +393,10 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/influxdata/influxdb-client-go/v2 v2.9.0/go.mod h1:x7Jo5UHHl+w8wu8UnGiNobDDHygojXwJX4mx7rXGKMk= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= +github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= @@ -375,12 +404,18 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.12.1 h1:rsDFzIpRk7xT4B8FufgpCCeyjdNpKyghZeSefViE5W8= github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +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/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= @@ -388,17 +423,24 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y= github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs= github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw= +github.com/jackc/pgx/v4 v4.16.1 h1:JzTglcal01DrghUqt+PmzWsZx/Yh7SC/CTQmSBMTd0Y= github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= @@ -408,6 +450,7 @@ github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= 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.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= @@ -421,6 +464,7 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X 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/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 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.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= @@ -464,6 +508,7 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -515,6 +560,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= @@ -542,6 +588,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -566,6 +613,8 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/rabbitmq/amqp091-go v1.3.4/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= @@ -585,6 +634,7 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg 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/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= @@ -747,6 +797,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= @@ -759,6 +810,7 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 h1:SLP7Q4Di66FONjDJbCYrCRrh97focO6sLogHO7/g8F0= @@ -846,6 +898,7 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -934,6 +987,7 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -958,6 +1012,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -970,6 +1025,8 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a h1:N2T1jUrTQE9Re6TFF5PhvEHXHCguynGhKjWVsIUt5cY= golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -1043,6 +1100,7 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -1243,11 +1301,23 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/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/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI= +gorm.io/driver/mysql v1.3.3 h1:jXG9ANrwBc4+bMvBcSl8zCfPBaVoPyBEBshA8dA93X8= +gorm.io/driver/mysql v1.3.3/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U= +gorm.io/driver/postgres v1.3.4 h1:evZ7plF+Bp+Lr1mO5NdPvd6M/N98XtwHixGB+y7fdEQ= +gorm.io/driver/postgres v1.3.4/go.mod h1:y0vEuInFKJtijuSGu9e5bs5hzzSzPK+LancpKpvbRBw= gorm.io/driver/sqlite v1.3.6 h1:Fi8xNYCUplOqWiPa3/GuCeowRNBRGTf62DEmhMDHeQQ= gorm.io/driver/sqlite v1.3.6/go.mod h1:Sg1/pvnKtbQ7jLXxfZa+jSHvoX8hoZA8cn4xllOMTgE= +gorm.io/driver/sqlserver v1.3.2 h1:yYt8f/xdAKLY7lCCyXxIUEgZ/WsURos3dHrx8MKFGAk= +gorm.io/driver/sqlserver v1.3.2/go.mod h1:w25Vrx2BG+CJNUu/xKbFhaKlGxT/nzRkhWCCoptX8tQ= +gorm.io/gorm v1.20.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +gorm.io/gorm v1.20.11/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE= gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/plugin/dbresolver v1.1.0 h1:cegr4DeprR6SkLIQlKhJLYxH8muFbJ4SmnojXvoeb00= +gorm.io/plugin/dbresolver v1.1.0/go.mod h1:tpImigFAEejCALOttyhWqsy4vfa2Uh/vAUVnL5IRF7Y= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1257,6 +1327,144 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54= launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.24/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.35.25/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.35.26/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= +modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= +modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= +modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= +modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= +modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= +modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= +modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= +modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= +modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= +modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs= +modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8= +modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE= +modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk= +modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w= +modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE= +modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8= +modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc= +modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU= +modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE= +modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk= +modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI= +modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE= +modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg= +modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74= +modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU= +modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU= +modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc= +modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM= +modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ= +modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84= +modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ= +modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY= +modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= +modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU= +modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko= +modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA= +modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4= +modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0= +modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8= +modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I= +modernc.org/ccgo/v3 v3.15.14/go.mod h1:144Sz2iBCKogb9OKwsu7hQEub3EVgOlyI8wMUPGKUXQ= +modernc.org/ccgo/v3 v3.15.15/go.mod h1:z5qltXjU4PJl0pE5nhYQCvA9DhPHiWsl5GWl89+NSYE= +modernc.org/ccgo/v3 v3.15.16/go.mod h1:XbKRMeMWMdq712Tr5ECgATYMrzJ+g9zAZEj2ktzBe24= +modernc.org/ccgo/v3 v3.15.17/go.mod h1:bofnFkpRFf5gLY+mBZIyTW6FEcp26xi2lgOFk2Rlvs0= +modernc.org/ccgo/v3 v3.15.18/go.mod h1:/2lv3WjHyanEr2sAPdGKRC38n6f0werut9BRXUjjX+A= +modernc.org/ccgo/v3 v3.15.19/go.mod h1:TDJj+DxR26pkDteH2E5WQDj/xlmtsX7JdzkJkaZhOVU= +modernc.org/ccgo/v3 v3.16.2/go.mod h1:w55kPTAqvRMAYS3Lwij6qhqIuBEYS3Z8QtDkjD8cnik= +modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= +modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= +modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= +modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= +modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= +modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= +modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= +modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= +modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= +modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= +modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY= +modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE= +modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg= +modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM= +modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg= +modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo= +modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8= +modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ= +modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA= +modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM= +modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg= +modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE= +modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM= +modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU= +modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw= +modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M= +modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18= +modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8= +modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= +modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0= +modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI= +modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE= +modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY= +modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ= +modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c= +modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI= +modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ= +modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= +modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34= +modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ= +modernc.org/libc v1.14.6/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak= +modernc.org/libc v1.14.7/go.mod h1:f8xfWXW8LW41qb4X5+huVQo5dcfPlq7Cbny2TDheMv0= +modernc.org/libc v1.14.8/go.mod h1:9+JCLb1MWSY23smyOpIPbd5ED+rSS/ieiDWUpdyO3mo= +modernc.org/libc v1.14.10/go.mod h1:y1MtIWhwpJFpLYm6grAThtuXJKEsY6xkdZmXbRngIdo= +modernc.org/libc v1.14.11/go.mod h1:l5/Mz/GrZwOqzwRHA3abgSCnSeJzzTl+Ify0bAwKbAw= +modernc.org/libc v1.14.12/go.mod h1:fJdoe23MHu2ruPQkFPPqCpToDi5cckzsbmkI6Ez0LqQ= +modernc.org/libc v1.15.0/go.mod h1:H1OKCu+NYa9+uQG8WsP7DndMBP61I4PWH8ivWhbdoWQ= +modernc.org/libc v1.15.1 h1:q4wjNNEdw9eceGHEUxklZyPVTkOPHjywI7qKQBj1f/A= +modernc.org/libc v1.15.1/go.mod h1:CoZ2riUhSNTAP4bADwpxkLCyJK9SbbMvle0YRzkRT/I= +modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= +modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= +modernc.org/memory v1.0.6/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.0.7 h1:UE3cxTRFa5tfUibAV7Jqq8P7zRY0OlJg+yWVIIaluEE= +modernc.org/memory v1.0.7/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.16.0 h1:DdvOGaWN0y+X7t2L7RUD63gcwbVjYZjcBZnA68g44EI= +modernc.org/sqlite v1.16.0/go.mod h1:Jwe13ItpESZ+78K5WS6+AjXsUg+JvirsjN3iIDO4C8k= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/tcl v1.11.2/go.mod h1:BRzgpajcGdS2qTxniOx9c/dcxjlbA7p12eJNmiriQYo= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.3.2/go.mod h1:PEU2oK2OEA1CfzDTd+8E908qEXhC9s0MfyKp5LZsd+k= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 2f69a55..1ff755c 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -2,32 +2,39 @@ package auth import ( "time" -// "strings" - "os" - "fmt" - "net/http" + // "strings" "context" "crypto/sha1" + "fmt" + "net/http" + "os" +// "strings" + dbauth "github.com/Fishwaldo/mouthpiece/internal/auth/db" + telegramauth "github.com/Fishwaldo/mouthpiece/internal/auth/telegram" + "github.com/Fishwaldo/mouthpiece/internal/db" . "github.com/Fishwaldo/mouthpiece/internal/log" -// "github.com/Fishwaldo/mouthpiece/internal" - "github.com/Fishwaldo/mouthpiece/internal/user" - "github.com/Fishwaldo/mouthpiece/internal/auth/telegram" "github.com/spf13/viper" pkauth "github.com/go-pkgz/auth" "github.com/go-pkgz/auth/avatar" - //"github.com/go-pkgz/auth/middleware" "github.com/go-pkgz/auth/provider" "github.com/go-pkgz/auth/token" - //"github.com/go-pkgz/auth/logger" + + "github.com/casbin/casbin/v2" + "github.com/casbin/casbin/v2/util" + + //"github.com/casbin/casbin/v2/log" + defaultrolemanager "github.com/casbin/casbin/v2/rbac/default-role-manager" + gormadapter "github.com/casbin/gorm-adapter/v3" "golang.org/x/oauth2/github" ) type Auth struct { - Service *pkauth.Service + Service *pkauth.Service + AuthEnforcer *casbin.Enforcer } var AuthService *Auth @@ -41,6 +48,13 @@ func (AL AuthLogger) Logf(format string, args ...interface{}) { Log.WithName("Auth").Info("Authentication", "message", fmt.Sprintf(format, args...)) } +type AuthConfig struct { + CredChecker func(username string, password string) (ok bool, err error) + MapClaimsToUser token.ClaimsUpdFunc + Validator token.ValidatorFunc + Host string +} + func init() { viper.SetDefault("auth.github.enabled", false) viper.SetDefault("auth.github.signups", true) @@ -50,17 +64,21 @@ func init() { viper.SetDefault("auth.github.fields.FullName", "name") viper.SetDefault("auth.dev.enabled", false) viper.SetDefault("auth.email.enabled", false) + viper.SetDefault("auth.microsoft.signups", false) + viper.SetDefault("auth.secret", "secret") + viper.SetDefault("auth.debug", false) + //viper.SetDefault("auth.avatar.cachedir", os.MkdirTemp("", "mouthpiece_avatar")) } func customGitHubProvider() (cred pkauth.Client, ch provider.CustomHandlerOpt) { - ch = provider.CustomHandlerOpt { + ch = provider.CustomHandlerOpt{ Endpoint: github.Endpoint, - InfoURL: "https://api.github.com/user", + InfoURL: "https://api.github.com/user", MapUserFn: func(data provider.UserData, _ []byte) token.User { userInfo := token.User{ - ID: "github_" + token.HashID(sha1.New(), data.Value(viper.GetString("auth.github.fields.UserName"))), - Name: data.Value(viper.GetString("auth.github.fields.UserName")), - Email: data.Value(viper.GetString("auth.github.fields.Email")), + ID: "github_" + token.HashID(sha1.New(), data.Value(viper.GetString("auth.github.fields.UserName"))), + Name: data.Value(viper.GetString("auth.github.fields.UserName")), + Email: data.Value(viper.GetString("auth.github.fields.Email")), Picture: data.Value(viper.GetString("auth.github.fields.Avatar")), } userInfo.SetStrAttr("fullname", data.Value(viper.GetString("auth.github.fields.FullName"))) @@ -70,66 +88,40 @@ func customGitHubProvider() (cred pkauth.Client, ch provider.CustomHandlerOpt) { Scopes: []string{}, } cred = pkauth.Client{ - Cid: viper.GetString("auth.github.client_id"), + Cid: viper.GetString("auth.github.client_id"), Csecret: viper.GetString("auth.github.client_secret"), } return cred, ch } -func mapClaimsToUser(claims token.Claims) token.Claims { - if claims.User != nil { - if user, err := user.GetUser(claims.User.Name); err != nil { - Log.Info("User not found", "user", claims.User.Name) - claims.User.SetBoolAttr("valid", false) - } else { - claims.User.SetStrAttr("backenduser", user.Username) - claims.User.SetBoolAttr("valid", true) - } - } - fmt.Printf("Update Claims %+v\n", claims) - return claims -} - - -func InitAuth(host string) { +func InitAuth(Config AuthConfig) { AL = &AuthLogger{} AuthService = &Auth{} + var avatarcachedir string + if viper.IsSet("auth.avatar.cachedir") { + avatarcachedir = viper.GetString("auth.avatar.cachedir") + } else { + avatarcachedir, _ = os.MkdirTemp("", "mouthpiece_avatar") + } options := pkauth.Opts{ SecretReader: token.SecretFunc(func(_ string) (string, error) { // secret key for JWT, ignores aud - return "secret", nil + return viper.GetString("auth.secret"), nil }), - TokenDuration: time.Minute, // short token, refreshed automatically - CookieDuration: time.Hour * 24, // cookie fine to keep for long time - DisableXSRF: true, // don't disable XSRF in real-life applications! - Issuer: "my-demo-service", // part of token, just informational - URL: host, // base url of the protected service - AdminPasswd: "password", // admin password - AvatarStore: avatar.NewLocalFS("/tmp/demo-auth-service"), // stores avatars locally + TokenDuration: time.Minute * 5, // short token, refreshed automatically + CookieDuration: time.Hour * 24, // cookie fine to keep for long time + DisableXSRF: true, // don't disable XSRF in real-life applications! + Issuer: "mouthpiece", // part of token, just informational + URL: Config.Host, // base url of the protected service + //AdminPasswd: "password", // admin password + AvatarStore: avatar.NewLocalFS(avatarcachedir), // stores avatars locally AvatarResizeLimit: 200, // resizes avatars to 200x200 - ClaimsUpd: token.ClaimsUpdFunc(mapClaimsToUser), -// Validator: token.ValidatorFunc(func(_ string, claims token.Claims) bool { // rejects some tokens -// if claims.User != nil { -// if strings.HasPrefix(claims.User.ID, "github_") { // allow all users with github auth -// return true -// } -// if strings.HasPrefix(claims.User.ID, "microsoft_") { // allow all users with ms auth -// return true -// } -// if strings.HasPrefix(claims.User.ID, "patreon_") { // allow all users with ms auth -// return true -// } -// if strings.HasPrefix(claims.User.Name, "dev_") { // non-guthub allow only dev_* names -// return true -// } -// return strings.HasPrefix(claims.User.Name, "custom123_") -// } -// return false -// }), - Logger: AL, // optional logger for auth library - UseGravatar: true, // for verified provider use gravatar service + ClaimsUpd: token.ClaimsUpdFunc(Config.MapClaimsToUser), + Validator: Config.Validator, + Logger: AL, // optional logger for auth library + UseGravatar: true, // for verified provider use gravatar service } // create auth service @@ -137,6 +129,18 @@ func InitAuth(host string) { if viper.GetBool("auth.dev.enabled") { Log.Info("Auth Dev Mode Enabled!") AuthService.Service.AddProvider("dev", "", "") + // run dev/test oauth2 server on :8084 + go func() { + devAuthServer, err := AuthService.Service.DevAuth() // peak dev oauth2 server + devAuthServer.GetEmailFn = func(username string) string { + return "admin@example.com" + } + if err != nil { + Log.Error(err, "[PANIC] failed to start dev oauth2 server") + } + devAuthServer.Run(context.Background()) + + }() } if viper.GetBool("auth.github.enabled") { if !viper.IsSet("auth.github.client_id") { @@ -144,15 +148,27 @@ func InitAuth(host string) { } else { if !viper.IsSet("auth.github.client_secret") { Log.Error(nil, "Github auth is enabled but client_secret is not set") - } else { + } else { Log.Info("Auth Github Enabled!") gcred, gch := customGitHubProvider() AuthService.Service.AddCustomProvider("github", gcred, gch) } } } - AuthService.Service.AddProvider("microsoft", os.Getenv("AEXMPL_MS_APIKEY"), os.Getenv("AEXMPL_MS_APISEC")) - + if viper.GetBool("auth.microsoft.enabled") { + Log.Info("Auth Microsoft Enabled!") + AuthService.Service.AddProvider("microsoft", os.Getenv("AEXMPL_MS_APIKEY"), os.Getenv("AEXMPL_MS_APISEC")) + } + /* direct loging (username/password) is always handled */ + dbprovider := dbauth.DirectHandler{ + L: AL, + ProviderName: "direct", + Issuer: options.Issuer, + TokenService: AuthService.Service.TokenService(), + AvatarSaver: AuthService.Service.AvatarProxy(), + CredChecker: Config.CredChecker, + } + AuthService.Service.AddCustomHandler(dbprovider) if viper.GetBool("auth.email.enabled") { Log.Info("Auth Email Enabled!") @@ -191,42 +207,61 @@ func InitAuth(host string) { Log.Error(nil, "Telegram auth is enabled but token is not set") } } - - // run dev/test oauth2 server on :8084 - go func() { - devAuthServer, err := AuthService.Service.DevAuth() // peak dev oauth2 server - devAuthServer.GetEmailFn = func(username string) string { - return username + "@dynam.com" - } - if err != nil { - Log.Error(err, "[PANIC] failed to start dev oauth2 server") - } - devAuthServer.Run(context.Background()) - }() + InitCasbin() Log.Info("Auth service started") } +func InitCasbin() { + cdb, err := gormadapter.NewAdapterByDB(db.Db) + if err != nil { + Log.Error(err, "Failed to Setup Casbin Auth Adapter") + } + AuthService.AuthEnforcer, err = casbin.NewEnforcer("config/auth_model.conf", cdb) + if err != nil { + Log.Error(err, "Failed to setup Casbin") + } + AuthService.AuthEnforcer.EnableLog(viper.GetBool("auth.debug")) + AuthService.AuthEnforcer.EnableAutoSave(true) + AuthService.AuthEnforcer.SetRoleManager(defaultrolemanager.NewRoleManager(10)) + if err := AuthService.AuthEnforcer.LoadModel(); err != nil { + Log.Error(err, "Failed to load Casbin model") + } + + if err := AuthService.AuthEnforcer.LoadPolicy(); err != nil { + Log.Error(err, "Failed to Load Casbin Policy") + } + if !AuthService.AuthEnforcer.AddNamedMatchingFunc("g2", "KeyMatch3", util.KeyMatch3) { + Log.Error(nil, "Failed to add g2 matching function") + } + AuthService.AuthEnforcer.AddPolicy("role:admin", "apigroup:apps", "PUT") + AuthService.AuthEnforcer.AddPolicy("role:user", "apigroup:apps", "GET") + AuthService.AuthEnforcer.AddPolicy("role:admin", "apigroup:message", "PUT") + AuthService.AuthEnforcer.AddPolicy("role:user", "apigroup:message", "GET") + AuthService.AuthEnforcer.AddPolicy("role:admin", "apigroup:users", "PUT") + AuthService.AuthEnforcer.AddPolicy("role:user", "apigroup:users", "GET") + AuthService.AuthEnforcer.AddPolicy("role:admin", "apigroup:transports", "PUT") + AuthService.AuthEnforcer.AddPolicy("role:user", "apigroup:transports", "GET") + + // AuthService.AuthEnforcer.AddPolicy("role:user", "apigroup:apps", "GET") + AuthService.AuthEnforcer.AddRoleForUser("role:admin", "role:user") + + // AuthService.AuthEnforcer.AddRoleForUser("admin", "role:admin") + // AuthService.AuthEnforcer.AddRoleForUser("dev_user", "role:admin") + p, _ := AuthService.AuthEnforcer.GetImplicitPermissionsForUser("admin@example.com") + fmt.Printf("Admin Permissions: %+v\n", p) + + AuthService.AuthEnforcer.SavePolicy() + rm := AuthService.AuthEnforcer.GetPolicy() + Log.Info("Casbin Policy", "policy", rm) + Log.Info("Casbin User Roles", "Roles", AuthService.AuthEnforcer.GetGroupingPolicy()) + Log.Info("Casbin API Groups", "API Groups", AuthService.AuthEnforcer.GetNamedGroupingPolicy("g2")) -// UpdateAuthContext defines interface adding extras or modifying UserInfo in request context -type UpdateAuthContext struct { } - -type CtxUserValue struct{} - -// Update user info in request context from go-pkgz/auth token.User to mouthpiece.User -func (a *UpdateAuthContext) Update() func(http.Handler) http.Handler { - f := func(h http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - // call update only if user info exists, otherwise do nothing - if user, err := token.GetUserInfo(r); err == nil { - /* find out DB User */ - - r = r.WithContext(context.WithValue(r.Context(), CtxUserValue{}, user)) - } - h.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) +func (a *Auth) AddResourceURL(url string, group string) bool { + ok, err := a.AuthEnforcer.AddNamedGroupingPolicy("g2", url, group) + if err != nil { + Log.Error(err, "Failed to add g2 policy", "url", url, "group", group) } - return f -} \ No newline at end of file + return ok +} diff --git a/internal/auth/db/db.go b/internal/auth/db/db.go new file mode 100644 index 0000000..a903e60 --- /dev/null +++ b/internal/auth/db/db.go @@ -0,0 +1,199 @@ +package dbauth + +import ( + "crypto/sha1" //nolint + "crypto/rand" + "encoding/json" + "fmt" + "mime" + "net/http" + "time" + + "github.com/go-pkgz/rest" + "github.com/golang-jwt/jwt" + + "github.com/go-pkgz/auth/logger" + "github.com/go-pkgz/auth/token" + "github.com/go-pkgz/auth/provider" +) + +const ( + // MaxHTTPBodySize defines max http body size + MaxHTTPBodySize = 1024 * 1024 +) + + +type ICredChecker func(user string, password string) (ok bool, err error) + +// DirectHandler implements non-oauth2 provider authorizing user in traditional way with storage +// with users and hashes +type DirectHandler struct { + logger.L + ProviderName string + TokenService provider.TokenService + Issuer string + AvatarSaver provider.AvatarSaver + CredChecker ICredChecker +} + +// credentials holds user credentials +type credentials struct { + User string `json:"user"` + Password string `json:"passwd"` + Audience string `json:"aud"` +} + +// Name of the handler +func (p DirectHandler) Name() string { return p.ProviderName } + +// LoginHandler checks "user" and "passwd" against data store and makes jwt if all passed. +// +// GET /something?user=name&passwd=xyz&aud=bar&sess=[0|1] +// +// POST /something?sess[0|1] +// Accepts application/x-www-form-urlencoded or application/json encoded requests. +// +// application/x-www-form-urlencoded body example: +// user=name&passwd=xyz&aud=bar +// +// application/json body example: +// { +// "user": "name", +// "passwd": "xyz", +// "aud": "bar", +// } +func (p DirectHandler) LoginHandler(w http.ResponseWriter, r *http.Request) { + creds, err := p.getCredentials(w, r) + if err != nil { + rest.SendErrorJSON(w, r, p.L, http.StatusBadRequest, err, "failed to parse credentials") + return + } + sessOnly := r.URL.Query().Get("sess") == "1" + ok := true + ok, err = p.CredChecker(creds.User, creds.Password) + if err != nil { + rest.SendErrorJSON(w, r, p.L, http.StatusInternalServerError, err, "failed to check user credentials") + return + } + if !ok { + rest.SendErrorJSON(w, r, p.L, http.StatusForbidden, nil, "incorrect user or password") + return + } + + userID := p.ProviderName + "_" + token.HashID(sha1.New(), creds.User) + + u := token.User{ + Name: creds.User, + ID: userID, + Email: creds.User, + } + u, err = setAvatar(p.AvatarSaver, u, &http.Client{Timeout: 5 * time.Second}) + if err != nil { + rest.SendErrorJSON(w, r, p.L, http.StatusInternalServerError, err, "failed to save avatar to proxy") + return + } + + cid, err := randToken() + if err != nil { + rest.SendErrorJSON(w, r, p.L, http.StatusInternalServerError, err, "can't make token id") + return + } + + claims := token.Claims{ + User: &u, + StandardClaims: jwt.StandardClaims{ + Id: cid, + Issuer: p.Issuer, + Audience: creds.Audience, + }, + SessionOnly: sessOnly, + } + + if _, err = p.TokenService.Set(w, claims); err != nil { + rest.SendErrorJSON(w, r, p.L, http.StatusInternalServerError, err, "failed to set token") + return + } + rest.RenderJSON(w, claims.User) +} + +// getCredentials extracts user and password from request +func (p DirectHandler) getCredentials(w http.ResponseWriter, r *http.Request) (credentials, error) { + + // GET /something?user=name&passwd=xyz&aud=bar + if r.Method == "GET" { + return credentials{ + User: r.URL.Query().Get("user"), + Password: r.URL.Query().Get("passwd"), + Audience: r.URL.Query().Get("aud"), + }, nil + } + + if r.Method != "POST" { + return credentials{}, fmt.Errorf("method %s not supported", r.Method) + } + + if r.Body != nil { + r.Body = http.MaxBytesReader(w, r.Body, MaxHTTPBodySize) + } + contentType := r.Header.Get("Content-Type") + if contentType != "" { + mt, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) + if err != nil { + return credentials{}, err + } + contentType = mt + } + + // POST with json body + if contentType == "application/json" { + var creds credentials + if err := json.NewDecoder(r.Body).Decode(&creds); err != nil { + return credentials{}, fmt.Errorf("failed to parse request body: %w", err) + } + return creds, nil + } + + // POST with form + if err := r.ParseForm(); err != nil { + return credentials{}, fmt.Errorf("failed to parse request: %w", err) + } + + return credentials{ + User: r.Form.Get("user"), + Password: r.Form.Get("passwd"), + Audience: r.Form.Get("aud"), + }, nil +} + +// AuthHandler doesn't do anything for direct login as it has no callbacks +func (p DirectHandler) AuthHandler(w http.ResponseWriter, r *http.Request) {} + +// LogoutHandler - GET /logout +func (p DirectHandler) LogoutHandler(w http.ResponseWriter, r *http.Request) { + p.TokenService.Reset(w) +} + +// setAvatar saves avatar and puts proxied URL to u.Picture +func setAvatar(ava provider.AvatarSaver, u token.User, client *http.Client) (token.User, error) { + if ava != nil { + avatarURL, e := ava.Put(u, client) + if e != nil { + return u, fmt.Errorf("failed to save avatar for: %w", e) + } + u.Picture = avatarURL + return u, nil + } + return u, nil // empty AvatarSaver ok, just skipped +} + +func randToken() (string, error) { + b := make([]byte, 32) + if _, err := rand.Read(b); err != nil { + return "", fmt.Errorf("can't get random: %w", err) + } + s := sha1.New() + if _, err := s.Write(b); err != nil { + return "", fmt.Errorf("can't write randoms to sha1: %w", err) + } + return fmt.Sprintf("%x", s.Sum(nil)), nil +} \ No newline at end of file diff --git a/internal/db/db.go b/internal/db/db.go index ead5c74..286038c 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -4,6 +4,7 @@ import ( _ "fmt" "gorm.io/gorm" "gorm.io/driver/sqlite" + . "github.com/Fishwaldo/mouthpiece/internal/log" ) var Db *gorm.DB @@ -14,4 +15,5 @@ func InitializeDB() { if err != nil { panic(err) } + Log.Info("Database Initialized") } \ No newline at end of file diff --git a/internal/errors/errors.go b/internal/errors/errors.go index 0f6eddc..f15e3fe 100644 --- a/internal/errors/errors.go +++ b/internal/errors/errors.go @@ -7,4 +7,5 @@ import ( var ( ErrAppExists = errors.New("App Already Exists") ErrAppNotFound = errors.New("App Not Found") + ErrUserNotFound = errors.New("User Not Found") ) \ No newline at end of file diff --git a/internal/middleware/accesscontrol.go b/internal/middleware/accesscontrol.go new file mode 100644 index 0000000..46a9eef --- /dev/null +++ b/internal/middleware/accesscontrol.go @@ -0,0 +1,55 @@ +package middleware + +import ( + "context" +// "fmt" + "net/http" + + "github.com/Fishwaldo/mouthpiece/internal/auth" + . "github.com/Fishwaldo/mouthpiece/internal/log" + "github.com/Fishwaldo/mouthpiece/internal/user" + + "github.com/danielgtaylor/huma" + "github.com/go-pkgz/auth/token" +) + +// UpdateAuthContext defines interface adding extras or modifying UserInfo in request context +type Middleware struct { +} + + +type CtxUserValue struct{} + +// Update user info in request context from go-pkgz/auth token.User to mouthpiece.User +func (a *Middleware) Update() func(http.Handler) http.Handler { + f := func(h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + // call update only if user info exists, otherwise do nothing + if tknuser, err := token.GetUserInfo(r); err == nil { + /* find out DB User */ + if dbUser, err := user.GetUser(tknuser.Email); err != nil { + Log.Info("DBUser Not Found", "token", tknuser, "error", err) + ctx := huma.ContextFromRequest(w, r) + /* do Something */ + ctx.WriteError(http.StatusForbidden, "User not found", err) + return + } else { + ok, res, err := auth.AuthService.AuthEnforcer.EnforceEx(dbUser.Email, r.URL.Path, r.Method) + Log.V(1).Info("Access Control", "result", ok, "Policy", res, "Error", err) + if (!ok) { + huma.ContextFromRequest(w, r).WriteError(http.StatusForbidden, "Access Denied", err) + return; + } + r = r.WithContext(context.WithValue(r.Context(), CtxUserValue{}, tknuser)) + } + h.ServeHTTP(w, r) + return; + } else { + ctx := huma.ContextFromRequest(w, r) + ctx.WriteError(http.StatusUnauthorized, "Access Denied") + } + } + return http.HandlerFunc(fn) + } + return f +} \ No newline at end of file diff --git a/internal/user/auth.go b/internal/user/auth.go new file mode 100644 index 0000000..ba1a484 --- /dev/null +++ b/internal/user/auth.go @@ -0,0 +1,51 @@ +package user + +import ( + "strings" + + "github.com/Fishwaldo/mouthpiece/internal/errors" + . "github.com/Fishwaldo/mouthpiece/internal/log" + "github.com/go-pkgz/auth/token" + +) + +func dbAuthProvider(user, pass string) (ok bool, err error) { + user = strings.TrimSpace(user) + Log.Info("Direct Login", "user", user, "pass", pass) + dbUser, err := GetUser(user) + Log.Info("User", "user", dbUser, "error", err) + + if err == mperror.ErrUserNotFound { + Log.Info("User not found", "user", user) + return false, nil + } + if !dbUser.CheckPassword(pass) { + Log.Info("Password Invalid", "user", user) + return false, nil + } + return true, nil +} + +// Called when the Tokens are created/refreshed. +func MapClaimsToUser(claims token.Claims) token.Claims { + Log.Info("Map Claims To User", "claims", claims) +// if claims.User != nil { +// if user, err := GetUser(claims.User.Name); err != nil { +// Log.Info("User not found", "user", claims.User.Name) +// claims.User.SetBoolAttr("valid", false) +// } else { +// claims.User.SetStrAttr("backenduser", user.Username) +// claims.User.SetBoolAttr("valid", true) +// } +// } + return claims +} + +// called on every access to the API +func UserValidator(token string, claims token.Claims) (bool) { + Log.Info("User Validator", "token", token, "claims", claims) + if claims.User != nil { + return true + } + return false +} \ No newline at end of file diff --git a/internal/user/users.go b/internal/user/users.go index 60bf1ec..52843e9 100644 --- a/internal/user/users.go +++ b/internal/user/users.go @@ -1,24 +1,76 @@ package user import ( - "errors" + "fmt" . "github.com/Fishwaldo/mouthpiece/internal/log" "github.com/Fishwaldo/mouthpiece/internal/message" "github.com/Fishwaldo/mouthpiece/internal/transport" "github.com/Fishwaldo/mouthpiece/internal/db" + "github.com/Fishwaldo/mouthpiece/internal/auth" + "github.com/Fishwaldo/mouthpiece/internal/errors" + + "gorm.io/gorm" "gorm.io/gorm/clause" + "github.com/go-playground/validator/v10" ) type User struct { gorm.Model `json:"-"` - Username string - FirstName string - LastName string - Email string - TransportConfigs []transport.TransportConfig `json:"transports,omitempty" gorm:"many2many:user_transports;"` + ID uint `gorm:"primarykey"` + Email string `validate:"required,email"` + FirstName string `validate:"required"` + LastName string `validate:"required"` + Password string `json:"-" writeOnly:"true" validate:"required"` + TransportConfigs []transport.TransportConfig `json:"transports,omitempty" gorm:"many2many:user_transports;" validate:"-"` +} + +var AuthConfig auth.AuthConfig +func init() { + AuthConfig = auth.AuthConfig { + CredChecker: dbAuthProvider, + MapClaimsToUser: MapClaimsToUser, + Validator: UserValidator, + } +} + + +func CreateUser(user *User) error { + validate := validator.New() + if err := validate.Struct(user); err != nil { + Log.Info("User Validation Error", "Error", err) + return err; + } + tx := db.Db.Create(&user) + if tx.Error != nil { + return tx.Error + } + /* New Users all Start with User Role */ + if user, err := GetUser(user.Email); err == nil { + user.addUserRole("user") + return nil + } else { + return err + } +} + +func (u *User) addUserRole(role string) bool { + _, err := auth.AuthService.AuthEnforcer.AddRoleForUser(u.Email, fmt.Sprintf("role:%s", role)) + if err != nil { + Log.Info("Failed to add role for user", "email", u.Email, "role", role, "error", err) + return false + } + return true; +} + +func (u *User) CheckPassword(string) bool { + return true +} + +func (u *User) SetPassword(string) error { + return nil } func InitializeUsers() { @@ -27,14 +79,15 @@ func InitializeUsers() { db.Db.Model(&User{}).Count(&count) Log.V(1).Info("Initializing Users", "count", count) if (count == 0) { - admin := &User{Username: "admin", FirstName: "Admin", LastName: "User", Email: "admin@example.com"} - user := &User{Username: "user", FirstName: "User", LastName: "User", Email: "user@example.com"} - t, _ := transport.GetTransport("stdout") - t.NewTransportConfig() - t.NewTransportConfig() - Log.Info("Admin User", "user", admin) - db.Db.Create(admin) - db.Db.Create(user) + Log.Info("Creating Default Users") + admin := &User{FirstName: "Admin", LastName: "User", Email: "admin@example.com", Password: "password"} + if err := CreateUser(admin); err == nil { + admin.addUserRole("admin") + Log.Info("Created Default Admin admin@example.com") + } + if err := CreateUser(&User{FirstName: "User", LastName: "User", Email: "user@example.com", Password: "password"}); err == nil { + Log.Info("Created Default User user@example.com") + } } } @@ -44,21 +97,29 @@ func GetUsers() []User { return users } -func GetUser(username string) (user *User, err error) { - tx := db.Db.Preload(clause.Associations).First(&user, "username = ?", username) +func GetUser(email string) (user *User, err error) { + tx := db.Db.Preload(clause.Associations).First(&user, "email = ?", email) if tx.Error == gorm.ErrRecordNotFound { - return nil, errors.New("User Not Found") + return nil, mperror.ErrUserNotFound + } + return +} +func GetUserByID(id uint) (user *User, err error) { + tx := db.Db.Preload(clause.Associations).First(&user, "ID = ?", id) + if tx.Error == gorm.ErrRecordNotFound { + return nil, mperror.ErrUserNotFound } return } + + func (u User) ProcessMessage(msg msg.Message) (err error) { /* add User Fields to Message */ - msg.Body.Fields["username"] = u.Username msg.Body.Fields["first_name"] = u.FirstName msg.Body.Fields["last_name"] = u.LastName msg.Body.Fields["email"] = u.Email - Log.V(1).Info("User Processing Message", "User", u.Username, "MessageID", msg.ID) + Log.V(1).Info("User Processing Message", "Email", u.Email, "MessageID", msg.ID) for _, tc := range u.TransportConfigs { t, err := transport.GetTransport(tc.Transport); if err != nil { diff --git a/main.go b/main.go index 2c9ba16..177cd37 100644 --- a/main.go +++ b/main.go @@ -41,6 +41,7 @@ import ( "github.com/Fishwaldo/mouthpiece/internal/transport" "github.com/Fishwaldo/mouthpiece/internal/user" "github.com/Fishwaldo/mouthpiece/internal/app" + "github.com/Fishwaldo/mouthpiece/internal/middleware" . "github.com/Fishwaldo/mouthpiece/internal/log" @@ -102,9 +103,9 @@ func main() { } // Create a new router & CLI with default middleware. InitLogger() + db.InitializeDB() humucli := cli.NewRouter("MouthPiece", "0.0.1") humucli.DisableSchemaProperty() - humucli.PreStart(db.InitializeDB) humucli.PreStart(transport.InitializeTransports) humucli.PreStart(msg.InitializeMessage) humucli.PreStart(user.InitializeUsers) @@ -117,18 +118,10 @@ func main() { humucli.GatewayAuthCode("mouthpiece2", "/oauth2/token", "/oauth2/token", nil) humucli.GatewayBasicAuth("basic") - - auth.InitAuth(fmt.Sprintf("http://arm64-1.dmz.dynam.ac:%v", viper.Get("Port"))) + user.AuthConfig.Host = fmt.Sprintf("http://arm64-1.dmz.dynam.ac:%v", viper.Get("Port")) + auth.InitAuth(user.AuthConfig) m := auth.AuthService.Service.Middleware() - p := auth.UpdateAuthContext{} - authRoutes, avaRoutes := auth.AuthService.Service.Handlers() - mux := humucli.Resource("/").GetMux() - mux.Mount("/auth", authRoutes) - mux.Mount("/avatar", avaRoutes) - - fileServer(mux, "/frontend", http.Dir("frontend")) - - + p := middleware.Middleware{} // Declare the root resource and a GET operation on it. humucli.Resource("/health").Get("get-health", "Get Health of the Service", @@ -147,6 +140,10 @@ func main() { ctx.WriteModel(status, test) }) v1api := humucli.Resource("/v1") + v1api.Middleware(m.Trace) + v1api.Middleware(p.Update()) + + auth.AuthService.AddResourceURL("/v1/message/{application}", "apigroup:message") v1api.SubResource("/message/{application}").Post("post-message", "Post Message to the Service", responses.OK().ContentType("application/json"), responses.OK().Model(&msg.MessageResult{}), @@ -164,24 +161,17 @@ func main() { ctx.WriteError(http.StatusNotFound, "Application Not Found") } }) + + + auth.AuthService.AddResourceURL("/v1/apps/", "apigroup:apps") appapi := v1api.SubResource("/apps/") - - appapi.Middleware(m.Auth) - appapi.Middleware(p.Update()) - appapi.Get("get-apps", "Get A List of Applications", responses.OK().ContentType("application/json"), responses.OK().Headers("Set-Cookie"), responses.OK().Model([]app.App{}), - ).Run(func(ctx huma.Context) { -// printContextInternals(ctx, false) -// fmt.Printf("%+v\n", reflect.TypeOf(contextKey("user"))) - fmt.Printf("Normal %+v\n", ctx.Value(auth.CtxUserValue{})) -// fmt.Printf("User: %+v\n", ctx.Value(contextKey("user"))) -// fmt.Printf("User: %+v\n", getUserFromContext(ctx)) + ).Run(func(ctx huma.Context) { ctx.WriteModel(http.StatusOK, app.GetApps()) }) - appapi.Put("create-app", "Create a Application", responses.OK().ContentType("application/json"), responses.OK().Headers("Set-Cookie"), @@ -197,22 +187,29 @@ func main() { ctx.WriteModel(http.StatusOK, app) } }) + + auth.AuthService.AddResourceURL("/v1/users/", "apigroup:users") userapi := v1api.SubResource("/users/") userapi.Get("get-users", "Get A List of Users", responses.OK().ContentType("application/json"), + responses.OK().Headers("Set-Cookie"), responses.OK().Model([]user.User{}), ).Run(func(ctx huma.Context) { ctx.WriteModel(http.StatusOK, user.GetUsers()) }) - usertransports := v1api.SubResource("/users/{user}/transports/") + + + auth.AuthService.AddResourceURL("/v1/users/{userid}/transports/", "apigroup:users") + usertransports := v1api.SubResource("/users/{userid}/transports/") usertransports.Get("get-user-transports", "Get A List of Transports for a User", responses.OK().ContentType("application/json"), + responses.OK().Headers("Set-Cookie"), responses.OK().Model([]string{}), responses.NotFound().ContentType("application/json"), ).Run(func(ctx huma.Context, input struct { - User string `path:"user"` + User uint `path:"userid"` }) { - if user, err := user.GetUser(input.User); err != nil { + if user, err := user.GetUserByID(input.User); err != nil { ctx.WriteError(http.StatusNotFound, "User Not Found", err) } else { var transport []string @@ -222,16 +219,18 @@ func main() { ctx.WriteModel(http.StatusOK, transport) } }) - usertransportdetails := v1api.SubResource("/users/{user}/transports/{transport}/") + auth.AuthService.AddResourceURL("/v1/users/{userid}/transports/{transportid}/", "apigroup:users") + usertransportdetails := v1api.SubResource("/users/{userid}/transports/{transportid}/") usertransportdetails.Get("get-user-transport-details", "Get Details for a Transport for a User", responses.OK().ContentType("application/json"), + responses.OK().Headers("Set-Cookie"), responses.OK().Model(transport.TransportConfig{}), responses.NotFound().ContentType("application/json"), ).Run(func(ctx huma.Context, input struct { - User string `path:"user"` - Transport string `path:"transport"` + User uint `path:"userid"` + Transport string `path:"transportid"` }) { - if user, err := user.GetUser(input.User); err != nil { + if user, err := user.GetUserByID(input.User); err != nil { ctx.WriteError(http.StatusNotFound, "User Not Found", err) } else { ok := false @@ -246,14 +245,25 @@ func main() { } } }) + auth.AuthService.AddResourceURL("/v1/transports/", "apigroup:transports") transportapi := v1api.SubResource("/transports/") transportapi.Get("get-transports", "Get A List of Transports", responses.OK().ContentType("application/json"), + responses.OK().Headers("Set-Cookie"), responses.OK().Model([]string{}), ).Run(func(ctx huma.Context) { ctx.WriteModel(http.StatusOK, transport.GetTransports()) }) + + authRoutes, avaRoutes := auth.AuthService.Service.Handlers() + mux := humucli.Resource("/").GetMux() + mux.Mount("/auth", authRoutes) + mux.Mount("/avatar", avaRoutes) + + fileServer(mux, "/static", http.Dir("frontend")) + + // Run the CLI. When passed no arguments, it starts the server. humucli.Run() }