diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index a2ce761..4b3c9b3 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -19,7 +19,8 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version-file: go.mod + go-version: '1.26' + check-latest: true - name: Go Generate run: go generate - name: Run coverage diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3a18522..8453707 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,7 +22,8 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version-file: go.mod + go-version: '1.26' + check-latest: true cache: true - name: Generate APIs and run tests diff --git a/.gitignore b/.gitignore index bbd1446..16ef15c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,8 +20,4 @@ target/ dist/ __debug_bin codegen/ - -node_modules/ -dist/ .idea -.package-lock.json \ No newline at end of file diff --git a/README.md b/README.md index 0441463..9a40b69 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Three things about it are easy to get wrong. - **Registration is gated by a one-shot key.** `GET /v1/users/status` returns a fresh UUID as `key` only while the user count is zero. Register must present that key, and it is deleted on success. The keys live in a map in memory, so a restart discards any that were handed out and not used. - **`GET /v1/users/image` is deliberately reachable without a token,** because the login page shows the avatar before anyone has one. It is confined instead: the cleaned absolute path must contain the configured user data path and match `^/var/lib/casaos/\d`. -Everything else under `/v1/users` sits behind JWT middleware, which skips the check for requests from `127.0.0.1` or `::1` — upstream behaviour, unchanged here. Login, token refresh and the username list are open by necessity. The `/v2/users` API has nothing to do with accounts: it lists and deletes the events shown in the dashboard's notification list. The events themselves arrive over a websocket subscription to CasaOS-MessageBus (`route/event_listen.go`), not through this API. +Everything else under `/v1/users`, and all of `/v2/users`, sits behind JWT middleware. It skips the check only for this box's other services: a request from `127.0.0.1` or `::1` that also carries `Authorization: Internal `, the secret the gateway writes to `/var/run/casaos/internal.secret` at every boot (see "Loopback is not an identity" below). Loopback alone gets no pass. Login, token refresh and the username list are open by necessity. The `/v2/users` API has nothing to do with accounts: it lists and deletes the events shown in the dashboard's notification list. The events themselves arrive over a websocket subscription to CasaOS-MessageBus (`route/event_listen.go`), not through this API. Configuration is `/etc/casaos/user-service.conf`, written from an embedded sample on first run. It points at the SQLite database `/var/lib/casaos/db/user.db`, at per-user files under `/var/lib/casaos//`, and at the log `/var/log/casaos/user-service.log`. The binary is installed as `/usr/bin/casaos-user-service` and runs under `casaos-user-service.service`. Run it as `casaos-user-service -ru -user ` to reset a forgotten password to a random one, which it prints. diff --git a/go.mod b/go.mod index c84ac5e..3493b7f 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ module github.com/ReCasaOS/CasaOS-UserService -go 1.26.0 +go 1.26.8 require ( - github.com/ReCasaOS/CasaOS-Common v0.4.25 + github.com/ReCasaOS/CasaOS-Common v0.4.26 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/deepmap/oapi-codegen v1.12.4 - github.com/getkin/kin-openapi v0.117.0 + github.com/getkin/kin-openapi v0.149.0 github.com/glebarez/sqlite v1.8.0 github.com/labstack/echo-jwt/v4 v4.4.0 github.com/labstack/echo/v4 v4.15.4 @@ -29,31 +29,28 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/glebarez/go-sqlite v1.21.1 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/jsonpointer v0.22.5 // indirect + github.com/go-openapi/swag/jsonname v0.25.5 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/google/uuid v1.5.0 // indirect github.com/gorilla/mux v1.8.0 // indirect - github.com/invopop/yaml v0.2.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/labstack/gommon v0.5.0 // indirect - github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.15 // indirect github.com/mattn/go-isatty v0.0.22 // indirect - github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/oasdiff/yaml v0.1.1 // indirect + github.com/oasdiff/yaml3 v0.0.14 // indirect github.com/orca-zhang/ecache v1.1.3 // indirect - github.com/perimeterx/marshmallow v1.1.4 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/samber/lo v1.38.1 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect - github.com/ugorji/go/codec v1.2.11 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect go.uber.org/atomic v1.10.0 // indirect @@ -62,7 +59,6 @@ require ( golang.org/x/sys v0.48.0 // indirect golang.org/x/text v0.42.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/libc v1.22.4 // indirect modernc.org/mathutil v1.5.0 // indirect modernc.org/memory v1.5.0 // indirect diff --git a/go.sum b/go.sum index b8c2167..5ada4a8 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= -github.com/ReCasaOS/CasaOS-Common v0.4.25 h1:QWN8jXUyo8kwK5jbPVVBSYPQCxgcLWnXExiuVn7Dl6I= -github.com/ReCasaOS/CasaOS-Common v0.4.25/go.mod h1:tOdXqfe3OdC5DRJ+9vPkDeeadEN9TPSWjrJdtTeQ5hw= +github.com/ReCasaOS/CasaOS-Common v0.4.26 h1:aefnl58xQ4z0ga0+vBnQHmkG1/3fnd1C1W/EBFeAgvc= +github.com/ReCasaOS/CasaOS-Common v0.4.26/go.mod h1:HjYCcRAVDa0c/IncJDof1sddCpze2esCTZt941Sk7LI= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/benbjohnson/clock v1.3.1 h1:Heo0FGXzOxUHquZbraxt+tT7UXVDhesUQH5ISbsOkCQ= @@ -20,34 +20,34 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/deepmap/oapi-codegen v1.12.4 h1:pPmn6qI9MuOtCz82WY2Xaw46EQjgvxednXXrP7g5Q2s= github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGnFiDaOaKKl5yas= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/getkin/kin-openapi v0.117.0 h1:QT2DyGujAL09F4NrKDHJGsUoIprlIcFVHWDVDcUFE8A= -github.com/getkin/kin-openapi v0.117.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/getkin/kin-openapi v0.149.0 h1:ZbhmVJ4yq5RZDUsyP8lcBcGMsjsaTqXEFt6isdtMDfA= +github.com/getkin/kin-openapi v0.149.0/go.mod h1:1+BHDzstro+P5CKtPy1X4PfofnFgmRe6uvMy9+r9fKY= github.com/glebarez/go-sqlite v1.21.1 h1:7MZyUPh2XTrHS7xNEHQbrhfMZuPSzhkm2A1qgg0y5NY= github.com/glebarez/go-sqlite v1.21.1/go.mod h1:ISs8MF6yk5cL4n/43rSOmVMGJJjHYr7L2MbZZ5Q4E2E= github.com/glebarez/sqlite v1.8.0 h1:02X12E2I/4C1n+v90yTqrjRa8yuo7c3KeHI3FRznCvc= github.com/glebarez/sqlite v1.8.0/go.mod h1:bpET16h1za2KOOMb8+jCp6UBP/iahDpfPQqSaYLTLx8= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= +github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0= +github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= +github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= +github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM= +github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-redis/redis/v7 v7.4.1/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -73,18 +73,12 @@ github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= -github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= -github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -97,18 +91,16 @@ github.com/labstack/echo/v4 v4.15.4 h1:DL45vVYa+BWE+XuW+zZNd9H0YEdZ80UAWJGcTVW4E github.com/labstack/echo/v4 v4.15.4/go.mod h1:CuMetKIRwsuO/qlAgMq+KTAalwGoB/h4tC+yPdrTj1g= github.com/labstack/gommon v0.5.0 h1:6VSQ2NOzsnEJ5W6+84E0RbcaDDmgB6NIAzWCczTEe6c= github.com/labstack/gommon v0.5.0/go.mod h1:Rzlg7HHy1maLfzBYGg9NZcVuz1sA68HHhLjhcEllYE0= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY= github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oasdiff/yaml v0.1.1 h1:6nHx+pn9gBRM6YpBlFZFQGCCd1nuvqOBtTD3KKTgGxY= +github.com/oasdiff/yaml v0.1.1/go.mod h1:EYJNoyktvWMJ0Hmhx+6qTaqMOsalUaRGT8Sj1hNcegU= +github.com/oasdiff/yaml3 v0.0.14 h1:aLJee3hxBK2H5wdXd9iPcIXb93Nty1Ge0pT171eHtkw= +github.com/oasdiff/yaml3 v0.0.14/go.mod h1:csto2xfDjYccdUn/yw/bPjj/cYTdp6HtFA0J4TWG+gg= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= @@ -119,8 +111,6 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/orca-zhang/ecache v1.1.3 h1:cnmEER72jEOzZ6+XCp++1JWs/lJ3vFTynTVcdXx5qHg= github.com/orca-zhang/ecache v1.1.3/go.mod h1:1/6HCddx1iAbafDyRdddq5v/0rZf5sCU/fkLZ3BhciQ= -github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= -github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -136,17 +126,14 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 h1:1EYB5IzjZawrrnELUi78f9fPu57HuXjmddZPjrls/28= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.3/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= @@ -156,10 +143,6 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 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/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= @@ -232,7 +215,6 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -247,8 +229,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -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/gorm v1.25.0 h1:+KtYtb2roDz14EQe4bla8CbQlmb9dN3VejSai3lprfU= diff --git a/package.json b/package.json deleted file mode 100644 index f2530e7..0000000 --- a/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "casaos-userservice-openapi", - "version": "0.0.1", - "scripts": { - "build": "rm -rf dist && tsc && rm -rf generate", - "generate:local": "openapi-generator-cli generate -g typescript-axios -i ./api/user-service/openapi.yaml -o ./generate", - "generate:npx": "npx @openapitools/openapi-generator-cli generate -g typescript-axios -i ./api/user-service/openapi.yaml -o ./generate", - "generate:ts": "npx openapi-typescript-codegen --input ./api/user-service/openapi.yaml --output ./generate", - "publish": "npm publish --access public", - "start": "yarn generate:ts && yarn build" - }, - "homepage": "https://github.com/ReCasaOS/CasaOS-UserService#readme", - "description": "Casaos-Localstorage Typescript+Axios SDK", - "keywords": [ - "CasaOS-UserService", - "SDK", - "CasaOS-UserService Axios" - ], - "main": "dist/index", - "files": [ - "LICENSE", - "README.md", - "dist" - ], - "dependencies": { - "axios": "^1.1.0" - }, - "devDependencies": { - "@openapitools/openapi-generator-cli": "2.5.2", - "@types/node": "^18.8.3", - "openapi-typescript-codegen": "^0.23.0", - "typescript": "^4.9.5" - }, - "author": "casaos", - "license": "Apache-2.0" -} diff --git a/route/v1_2fa_test.go b/route/v1_2fa_test.go index 0f9e851..6e99383 100644 --- a/route/v1_2fa_test.go +++ b/route/v1_2fa_test.go @@ -46,9 +46,9 @@ type client struct { h http.Handler } -// do sends a JSON request from a non-loopback address (the JWT middleware -// skips auth for 127.0.0.1) with the raw token in Authorization, as the -// dashboard does. +// do sends a JSON request from a non-loopback address with the raw token in +// Authorization, as the dashboard does. Off loopback, the JWT middleware's +// skip (loopback plus the internal secret) can never apply. func (c client) do(method, path, token string, body interface{}) (int, result) { c.t.Helper() var buf bytes.Buffer diff --git a/route/v2_test.go b/route/v2_test.go new file mode 100644 index 0000000..a0b2262 --- /dev/null +++ b/route/v2_test.go @@ -0,0 +1,83 @@ +package route_test + +import ( + "net/http/httptest" + "os" + "path/filepath" + "testing" + + "github.com/ReCasaOS/CasaOS-Common/external" + "github.com/ReCasaOS/CasaOS-UserService/model" + "github.com/ReCasaOS/CasaOS-UserService/pkg/config" + "github.com/ReCasaOS/CasaOS-UserService/pkg/utils/encryption" + "github.com/ReCasaOS/CasaOS-UserService/route" + "github.com/ReCasaOS/CasaOS-UserService/service" + model2 "github.com/ReCasaOS/CasaOS-UserService/service/model" + "github.com/glebarez/sqlite" + "gorm.io/gorm" +) + +// eventsRepo is the rig's repository with a real event store, so a request +// that clears both middlewares reaches its handler. +type eventsRepo struct { + fakeRepo + events service.EventService +} + +func (r eventsRepo) Event() service.EventService { return r.events } + +// TestV2AuthThenValidation pins the v2 middleware chain: the JWT check runs +// before the OpenAPI validator, the validator still resolves routes and +// checks parameters against the spec, and a valid request, from the user or +// from one of this box's services (loopback plus the internal secret), gets +// through both to its handler. Only the handler answers 200. +func TestV2AuthThenValidation(t *testing.T) { + users, c := newRig(t) + users.CreateUser(model2.UserDBModel{Username: "admin", Password: encryption.GetMD5ByStr("pw"), Role: "admin"}) + access, _ := tokens(t, c.expect(200, 200, "POST", "/v1/users/login", "", map[string]string{"username": "admin", "password": "pw"})) + + db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) + if err != nil { + t.Fatal(err) + } + sqlDB, _ := db.DB() + sqlDB.SetMaxOpenConns(1) + if err := db.AutoMigrate(&model.EventModel{}); err != nil { + t.Fatal(err) + } + service.MyService = eventsRepo{fakeRepo{user: users}, service.NewEventService(db)} + + dir := t.TempDir() + if err := os.WriteFile(filepath.Join(dir, external.InternalSecretFilename), []byte("s3cret\n"), 0o600); err != nil { + t.Fatal(err) + } + defer func(old string) { config.CommonInfo.RuntimePath = old }(config.CommonInfo.RuntimePath) + config.CommonInfo.RuntimePath = dir + + const lan, loopback, secret = "10.0.0.1:1234", "127.0.0.1:1234", "Internal s3cret" + h := route.InitV2Router() + for i, tc := range []struct { + from, path, auth string + want int + }{ + {lan, "/v2/users/nope", "", 401}, // the validator would say 400: auth runs first + {lan, "/v2/users/events?form=yesterday", "", 401}, // same with a known route + {lan, "/v2/users/nope", access, 400}, // unknown route + {lan, "/v2/users/events?form=yesterday", access, 400}, // form is not a date-time + {lan, "/v2/users/events?form=2021-01-01T00:00:00Z", access, 200}, // the user: valid, reaches the handler + {lan, "/v2/users/events", secret, 401}, // the secret counts from loopback only + {loopback, "/v2/users/events", "", 401}, // loopback alone is not trusted + {loopback, "/v2/users/events?form=2021-01-01T00:00:00Z", secret, 200}, // a service: valid, reaches the handler + } { + req := httptest.NewRequest("GET", tc.path, nil) + req.RemoteAddr = tc.from + if tc.auth != "" { + req.Header.Set("Authorization", tc.auth) + } + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + if rec.Code != tc.want { + t.Errorf("case %d, GET %s from %s: got %d, want %d", i, tc.path, tc.from, rec.Code, tc.want) + } + } +} diff --git a/service/service.go b/service/service.go index f707f21..cd710c0 100644 --- a/service/service.go +++ b/service/service.go @@ -63,7 +63,7 @@ func (c *store) MessageBus() *message_bus.ClientWithResponses { c.Server = messageBusAddress return nil - }) + }, message_bus.WithRequestEditorFn(external.InternalRequestEditor(config.CommonInfo.RuntimePath))) return client } diff --git a/service/service_test.go b/service/service_test.go new file mode 100644 index 0000000..c09d51e --- /dev/null +++ b/service/service_test.go @@ -0,0 +1,45 @@ +package service + +import ( + "context" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + + "github.com/ReCasaOS/CasaOS-Common/external" + "github.com/ReCasaOS/CasaOS-UserService/pkg/config" +) + +// TestMessageBusSendsTheInternalSecret: the message bus refuses loopback +// calls that lack the per-boot internal secret, so its client must add it. +func TestMessageBusSendsTheInternalSecret(t *testing.T) { + var got string + bus := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { + got = r.Header.Get("Authorization") + })) + defer bus.Close() + + dir := t.TempDir() + for name, content := range map[string]string{ + external.MessageBusAddressFilename: bus.URL, + external.InternalSecretFilename: "s3cret", + } { + if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o600); err != nil { + t.Fatal(err) + } + } + defer func(old string) { config.CommonInfo.RuntimePath = old }(config.CommonInfo.RuntimePath) + config.CommonInfo.RuntimePath = dir + + resp, err := (&store{}).MessageBus().RegisterEventTypes(context.Background(), nil) + if err != nil { + t.Fatal(err) + } + resp.Body.Close() + + if !external.IsInternalRequest("127.0.0.1", got, dir) { + t.Fatalf("the bus got Authorization %q, not the internal secret", got) + } +} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 89d3573..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "declaration": true, - "lib": [ - "es2017", - "DOM" - ], - "module": "commonjs", - "target": "es6", - "skipLibCheck": true, - "sourceMap": false, - "strict": true, - "useUnknownInCatchVariables": false, - "resolveJsonModule": true, - "esModuleInterop": true, - "outDir": "dist", - "types": [ - "node" - ], - }, - "exclude": [ - "node_modules" - ], - "main": "generate/index" -} \ No newline at end of file