From 21d617aedb236a40318994f43044a0092832ceab Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 29 May 2024 21:07:41 +0200 Subject: [PATCH 01/17] authorize requests --- api/.env | 2 +- api/.env.deployment | 2 + api/README.md | 20 +++--- api/cmd/main.go | 12 ++++ api/go.mod | 31 +++++++-- api/go.sum | 132 ++++++++++++++++++++++++++++++++++++ api/services/authService.go | 51 ++++++++++++++ 7 files changed, 233 insertions(+), 17 deletions(-) create mode 100644 api/services/authService.go diff --git a/api/.env b/api/.env index d5e69e1..0e6c51c 100644 --- a/api/.env +++ b/api/.env @@ -7,4 +7,4 @@ MYSQL_ROOT_PASSWORD="Root#123" MYSQL_HOST="localhost" MYSQL_PORT=3306 - +OAUTH_CLIENT="" \ No newline at end of file diff --git a/api/.env.deployment b/api/.env.deployment index 614a6a8..86e8652 100644 --- a/api/.env.deployment +++ b/api/.env.deployment @@ -7,3 +7,5 @@ MYSQL_ROOT_PASSWORD="changeme" #TODO change me. MYSQL_HOST="mysql" MYSQL_PORT="3306" +OAUTH_CLIENT="" #TODO set me + diff --git a/api/README.md b/api/README.md index fc048cc..b63d612 100644 --- a/api/README.md +++ b/api/README.md @@ -7,14 +7,14 @@ The api will be exposed to port 8080, access it with `localhost:8080`. ## Environment variables The docker image will use the following environment variables: -| Key | Default Value | Options | -|----------------------------------------------------|---------------|--------------------| -| PORT | "8080" | | -| GIN_MODE | "release" | "release", "debug" | -| MYSQL_HOST | "mysql" | | -| MYSQL_PORT | "3306" | | -| MYSQL_DATABASE | "api" | | -| MYSQL_ROOT_USER | "root" | | -| MYSQL_ROOT_PASSWORD | "changeme" | | - +| Key | Default Value | Options | +|----------------------------------------------------|---------------|------------------------| +| PORT | "8080" | | +| GIN_MODE | "release" | "release", "debug" | +| MYSQL_HOST | "mysql" | | +| MYSQL_PORT | "3306" | | +| MYSQL_DATABASE | "api" | | +| MYSQL_ROOT_USER | "root" | | +| MYSQL_ROOT_PASSWORD | "changeme" | | +| OAUTH_CLIENT | | Google OAuth Client ID | If you use the docker image directly (without our provided docker-compose), you must specify them. \ No newline at end of file diff --git a/api/cmd/main.go b/api/cmd/main.go index cacf756..0ad2cd7 100644 --- a/api/cmd/main.go +++ b/api/cmd/main.go @@ -24,12 +24,15 @@ func setupRouter(db *sql.DB) *gin.Engine { gamesService := services.GameService(gamesRepository) gamesController := controllers.GameController(gamesService) + authService := services.AuthService() + // Ping test r.GET("/ping", func(c *gin.Context) { c.String(http.StatusOK, "pong") }) //Upload a game +<<<<<<< HEAD r.POST("/games/", CorsHeader, gamesController.UploadGame) //Get all uploaded games r.GET("/games/", CorsHeader, gamesController.GetAllGames) @@ -37,6 +40,15 @@ func setupRouter(db *sql.DB) *gin.Engine { r.GET("/games/:id", CorsHeader, gamesController.GetGameById) //Delete a specific game, identified by its id r.DELETE("/games/:id", CorsHeader, gamesController.DeleteGameById) +======= + r.POST("/games/", authService.Authorize, gamesController.UploadGame) + //Get all uploaded games + r.GET("/games", authService.Authorize, gamesController.GetAllGames) + //Get a specific game by its id + r.GET("/games/:id", authService.Authorize, gamesController.GetGameById) + //Delete a specific game, identified by its id + r.DELETE("/games/:id", authService.Authorize, gamesController.DeleteGameById) +>>>>>>> e186556 (authorize requests) return r } diff --git a/api/go.mod b/api/go.mod index ff57214..eeaa89d 100644 --- a/api/go.mod +++ b/api/go.mod @@ -7,21 +7,32 @@ require ( github.com/dranikpg/dto-mapper v0.2.1 github.com/gin-gonic/gin v1.9.1 github.com/go-sql-driver/mysql v1.8.1 - github.com/google/uuid v1.4.0 + github.com/google/uuid v1.6.0 + google.golang.org/api v0.182.0 ) require ( + cloud.google.com/go/auth v0.4.2 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/bytedance/sonic v1.11.4 // indirect github.com/cloudwego/base64x v0.1.1 // indirect github.com/cloudwego/iasm v0.1.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.19.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -42,15 +53,23 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/arch v0.7.0 // indirect - golang.org/x/crypto v0.22.0 // indirect + golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/oauth2 v0.20.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e // indirect + google.golang.org/grpc v1.64.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/api/go.sum b/api/go.sum index 7762e57..1dbd9f8 100644 --- a/api/go.sum +++ b/api/go.sum @@ -1,25 +1,43 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.114.0 h1:OIPFAdfrFDFO2ve2U7r/H5SwSbBzEdrBdE7xkgwc+kY= +cloud.google.com/go/auth v0.4.2 h1:sb0eyLkhRtpq5jA+a8KWw0W70YcdVca7KJ8TM0AFYDg= +cloud.google.com/go/auth v0.4.2/go.mod h1:Kqvlz1cf1sNA0D+sYJnkPQOP+JMHkuHeIgVmCRtZOLc= +cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4= +cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/sonic v1.11.4 h1:8+OMLSSDDm2/qJc6ld5K5Sm62NK9VHcUKk0NzBoMAM4= github.com/bytedance/sonic v1.11.4/go.mod h1:YrWEqYtlBPS6LUA0vpuG79a1trsh4Ae41uWUWUreHhE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudwego/base64x v0.1.0/go.mod h1:lM8nFiNbg74QgesNo6EAtv8N9tlRjBWExmHoNDa3PkU= github.com/cloudwego/base64x v0.1.1 h1:ClI/Dhf8lEZ753TZ6N2CEsTvBgj48LUXqT1rSsKJrf8= github.com/cloudwego/base64x v0.1.1/go.mod h1:8DeCguxATjLTYFYhoS/CLQuTjr4/ofRb1vOVJLalPPM= github.com/cloudwego/iasm v0.0.9/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cloudwego/iasm v0.1.1 h1:Py/XoYVR3xFd2pXmvmOnoS5vHTlYT9SnGK28ES8JOIk= github.com/cloudwego/iasm v0.1.1/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/dranikpg/dto-mapper v0.2.1 h1:1DaphrSfBXZVlVolCP+XspMzBAFYGne91+SK594xyTg= github.com/dranikpg/dto-mapper v0.2.1/go.mod h1:Hkidt8Lkurm7pLPYOiq3I/LlIBmDdB4J4c/VMqFXHfg= +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= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= @@ -29,6 +47,11 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= @@ -40,10 +63,40 @@ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpv github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= @@ -77,6 +130,7 @@ github.com/pelletier/go-toml/v2 v2.2.1 h1:9TA9+T8+8CUCO2+WYnDLCgrYi9+omqKXyjDtos github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= @@ -110,6 +164,16 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= @@ -117,20 +181,86 @@ go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTV golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.182.0 h1:if5fPvudRQ78GeRx3RayIoiuV7modtErPIZC/T2bIvE= +google.golang.org/api v0.182.0/go.mod h1:cGhjy4caqA5yXRzEhkHI8Y9mfyC2VLTlER2l08xaqtM= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e h1:Elxv5MwEkCI9f5SkoL6afed6NTdxaGoAo39eANBwHL8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -139,5 +269,7 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/api/services/authService.go b/api/services/authService.go new file mode 100644 index 0000000..2e36bda --- /dev/null +++ b/api/services/authService.go @@ -0,0 +1,51 @@ +package services + +import ( + "context" + "github.com/gin-gonic/gin" + "google.golang.org/api/idtoken" + "log" + "net/http" + "os" +) + +type IAuthService interface { + Authorize(_ *gin.Context) +} + +type authService struct { +} + +func (_ authService) Authorize(c *gin.Context) { + //Check if OAUTH_CLIENT has been set + if len(os.Getenv("OAUTH_CLIENT")) == 0 { + //if not set & we are in production mode, abort + if os.Getenv("GIN_MODE") == "release" { + log.Fatalf("OAUTH_CLIENT is not set, cannot authorize requests") + } + //otherwise we are in debug mode and we can accept it + log.Println("OAUTH_CLIENT is not set, cannot authorize requests") + c.Set("subject", "") + return + } + + tokenString := c.GetHeader("Authorization") + payload, err := idtoken.Validate(context.Background(), tokenString, os.Getenv("OAUTH_CLIENT")) + + if err != nil { + log.Println(err.Error()) + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid token"}) + return + } + if payload == nil { + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + c.Set("subject", payload.Subject) + +} + +func AuthService() IAuthService { + return &authService{} +} From f8d2ff0917a34d44d661ddc9a20dbb60de4c6cd7 Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 29 May 2024 21:37:23 +0200 Subject: [PATCH 02/17] save ownership in db when creating a game --- api/controllers/gameController.go | 2 +- api/migrations/1_game_ownership.sql | 2 ++ api/models/game.go | 3 ++- api/repositories/gameRepository.go | 9 +++++---- api/services/gameService.go | 8 ++++---- 5 files changed, 14 insertions(+), 10 deletions(-) create mode 100644 api/migrations/1_game_ownership.sql diff --git a/api/controllers/gameController.go b/api/controllers/gameController.go index 5f50db5..491a317 100644 --- a/api/controllers/gameController.go +++ b/api/controllers/gameController.go @@ -79,7 +79,7 @@ func (g gameController) UploadGame(c *gin.Context) { c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()}) } - _, err = g.service.Save(file, title) + _, err = g.service.Save(file, title, c.GetString("subject")) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) diff --git a/api/migrations/1_game_ownership.sql b/api/migrations/1_game_ownership.sql new file mode 100644 index 0000000..40b1110 --- /dev/null +++ b/api/migrations/1_game_ownership.sql @@ -0,0 +1,2 @@ +ALTER TABLE games ADD Owner varchar(255); +INSERT INTO db_state VALUES (1); \ No newline at end of file diff --git a/api/models/game.go b/api/models/game.go index 1462db1..5ea2fc1 100644 --- a/api/models/game.go +++ b/api/models/game.go @@ -11,4 +11,5 @@ type Game struct { StorageLocation string `json:"storageLocation"` Status shared.GameStatus `json:"status"` Url string `json:"url"` -} + Owner string `json:"owner"` +} diff --git a/api/repositories/gameRepository.go b/api/repositories/gameRepository.go index 25ef3e4..c8e9076 100644 --- a/api/repositories/gameRepository.go +++ b/api/repositories/gameRepository.go @@ -34,7 +34,7 @@ func (g gameRepository) FindAll() ([]models.Game, error) { var games = []models.Game{} for query.Next() { var game models.Game - err = query.Scan(&game.ID, &game.Title, &game.StorageLocation, &game.Status, &game.Url) + err = query.Scan(&game.ID, &game.Title, &game.StorageLocation, &game.Status, &game.Url, &game.Owner) if err != nil { return nil, err } @@ -52,7 +52,8 @@ func (g gameRepository) FindAll() ([]models.Game, error) { // FindByID finds a game with a specific id or nil if the game has not been found. func (g gameRepository) FindByID(id uuid.UUID) (*models.Game, error) { var game models.Game - err := g.db.QueryRow("SELECT * FROM games WHERE ID = ?", id).Scan(&game.ID, &game.Title, &game.StorageLocation, &game.Status, &game.Url) + err := g.db.QueryRow("SELECT * FROM games WHERE ID = ?", id). + Scan(&game.ID, &game.Title, &game.StorageLocation, &game.Status, &game.Url, &game.Owner) if err != nil { if err == sql.ErrNoRows { return nil, nil @@ -86,12 +87,12 @@ func (g gameRepository) Save(game *models.Game) error { } //If not create a new one - stmt, err := g.db.Prepare("INSERT INTO games (ID, Title, StorageLocation, Status, Url) VALUES (?,?,?,?,?)") + stmt, err := g.db.Prepare("INSERT INTO games (ID, Title, StorageLocation, Status, Url, Owner) VALUES (?,?,?,?,?,?)") if err != nil { return err } - return checkResult(stmt.Exec(game.ID, game.Title, game.StorageLocation, game.Status, game.Url)) + return checkResult(stmt.Exec(game.ID, game.Title, game.StorageLocation, game.Status, game.Url, game.Owner)) } // Delete removes the entry with a specific id from the games database. diff --git a/api/services/gameService.go b/api/services/gameService.go index deaad06..3347c6c 100644 --- a/api/services/gameService.go +++ b/api/services/gameService.go @@ -11,7 +11,7 @@ import ( type IGameService interface { FindAll() ([]models.Game, error) FindByID(id uuid.UUID) (*models.Game, error) - Save(file *multipart.FileHeader, title string) (*models.Game, error) + Save(file *multipart.FileHeader, title string, owner string) (*models.Game, error) Delete(id uuid.UUID) error } @@ -25,7 +25,7 @@ func (g gameService) FindAll() ([]models.Game, error) { func (g gameService) FindByID(id uuid.UUID) (*models.Game, error) { return g.repository.FindByID(id) } -func (g gameService) Save(file *multipart.FileHeader, title string) (*models.Game, error) { +func (g gameService) Save(file *multipart.FileHeader, title string, owner string) (*models.Game, error) { //TODO save file game := models.Game{ @@ -34,8 +34,8 @@ func (g gameService) Save(file *multipart.FileHeader, title string) (*models.Gam StorageLocation: "", Status: shared.Status_New, Url: "", - } - + Owner: owner, + } return &game, g.repository.Save(&game) } From 07937a088751c5c1cbd3449c94f54f1c6ff996cf Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 29 May 2024 21:39:28 +0200 Subject: [PATCH 03/17] invalid token error message --- api/services/authService.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/services/authService.go b/api/services/authService.go index 2e36bda..e39e885 100644 --- a/api/services/authService.go +++ b/api/services/authService.go @@ -34,7 +34,7 @@ func (_ authService) Authorize(c *gin.Context) { if err != nil { log.Println(err.Error()) - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid token"}) + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "invalid token"}) return } if payload == nil { From d10aa2c9204f91b4d29aed787ea807f13ee8a7a9 Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 29 May 2024 22:03:05 +0200 Subject: [PATCH 04/17] GetGameById return 403 if game belongs to a different user . --- api/controllers/gameController.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/api/controllers/gameController.go b/api/controllers/gameController.go index 491a317..6ef2cf2 100644 --- a/api/controllers/gameController.go +++ b/api/controllers/gameController.go @@ -8,6 +8,7 @@ import ( "github.com/dranikpg/dto-mapper" "github.com/gin-gonic/gin" "github.com/google/uuid" + "log" "net/http" ) @@ -50,6 +51,13 @@ func (g gameController) GetGameById(c *gin.Context) { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } + if game.Owner != c.GetString("subject") { + log.Print(fmt.Printf("%s tried to access an resource of %s", c.GetString("subject"), game.Owner)) + c.AbortWithStatusJSON( + http.StatusForbidden, + gin.H{"message": "You don't have permission to access this resource"}) + return + } if game == nil { c.JSON(http.StatusNotFound, gin.H{"message": "Game not found"}) return From 4849f0a4bd67a6b8a0d42618060d0ca212e985ca Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 29 May 2024 22:05:23 +0200 Subject: [PATCH 05/17] invalid token uppercase --- api/services/authService.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/services/authService.go b/api/services/authService.go index e39e885..02cb5d7 100644 --- a/api/services/authService.go +++ b/api/services/authService.go @@ -34,7 +34,7 @@ func (_ authService) Authorize(c *gin.Context) { if err != nil { log.Println(err.Error()) - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "invalid token"}) + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "Invalid token"}) return } if payload == nil { From be9137f1284e0535abd2a3acdb8a1096a278fb08 Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 29 May 2024 22:10:40 +0200 Subject: [PATCH 06/17] GameRepository.FindAllByOwner --- api/repositories/gameRepository.go | 41 ++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/api/repositories/gameRepository.go b/api/repositories/gameRepository.go index c8e9076..e275fc6 100644 --- a/api/repositories/gameRepository.go +++ b/api/repositories/gameRepository.go @@ -11,6 +11,7 @@ type IGameRepository interface { FindByID(id uuid.UUID) (*models.Game, error) Save(game *models.Game) error Delete(id uuid.UUID) error + FindAllByOwner(owner string) ([]models.Game, error) } type gameRepository struct { @@ -30,23 +31,18 @@ func (g gameRepository) FindAll() ([]models.Game, error) { return nil, err } defer query.Close() + return readGamesFromRows(query) +} - var games = []models.Game{} - for query.Next() { - var game models.Game - err = query.Scan(&game.ID, &game.Title, &game.StorageLocation, &game.Status, &game.Url, &game.Owner) - if err != nil { - return nil, err - } - games = append(games, game) - } - - err = query.Err() +// FindAll returns all games of a specific owner from the database or (nil, err) if an error occurred. +func (g gameRepository) FindAllByOwner(owner string) ([]models.Game, error) { + stmt, err := g.db.Prepare("SELECT * FROM games WHERE owner = ?") + query, err := stmt.Query(owner) if err != nil { return nil, err } - - return games, nil + defer query.Close() + return readGamesFromRows(query) } // FindByID finds a game with a specific id or nil if the game has not been found. @@ -124,3 +120,22 @@ func checkAffectedRows(res sql.Result) error { } return nil } + +func readGamesFromRows(query *sql.Rows) ([]models.Game, error) { + var games = []models.Game{} + for query.Next() { + var game models.Game + err := query.Scan(&game.ID, &game.Title, &game.StorageLocation, &game.Status, &game.Url, &game.Owner) + if err != nil { + return nil, err + } + games = append(games, game) + } + + err := query.Err() + if err != nil { + return nil, err + } + + return games, nil +} From 7b4cf9f5db368b623dbc519646e0428a45826f5a Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 29 May 2024 22:12:18 +0200 Subject: [PATCH 07/17] Get games for user who is logged-in --- api/controllers/gameController.go | 2 +- api/services/gameService.go | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/api/controllers/gameController.go b/api/controllers/gameController.go index 6ef2cf2..8684021 100644 --- a/api/controllers/gameController.go +++ b/api/controllers/gameController.go @@ -25,7 +25,7 @@ type gameController struct { func (g gameController) GetAllGames(c *gin.Context) { //Get Games - games, err := g.service.FindAll() + games, err := g.service.FindAllByOwner(c.GetString("subject")) if err != nil { //TODO handle different errors c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return diff --git a/api/services/gameService.go b/api/services/gameService.go index 3347c6c..003c6fb 100644 --- a/api/services/gameService.go +++ b/api/services/gameService.go @@ -13,6 +13,7 @@ type IGameService interface { FindByID(id uuid.UUID) (*models.Game, error) Save(file *multipart.FileHeader, title string, owner string) (*models.Game, error) Delete(id uuid.UUID) error + FindAllByOwner(owner string) ([]models.Game, error) } type gameService struct { @@ -23,6 +24,10 @@ func (g gameService) FindAll() ([]models.Game, error) { return g.repository.FindAll() } +func (g gameService) FindAllByOwner(owner string) ([]models.Game, error) { + return g.repository.FindAllByOwner(owner) +} + func (g gameService) FindByID(id uuid.UUID) (*models.Game, error) { return g.repository.FindByID(id) } func (g gameService) Save(file *multipart.FileHeader, title string, owner string) (*models.Game, error) { From f1a4b5f74cbc7849688bc4ba17959bf37f77a87c Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 29 May 2024 22:34:08 +0200 Subject: [PATCH 08/17] hasAccessToGame logic --- api/controllers/gameController.go | 11 +++++++++++ api/repositories/gameRepository.go | 11 +++++++++++ api/services/gameService.go | 5 +++++ 3 files changed, 27 insertions(+) diff --git a/api/controllers/gameController.go b/api/controllers/gameController.go index 8684021..4cbe30e 100644 --- a/api/controllers/gameController.go +++ b/api/controllers/gameController.go @@ -134,3 +134,14 @@ func getUUIDFromRequest(c *gin.Context) uuid.UUID { } return _uuid } + +// Returns true if the owner the user who is logged-in has the same subject-id as the game owner. +// Returns false and error if any other error occurred. +func (g gameController) hasAccessToGame(c *gin.Context) (bool, error) { + owner, err := g.service.ReadOwner(getUUIDFromRequest(c)) + if err != nil { + return false, err + } + + return owner == c.GetString("subject"), nil +} diff --git a/api/repositories/gameRepository.go b/api/repositories/gameRepository.go index e275fc6..2bc38a5 100644 --- a/api/repositories/gameRepository.go +++ b/api/repositories/gameRepository.go @@ -12,6 +12,7 @@ type IGameRepository interface { Save(game *models.Game) error Delete(id uuid.UUID) error FindAllByOwner(owner string) ([]models.Game, error) + ReadOwner(id uuid.UUID) (string, error) } type gameRepository struct { @@ -24,6 +25,16 @@ func GameRepository(db *sql.DB) IGameRepository { } } +// Read the owner of a specific game or empty if the game has not been found +func (g gameRepository) ReadOwner(id uuid.UUID) (string, error) { + var owner string + err := g.db.QueryRow("SELECT Owner FROM games WHERE ID = ?", id).Scan(&owner) + if err != nil { + return "", err + } + return owner, nil +} + // FindAll returns all games from the database or (nil, err) if an error occurred. func (g gameRepository) FindAll() ([]models.Game, error) { query, err := g.db.Query("SELECT * FROM games") diff --git a/api/services/gameService.go b/api/services/gameService.go index 003c6fb..e3472d2 100644 --- a/api/services/gameService.go +++ b/api/services/gameService.go @@ -14,12 +14,17 @@ type IGameService interface { Save(file *multipart.FileHeader, title string, owner string) (*models.Game, error) Delete(id uuid.UUID) error FindAllByOwner(owner string) ([]models.Game, error) + ReadOwner(id uuid.UUID) (string, error) } type gameService struct { repository repositories.IGameRepository } +func (g gameService) ReadOwner(id uuid.UUID) (string, error) { + return g.repository.ReadOwner(id) +} + func (g gameService) FindAll() ([]models.Game, error) { return g.repository.FindAll() } From c4be77fdde4976e93609ad30d349b042e0743ddf Mon Sep 17 00:00:00 2001 From: Kai Date: Wed, 29 May 2024 22:36:14 +0200 Subject: [PATCH 09/17] check owner in DeleteGameById . --- api/controllers/gameController.go | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/api/controllers/gameController.go b/api/controllers/gameController.go index 4cbe30e..63d526a 100644 --- a/api/controllers/gameController.go +++ b/api/controllers/gameController.go @@ -87,7 +87,7 @@ func (g gameController) UploadGame(c *gin.Context) { c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()}) } - _, err = g.service.Save(file, title, c.GetString("subject")) + _, err = g.service.Save(file, title, c.GetString("subject")) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) @@ -100,11 +100,26 @@ func (g gameController) UploadGame(c *gin.Context) { func (g gameController) DeleteGameById(c *gin.Context) { _uuid := getUUIDFromRequest(c) if _uuid != uuid.Nil { - err := g.service.Delete(_uuid) - if err != nil { //TODO handle different errors + + //Check if the user has access to the game + authorized, err := g.hasAccessToGame(c) + + if err != nil { if err == sql.ErrNoRows { - c.JSON(http.StatusNotFound, gin.H{"message": "Game not found"}) + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"message": "Game not found"}) + return } + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return + } + + if !authorized { + c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"message": "You don't have permission to access this resource"}) + return + } + + err = g.service.Delete(_uuid) + if err != nil { //TODO handle different errors c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) } else { c.Status(http.StatusNoContent) From d65085d008d3916f108a98013f74de4994ebfcc3 Mon Sep 17 00:00:00 2001 From: Kai Date: Thu, 30 May 2024 11:27:26 +0200 Subject: [PATCH 10/17] updated tests --- api/tests/gameRepository_test.go | 222 +++++++++++++++++++------------ 1 file changed, 140 insertions(+), 82 deletions(-) diff --git a/api/tests/gameRepository_test.go b/api/tests/gameRepository_test.go index 3532710..df36bab 100644 --- a/api/tests/gameRepository_test.go +++ b/api/tests/gameRepository_test.go @@ -83,14 +83,15 @@ func Test_Create_Game_Without_Id_Should_Succeed_And_SetId(t *testing.T) { //Define the mock game := models.Game{ ID: uuid.Nil, - Title: "", - StorageLocation: "", - Status: "", - Url: "", + Title: "MockTitle", + StorageLocation: "MockStorageLocation", + Status: "MockStatus", + Url: "MockUrl", + Owner: "MockOwner", } mock.ExpectPrepare(regexp.QuoteMeta("INSERT INTO games")) mock.ExpectExec(regexp.QuoteMeta("INSERT INTO games")). - WithArgs(sqlmock.AnyArg(), game.Title, game.StorageLocation, game.Status, game.Url). + WithArgs(sqlmock.AnyArg(), game.Title, game.StorageLocation, game.Status, game.Url, game.Owner). WillReturnResult(sqlmock.NewResult(0, 1)) //Run the test @@ -122,10 +123,11 @@ func Test_Create_Game_With_Id_Should_Succeed(t *testing.T) { id := uuid.New() game := models.Game{ ID: id, - Title: "", - StorageLocation: "", - Status: "", - Url: "", + Title: "MockTitle", + StorageLocation: "MockStorageLocation", + Status: "MockStatus", + Url: "MockUrl", + Owner: "MockOwner", } mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games WHERE ID = ?")). @@ -134,7 +136,7 @@ func Test_Create_Game_With_Id_Should_Succeed(t *testing.T) { mock.ExpectPrepare("INSERT INTO games") mock.ExpectExec("INSERT INTO games"). - WithArgs(game.ID, game.Title, game.StorageLocation, game.Status, game.Url). + WithArgs(game.ID, game.Title, game.StorageLocation, game.Status, game.Url, game.Owner). WillReturnResult(sqlmock.NewResult(0, 1)) //Run the test @@ -169,21 +171,24 @@ func Test_Save_Existing_Game_Should_Succeed(t *testing.T) { id := uuid.New() game := models.Game{ ID: id, - Title: "Mock", - StorageLocation: "Mock", - Status: "Mock", - Url: "Mock", + Title: "MockTitle", + StorageLocation: "MockStorageLocation", + Status: "MockStatus", + Url: "MockUrl", + Owner: "MockOwner", } mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games WHERE ID = ?")). WithArgs(id).WillReturnRows( - sqlmock.NewRows([]string{"id", "title", "storage_location", "status", "url"}). - AddRow(id, "", "", "", ""), + sqlmock.NewRows([]string{"id", "title", "storage_location", "status", "url", "owner"}). + AddRow(id, "", "", "", "", ""), ) - mock.ExpectPrepare(regexp.QuoteMeta("UPDATE games SET Title=?, StorageLocation=?, Status=?, Url=? WHERE ID = ?")) + mock.ExpectPrepare(regexp. + QuoteMeta("UPDATE games SET Title=?, StorageLocation=?, Status=?, Url=? WHERE ID = ?")) - mock.ExpectExec(regexp.QuoteMeta("UPDATE games SET Title=?, StorageLocation=?, Status=?, Url=? WHERE ID = ?")). + mock.ExpectExec(regexp. + QuoteMeta("UPDATE games SET Title=?, StorageLocation=?, Status=?, Url=? WHERE ID = ?")). WithArgs(game.Title, game.StorageLocation, game.Status, game.Url, game.ID). WillReturnResult(sqlmock.NewResult(0, 1)) @@ -219,16 +224,17 @@ func Test_Find_Game_By_Id_Should_Succeed(t *testing.T) { id := uuid.New() game := models.Game{ ID: id, - Title: "MockA", - StorageLocation: "MockB", - Status: "MockC", - Url: "MockD", + Title: "MockTitle", + StorageLocation: "MockStorageLocation", + Status: "MockStatus", + Url: "MockUrl", + Owner: "MockOwner", } mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games WHERE ID = ?")). WithArgs(id).WillReturnRows( - sqlmock.NewRows([]string{"id", "title", "storage_location", "status", "url"}). - AddRow(id, game.Title, game.StorageLocation, game.Status, game.Url), + sqlmock.NewRows([]string{"id", "title", "storage_location", "status", "url", "owner"}). + AddRow(id, game.Title, game.StorageLocation, game.Status, game.Url, game.Owner), ) //Run the test @@ -239,29 +245,7 @@ func Test_Find_Game_By_Id_Should_Succeed(t *testing.T) { t.Errorf(err.Error()) } - if res == nil { - t.Errorf("game was not returned") - } - - if game.ID != res.ID { - t.Errorf("game id has been changed") - } - - if game.Title != res.Title { - t.Errorf("game title has been changed") - } - - if game.StorageLocation != res.StorageLocation { - t.Errorf("game storage location has been changed") - } - - if game.Status != res.Status { - t.Errorf("game status has been changed") - } - - if game.Url != res.Url { - t.Errorf("game url has been changed") - } + compareGames(t, &game, res) if err = mock.ExpectationsWereMet(); err != nil { t.Errorf(err.Error()) @@ -310,6 +294,7 @@ func Test_Find_Two_Games_Should_Succeed(t *testing.T) { StorageLocation: "AMockB", Status: "AMockC", Url: "AMockD", + Owner: "AMockE", } gameB := models.Game{ @@ -318,13 +303,14 @@ func Test_Find_Two_Games_Should_Succeed(t *testing.T) { StorageLocation: "BMockB", Status: "BMockC", Url: "BMockD", + Owner: "BMockE", } mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games")). WillReturnRows( - sqlmock.NewRows([]string{"id", "title", "storage_location", "status", "url"}). - AddRow(gameA.ID, gameA.Title, gameA.StorageLocation, gameA.Status, gameA.Url). - AddRow(gameB.ID, gameB.Title, gameB.StorageLocation, gameB.Status, gameB.Url), + sqlmock.NewRows([]string{"id", "title", "storage_location", "status", "url", "owner"}). + AddRow(gameA.ID, gameA.Title, gameA.StorageLocation, gameA.Status, gameA.Url, gameA.Owner). + AddRow(gameB.ID, gameB.Title, gameB.StorageLocation, gameB.Status, gameB.Url, gameB.Owner), ) //Run the test @@ -343,44 +329,78 @@ func Test_Find_Two_Games_Should_Succeed(t *testing.T) { t.Errorf("FindAll should return two games") } - if gameA.ID != res[0].ID { - t.Errorf("game id has been changed") + compareGames(t, &gameA, &res[0]) + compareGames(t, &gameB, &res[1]) + + if err = mock.ExpectationsWereMet(); err != nil { + t.Errorf(err.Error()) } +} - if gameA.Title != res[0].Title { - t.Errorf("game title has been changed") +func Test_Find_Games_When_Database_Is_Empty_Should_Succeed(t *testing.T) { + db, mock, err := sqlmock.New() + if err != nil { + t.Fatalf(err.Error()) } + defer db.Close() - if gameA.StorageLocation != res[0].StorageLocation { - t.Errorf("game storage location has been changed") + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games")). + WillReturnRows( + sqlmock.NewRows([]string{"id", "title", "storage_location", "status", "url", "owner"}), + ) + + //Run the test + repository := repositories.GameRepository(db) + + res, err := repository.FindAll() + if err != nil { + t.Errorf(err.Error()) } - if gameA.Status != res[0].Status { - t.Errorf("game status has been changed") + if res == nil { + t.Errorf("nil was returned but empty list was expected") } - if gameA.Url != res[0].Url { - t.Errorf("game url has been changed") + if len(res) != 0 { + t.Errorf("FindAll should return an empty list, but it was not empty") } - if gameB.ID != res[1].ID { - t.Errorf("game id has been changed") + if err = mock.ExpectationsWereMet(); err != nil { + t.Errorf(err.Error()) } +} - if gameB.Title != res[1].Title { - t.Errorf("game title has been changed") +func Test_Read_Owner_Should_Succeed(t *testing.T) { + db, mock, err := sqlmock.New() + if err != nil { + t.Fatalf(err.Error()) } + defer db.Close() - if gameB.StorageLocation != res[1].StorageLocation { - t.Errorf("game storage location has been changed") + id := uuid.New() + game := models.Game{ + ID: id, + Title: "MockTitle", + StorageLocation: "MockStorageLocation", + Status: "MockStatus", + Url: "MockUrl", + Owner: "MockOwner", } - if gameB.Status != res[1].Status { - t.Errorf("game status has been changed") + mock.ExpectQuery(regexp.QuoteMeta("SELECT Owner FROM games WHERE ID = ?")). + WithArgs(id). + WillReturnRows(sqlmock.NewRows([]string{"id", "title", "storage_location", "status", "url", "owner"})) + + //Run the test + repository := repositories.GameRepository(db) + + res, err := repository.ReadOwner(game.ID) + if err == nil { + t.Errorf("error was not returned") } - if gameB.Url != res[1].Url { - t.Errorf("game url has been changed") + if res != "" { + t.Errorf("Owner should be empty, but got %s", res) } if err = mock.ExpectationsWereMet(); err != nil { @@ -388,32 +408,39 @@ func Test_Find_Two_Games_Should_Succeed(t *testing.T) { } } -func Test_Find_Games_When_Database_Is_Empty_Should_Succeed(t *testing.T) { +func Test_Read_Owner_Should_Throw_When_Database_Is_Empty(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf(err.Error()) } defer db.Close() - mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games")). - WillReturnRows( - sqlmock.NewRows([]string{"id", "title", "storage_location", "status", "url"}), - ) + id := uuid.New() + game := models.Game{ + ID: id, + Title: "MockTitle", + StorageLocation: "MockStorageLocation", + Status: "MockStatus", + Url: "MockUrl", + Owner: "MockOwner", + } + + mock.ExpectQuery(regexp.QuoteMeta("SELECT Owner FROM games WHERE ID = ?")). + WithArgs(id).WillReturnRows( + sqlmock.NewRows([]string{"owner"}). + AddRow(game.Owner), + ) //Run the test repository := repositories.GameRepository(db) - res, err := repository.FindAll() + res, err := repository.ReadOwner(game.ID) if err != nil { t.Errorf(err.Error()) } - if res == nil { - t.Errorf("nil was returned but empty list was expected") - } - - if len(res) != 0 { - t.Errorf("FindAll should return an empty list, but it was not empty") + if res != game.Owner { + t.Errorf("ReadOwner should return %v, but got %v", game.Owner, res) } if err = mock.ExpectationsWereMet(); err != nil { @@ -422,3 +449,34 @@ func Test_Find_Games_When_Database_Is_Empty_Should_Succeed(t *testing.T) { } //************************************ END READ TESTS ************************************ + +func compareGames(t *testing.T, game *models.Game, res *models.Game) { + if res == nil { + t.Errorf("game was not returned") + } + + if game.ID != res.ID { + t.Errorf("game id has been changed") + } + + if game.Title != res.Title { + t.Errorf("game title has been changed") + } + + if game.StorageLocation != res.StorageLocation { + t.Errorf("game storage location has been changed") + } + + if game.Status != res.Status { + t.Errorf("game status has been changed") + } + + if game.Url != res.Url { + t.Errorf("game url has been changed") + } + + if game.Owner != res.Owner { + t.Errorf("game owner has been changed") + } + +} From c63fa170aad5afc1f4ada6d63f252743ca1e174e Mon Sep 17 00:00:00 2001 From: Kai Date: Thu, 30 May 2024 11:42:00 +0200 Subject: [PATCH 11/17] fix issue in FindAllByOwner --- api/repositories/gameRepository.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/repositories/gameRepository.go b/api/repositories/gameRepository.go index 2bc38a5..6f396f9 100644 --- a/api/repositories/gameRepository.go +++ b/api/repositories/gameRepository.go @@ -48,6 +48,9 @@ func (g gameRepository) FindAll() ([]models.Game, error) { // FindAll returns all games of a specific owner from the database or (nil, err) if an error occurred. func (g gameRepository) FindAllByOwner(owner string) ([]models.Game, error) { stmt, err := g.db.Prepare("SELECT * FROM games WHERE owner = ?") + if err != nil { + return nil, err + } query, err := stmt.Query(owner) if err != nil { return nil, err From 6e385810f823b72c19ed5d41f4878fc3b2d44c5b Mon Sep 17 00:00:00 2001 From: Kai Date: Thu, 30 May 2024 11:43:11 +0200 Subject: [PATCH 12/17] removed FindAll() games it has been replaced by FindAllByOwner --- api/repositories/gameRepository.go | 11 ----------- api/services/gameService.go | 14 +++++--------- api/tests/gameRepository_test.go | 28 ++++++++++++++++------------ 3 files changed, 21 insertions(+), 32 deletions(-) diff --git a/api/repositories/gameRepository.go b/api/repositories/gameRepository.go index 6f396f9..a2b6b24 100644 --- a/api/repositories/gameRepository.go +++ b/api/repositories/gameRepository.go @@ -7,7 +7,6 @@ import ( ) type IGameRepository interface { - FindAll() ([]models.Game, error) FindByID(id uuid.UUID) (*models.Game, error) Save(game *models.Game) error Delete(id uuid.UUID) error @@ -35,16 +34,6 @@ func (g gameRepository) ReadOwner(id uuid.UUID) (string, error) { return owner, nil } -// FindAll returns all games from the database or (nil, err) if an error occurred. -func (g gameRepository) FindAll() ([]models.Game, error) { - query, err := g.db.Query("SELECT * FROM games") - if err != nil { - return nil, err - } - defer query.Close() - return readGamesFromRows(query) -} - // FindAll returns all games of a specific owner from the database or (nil, err) if an error occurred. func (g gameRepository) FindAllByOwner(owner string) ([]models.Game, error) { stmt, err := g.db.Prepare("SELECT * FROM games WHERE owner = ?") diff --git a/api/services/gameService.go b/api/services/gameService.go index e3472d2..aedcca7 100644 --- a/api/services/gameService.go +++ b/api/services/gameService.go @@ -9,9 +9,8 @@ import ( ) type IGameService interface { - FindAll() ([]models.Game, error) FindByID(id uuid.UUID) (*models.Game, error) - Save(file *multipart.FileHeader, title string, owner string) (*models.Game, error) + Save(file *multipart.FileHeader, title string, owner string) (*models.Game, error) Delete(id uuid.UUID) error FindAllByOwner(owner string) ([]models.Game, error) ReadOwner(id uuid.UUID) (string, error) @@ -25,17 +24,14 @@ func (g gameService) ReadOwner(id uuid.UUID) (string, error) { return g.repository.ReadOwner(id) } -func (g gameService) FindAll() ([]models.Game, error) { - return g.repository.FindAll() -} - func (g gameService) FindAllByOwner(owner string) ([]models.Game, error) { return g.repository.FindAllByOwner(owner) } func (g gameService) FindByID(id uuid.UUID) (*models.Game, error) { return g.repository.FindByID(id) } -func (g gameService) Save(file *multipart.FileHeader, title string, owner string) (*models.Game, error) { +func (g gameService) Save(file *multipart.FileHeader, title string, owner string) (*models.Game, error) { + //TODO save file game := models.Game{ @@ -44,8 +40,8 @@ func (g gameService) Save(file *multipart.FileHeader, title string, owner string StorageLocation: "", Status: shared.Status_New, Url: "", - Owner: owner, - } + Owner: owner, + } return &game, g.repository.Save(&game) } diff --git a/api/tests/gameRepository_test.go b/api/tests/gameRepository_test.go index df36bab..42e1d16 100644 --- a/api/tests/gameRepository_test.go +++ b/api/tests/gameRepository_test.go @@ -180,7 +180,7 @@ func Test_Save_Existing_Game_Should_Succeed(t *testing.T) { mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games WHERE ID = ?")). WithArgs(id).WillReturnRows( - sqlmock.NewRows([]string{"id", "title", "storage_location", "status", "url", "owner"}). + sqlmock.NewRows([]string{"Id", "Title", "StorageLocation", "Status", "Url", "Owner"}). AddRow(id, "", "", "", "", ""), ) @@ -233,7 +233,7 @@ func Test_Find_Game_By_Id_Should_Succeed(t *testing.T) { mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games WHERE ID = ?")). WithArgs(id).WillReturnRows( - sqlmock.NewRows([]string{"id", "title", "storage_location", "status", "url", "owner"}). + sqlmock.NewRows([]string{"Id", "Title", "StorageLocation", "Status", "Url", "Owner"}). AddRow(id, game.Title, game.StorageLocation, game.Status, game.Url, game.Owner), ) @@ -281,7 +281,7 @@ func Test_Find_Game_By_Id_Should_Return_Nil(t *testing.T) { } } -func Test_Find_Two_Games_Should_Succeed(t *testing.T) { +func Test_Find_Two_Games_Of_Same_Owner_Should_Succeed(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf(err.Error()) @@ -294,7 +294,7 @@ func Test_Find_Two_Games_Should_Succeed(t *testing.T) { StorageLocation: "AMockB", Status: "AMockC", Url: "AMockD", - Owner: "AMockE", + Owner: "MockOwner", } gameB := models.Game{ @@ -303,12 +303,14 @@ func Test_Find_Two_Games_Should_Succeed(t *testing.T) { StorageLocation: "BMockB", Status: "BMockC", Url: "BMockD", - Owner: "BMockE", + Owner: "MockOwner", } - mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games")). + mock.ExpectPrepare(regexp.QuoteMeta("SELECT * FROM games WHERE owner = ?")) + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games WHERE owner = ?")). + WithArgs("MockOwner"). WillReturnRows( - sqlmock.NewRows([]string{"id", "title", "storage_location", "status", "url", "owner"}). + sqlmock.NewRows([]string{"ID", "Title", "StorageLocation", "Status", "Url", "Owner"}). AddRow(gameA.ID, gameA.Title, gameA.StorageLocation, gameA.Status, gameA.Url, gameA.Owner). AddRow(gameB.ID, gameB.Title, gameB.StorageLocation, gameB.Status, gameB.Url, gameB.Owner), ) @@ -316,7 +318,7 @@ func Test_Find_Two_Games_Should_Succeed(t *testing.T) { //Run the test repository := repositories.GameRepository(db) - res, err := repository.FindAll() + res, err := repository.FindAllByOwner("MockOwner") if err != nil { t.Errorf(err.Error()) } @@ -344,15 +346,17 @@ func Test_Find_Games_When_Database_Is_Empty_Should_Succeed(t *testing.T) { } defer db.Close() - mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games")). + mock.ExpectPrepare(regexp.QuoteMeta("SELECT * FROM games WHERE owner = ?")) + mock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM games WHERE owner = ?")). + WithArgs("MockOwner"). WillReturnRows( - sqlmock.NewRows([]string{"id", "title", "storage_location", "status", "url", "owner"}), + sqlmock.NewRows([]string{"Id", "Title", "StorageLocation", "Status", "Url", "Owner"}), ) //Run the test repository := repositories.GameRepository(db) - res, err := repository.FindAll() + res, err := repository.FindAllByOwner("MockOwner") if err != nil { t.Errorf(err.Error()) } @@ -389,7 +393,7 @@ func Test_Read_Owner_Should_Succeed(t *testing.T) { mock.ExpectQuery(regexp.QuoteMeta("SELECT Owner FROM games WHERE ID = ?")). WithArgs(id). - WillReturnRows(sqlmock.NewRows([]string{"id", "title", "storage_location", "status", "url", "owner"})) + WillReturnRows(sqlmock.NewRows([]string{"Id", "Title", "StorageLocation", "Status", "Url", "Owner"})) //Run the test repository := repositories.GameRepository(db) From eafee5d6f0b159d6489207e1b4a91bed272f261b Mon Sep 17 00:00:00 2001 From: Kai Date: Thu, 30 May 2024 11:58:09 +0200 Subject: [PATCH 13/17] fix controller flow by using AbortWithStatusJSON --- api/controllers/gameController.go | 33 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/api/controllers/gameController.go b/api/controllers/gameController.go index 63d526a..50964a0 100644 --- a/api/controllers/gameController.go +++ b/api/controllers/gameController.go @@ -27,7 +27,7 @@ func (g gameController) GetAllGames(c *gin.Context) { //Get Games games, err := g.service.FindAllByOwner(c.GetString("subject")) if err != nil { //TODO handle different errors - c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } @@ -35,11 +35,12 @@ func (g gameController) GetAllGames(c *gin.Context) { resultDto := []dtos.GetGameByIdResponseBody{} err = dto.Map(&resultDto, games) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } c.IndentedJSON(http.StatusOK, resultDto) + return } func (g gameController) GetGameById(c *gin.Context) { @@ -48,7 +49,7 @@ func (g gameController) GetGameById(c *gin.Context) { //Get game by uuid game, err := g.service.FindByID(_uuid) if err != nil { //TODO handle different errors - c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } if game.Owner != c.GetString("subject") { @@ -59,7 +60,7 @@ func (g gameController) GetGameById(c *gin.Context) { return } if game == nil { - c.JSON(http.StatusNotFound, gin.H{"message": "Game not found"}) + c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"message": "Game not found"}) return } @@ -67,7 +68,7 @@ func (g gameController) GetGameById(c *gin.Context) { resultDto := dtos.GetGameByIdResponseBody{} err = dto.Map(&resultDto, game) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) return } @@ -79,22 +80,25 @@ func (g gameController) UploadGame(c *gin.Context) { title := c.Query("title") if len(title) == 0 { - c.JSON(http.StatusBadRequest, gin.H{"message": "Title is required"}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "Title is required"})# + return } file, err := c.FormFile("file") if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"message": err.Error()}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": err.Error()}) + return } _, err = g.service.Save(file, title, c.GetString("subject")) if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return } c.Header("content-location", fmt.Sprintf("%s/games/%s", c.Request.Host, file.Filename)) - c.String(http.StatusCreated, "") + c.AbortWithStatus(http.StatusCreated) } func (g gameController) DeleteGameById(c *gin.Context) { @@ -120,14 +124,11 @@ func (g gameController) DeleteGameById(c *gin.Context) { err = g.service.Delete(_uuid) if err != nil { //TODO handle different errors - c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) } else { - c.Status(http.StatusNoContent) + c.AbortWithStatus(http.StatusNoContent) } } - - //TODO Implement delete game - c.String(http.StatusNoContent, "") } func GameController(service services.IGameService) IGameController { @@ -141,10 +142,10 @@ func GameController(service services.IGameService) IGameController { func getUUIDFromRequest(c *gin.Context) uuid.UUID { _uuid, err := uuid.Parse(c.Param("id")) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"message": "Invalid game ID"}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "Invalid game ID"}) return uuid.Nil } else if _uuid == uuid.Nil { - c.JSON(http.StatusBadRequest, gin.H{"message": "Invalid game ID"}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "Invalid game ID"}) return uuid.Nil } return _uuid From 554eb987d41289f3fed41638c0a4cbaea2da2704 Mon Sep 17 00:00:00 2001 From: Kai Date: Thu, 30 May 2024 17:03:02 +0200 Subject: [PATCH 14/17] use '' for env variable OAUTH_CLIENT --- api/.env | 2 +- api/.env.deployment | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/.env b/api/.env index 0e6c51c..d268296 100644 --- a/api/.env +++ b/api/.env @@ -7,4 +7,4 @@ MYSQL_ROOT_PASSWORD="Root#123" MYSQL_HOST="localhost" MYSQL_PORT=3306 -OAUTH_CLIENT="" \ No newline at end of file +OAUTH_CLIENT='' \ No newline at end of file diff --git a/api/.env.deployment b/api/.env.deployment index 86e8652..e0b5bfa 100644 --- a/api/.env.deployment +++ b/api/.env.deployment @@ -7,5 +7,5 @@ MYSQL_ROOT_PASSWORD="changeme" #TODO change me. MYSQL_HOST="mysql" MYSQL_PORT="3306" -OAUTH_CLIENT="" #TODO set me +OAUTH_CLIENT='' #TODO set me From 061931bdb69253f150bbcf59523ff9c6eafd8ad4 Mon Sep 17 00:00:00 2001 From: Kai Date: Thu, 30 May 2024 17:26:38 +0200 Subject: [PATCH 15/17] yet another return statement --- api/controllers/gameController.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/api/controllers/gameController.go b/api/controllers/gameController.go index 50964a0..56a92b8 100644 --- a/api/controllers/gameController.go +++ b/api/controllers/gameController.go @@ -73,6 +73,7 @@ func (g gameController) GetGameById(c *gin.Context) { } c.IndentedJSON(http.StatusOK, resultDto) + return } } @@ -80,7 +81,7 @@ func (g gameController) UploadGame(c *gin.Context) { title := c.Query("title") if len(title) == 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "Title is required"})# + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"message": "Title is required"}) return } @@ -99,6 +100,7 @@ func (g gameController) UploadGame(c *gin.Context) { c.Header("content-location", fmt.Sprintf("%s/games/%s", c.Request.Host, file.Filename)) c.AbortWithStatus(http.StatusCreated) + return } func (g gameController) DeleteGameById(c *gin.Context) { @@ -125,8 +127,10 @@ func (g gameController) DeleteGameById(c *gin.Context) { err = g.service.Delete(_uuid) if err != nil { //TODO handle different errors c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) + return } else { c.AbortWithStatus(http.StatusNoContent) + return } } } From 36f850fd3584aa50e1fb760069a89b5e3b9d92f7 Mon Sep 17 00:00:00 2001 From: Kai Date: Fri, 31 May 2024 15:25:05 +0200 Subject: [PATCH 16/17] verify sub is not empty --- api/controllers/gameController.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/api/controllers/gameController.go b/api/controllers/gameController.go index 56a92b8..111a049 100644 --- a/api/controllers/gameController.go +++ b/api/controllers/gameController.go @@ -91,7 +91,13 @@ func (g gameController) UploadGame(c *gin.Context) { return } - _, err = g.service.Save(file, title, c.GetString("subject")) + sub := c.GetString("subject") + if len(sub) == 0 { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "IdToken is invalid, sub is missing"}) + return + } + + _, err = g.service.Save(file, title, sub) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": err.Error()}) From 6b374f328691240298a46ee27098591c633025eb Mon Sep 17 00:00:00 2001 From: Thomas Riegler Date: Sun, 2 Jun 2024 17:13:45 +0200 Subject: [PATCH 17/17] Readded CorsHeader --- api/cmd/main.go | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/api/cmd/main.go b/api/cmd/main.go index 0ad2cd7..9df33af 100644 --- a/api/cmd/main.go +++ b/api/cmd/main.go @@ -32,23 +32,13 @@ func setupRouter(db *sql.DB) *gin.Engine { }) //Upload a game -<<<<<<< HEAD - r.POST("/games/", CorsHeader, gamesController.UploadGame) + r.POST("/games/", CorsHeader, authService.Authorize, gamesController.UploadGame) //Get all uploaded games - r.GET("/games/", CorsHeader, gamesController.GetAllGames) + r.GET("/games", CorsHeader, authService.Authorize, gamesController.GetAllGames) //Get a specific game by its id - r.GET("/games/:id", CorsHeader, gamesController.GetGameById) + r.GET("/games/:id", CorsHeader, authService.Authorize, gamesController.GetGameById) //Delete a specific game, identified by its id - r.DELETE("/games/:id", CorsHeader, gamesController.DeleteGameById) -======= - r.POST("/games/", authService.Authorize, gamesController.UploadGame) - //Get all uploaded games - r.GET("/games", authService.Authorize, gamesController.GetAllGames) - //Get a specific game by its id - r.GET("/games/:id", authService.Authorize, gamesController.GetGameById) - //Delete a specific game, identified by its id - r.DELETE("/games/:id", authService.Authorize, gamesController.DeleteGameById) ->>>>>>> e186556 (authorize requests) + r.DELETE("/games/:id", CorsHeader, authService.Authorize, gamesController.DeleteGameById) return r }