From 2ae872c5c979ecadf720d91725192486e44529c5 Mon Sep 17 00:00:00 2001 From: Nlkomaru Date: Fri, 31 May 2024 11:42:20 +0900 Subject: [PATCH 01/13] :sparkles: Add Hello, World. --- backend/go.mod | 17 +++++++++++++++++ backend/go.sum | 25 +++++++++++++++++++++++++ backend/main.go | 21 +++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 backend/go.mod create mode 100644 backend/go.sum create mode 100644 backend/main.go diff --git a/backend/go.mod b/backend/go.mod new file mode 100644 index 0000000..6133036 --- /dev/null +++ b/backend/go.mod @@ -0,0 +1,17 @@ +module github.com/kstm-su/Member-Portal/backend + +go 1.22.3 + +require ( + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/gofiber/fiber/v3 v3.0.0-beta.2 // indirect + github.com/gofiber/utils/v2 v2.0.0-beta.4 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/klauspost/compress v1.17.8 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.54.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/sys v0.20.0 // indirect +) diff --git a/backend/go.sum b/backend/go.sum new file mode 100644 index 0000000..b1f0d8f --- /dev/null +++ b/backend/go.sum @@ -0,0 +1,25 @@ +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/gofiber/fiber/v3 v3.0.0-beta.2 h1:mVVgt8PTaHGup3NGl/+7U7nEoZaXJ5OComV4E+HpAao= +github.com/gofiber/fiber/v3 v3.0.0-beta.2/go.mod h1:w7sdfTY0okjZ1oVH6rSOGvuACUIt0By1iK0HKUb3uqM= +github.com/gofiber/utils/v2 v2.0.0-beta.4 h1:1gjbVFFwVwUb9arPcqiB6iEjHBwo7cHsyS41NeIW3co= +github.com/gofiber/utils/v2 v2.0.0-beta.4/go.mod h1:sdRsPU1FXX6YiDGGxd+q2aPJRMzpsxdzCXo9dz+xtOY= +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/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.54.0 h1:cCL+ZZR3z3HPLMVfEYVUMtJqVaui0+gu7Lx63unHwS0= +github.com/valyala/fasthttp v1.54.0/go.mod h1:6dt4/8olwq9QARP/TDuPmWyWcl4byhpvTJ4AAtcz+QM= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/backend/main.go b/backend/main.go new file mode 100644 index 0000000..20f704e --- /dev/null +++ b/backend/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "log" + + "github.com/gofiber/fiber/v3" +) + +func main() { + // Initialize a new Fiber app + app := fiber.New() + + // Define a route for the GET method on the root path '/' + app.Get("/", func(c fiber.Ctx) error { + // Send a string response to the client + return c.SendString("Hello, World 👋!") + }) + + // Start the server on port 3000 + log.Fatal(app.Listen(":3000")) +} From 29907f51fb313071e7f608dfa8b331cd4c40a6f1 Mon Sep 17 00:00:00 2001 From: Nlkomaru Date: Wed, 12 Jun 2024 19:47:29 +0900 Subject: [PATCH 02/13] Add oauth server model --- .github/dependabot.yml | 12 +++ backend/Dockerfile | 30 ++++++ backend/README.md | 11 ++ backend/go.mod | 34 ++++-- backend/go.sum | 100 +++++++++++++++--- backend/main.go | 19 +--- backend/router/oauth2/authorize_endpoint.go | 10 ++ backend/router/oauth2/introspect_endpoint.go | 10 ++ backend/router/oauth2/oauth.go | 14 +++ .../router/oauth2/revoke_token_endpoint.go | 9 ++ backend/router/oauth2/token_endpoint.go | 10 ++ backend/router/oauth2/user_info_endpoint.go | 9 ++ backend/router/root.go | 23 ++++ 13 files changed, 254 insertions(+), 37 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 backend/Dockerfile create mode 100644 backend/README.md create mode 100644 backend/router/oauth2/authorize_endpoint.go create mode 100644 backend/router/oauth2/introspect_endpoint.go create mode 100644 backend/router/oauth2/oauth.go create mode 100644 backend/router/oauth2/revoke_token_endpoint.go create mode 100644 backend/router/oauth2/token_endpoint.go create mode 100644 backend/router/oauth2/user_info_endpoint.go create mode 100644 backend/router/root.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..f33a02c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for more information: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +# https://containers.dev/guide/dependabot + +version: 2 +updates: + - package-ecosystem: "devcontainers" + directory: "/" + schedule: + interval: weekly diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..772cef2 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,30 @@ +FROM --platform=$BUILDPLATFORM golang:1.22.4 as build +RUN mkdir /storage + +WORKDIR /go/src/github.com/kstm-su/Member-Portal/backend/ + +COPY ./go.* ./ + +RUN --mount=type=cache,target=/go/pkg/mod go mod download + +ENV GOCACHE=/tmp/go/cache +ENV CGO_ENABLED=0 + +ARG TARGETOS +ARG TARGETARCH +ENV GOOS=$TARGETOS +ENV GOARCH=$TARGETARCH + +COPY . . +RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/tmp/go/cache \ + go build -o /Member-Portal + +FROM gcr.io/distroless/static-debian11 +WORKDIR /app +EXPOSE 8080 + +COPY --from=build /storage/ /app/storage/ +VOLUME /app/storage + +COPY --from=build /Member-Portal ./ +CMD ["./Member-Portal"] diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..c8174cf --- /dev/null +++ b/backend/README.md @@ -0,0 +1,11 @@ +# kstm-member-portal backend side + +This is the backend part of kstm-member-portal.
+It is implemented by golang and [echo](https://github.com/gofiber/fiber). + +## Getting Started +```bash +go run main.go +``` + + diff --git a/backend/go.mod b/backend/go.mod index 6133036..e0d85fc 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -3,15 +3,35 @@ module github.com/kstm-su/Member-Portal/backend go 1.22.3 require ( - github.com/andybalholm/brotli v1.1.0 // indirect - github.com/gofiber/fiber/v3 v3.0.0-beta.2 // indirect - github.com/gofiber/utils/v2 v2.0.0-beta.4 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/klauspost/compress v1.17.8 // indirect + github.com/deepmap/oapi-codegen v1.16.3 + github.com/labstack/echo/v4 v4.12.0 + github.com/oapi-codegen/runtime v1.1.1 +) + +require ( + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/getkin/kin-openapi v0.118.0 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/swag v0.19.5 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/google/uuid v1.5.0 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/invopop/yaml v0.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/labstack/gommon v0.4.2 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/perimeterx/marshmallow v1.1.4 // indirect + github.com/stretchr/testify v1.9.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.54.0 // indirect - github.com/valyala/tcplisten v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/net v0.24.0 // indirect golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/backend/go.sum b/backend/go.sum index b1f0d8f..433c729 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,25 +1,97 @@ -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/gofiber/fiber/v3 v3.0.0-beta.2 h1:mVVgt8PTaHGup3NGl/+7U7nEoZaXJ5OComV4E+HpAao= -github.com/gofiber/fiber/v3 v3.0.0-beta.2/go.mod h1:w7sdfTY0okjZ1oVH6rSOGvuACUIt0By1iK0HKUb3uqM= -github.com/gofiber/utils/v2 v2.0.0-beta.4 h1:1gjbVFFwVwUb9arPcqiB6iEjHBwo7cHsyS41NeIW3co= -github.com/gofiber/utils/v2 v2.0.0-beta.4/go.mod h1:sdRsPU1FXX6YiDGGxd+q2aPJRMzpsxdzCXo9dz+xtOY= -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/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +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/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deepmap/oapi-codegen v1.16.3 h1:GT9G86SbQtT1r8ZB+4Cybi9VGdu1P5ieNvNdEoCSbrA= +github.com/deepmap/oapi-codegen v1.16.3/go.mod h1:JD6ErqeX0nYnhdciLc61Konj3NBASREMlkHOgHn8WAM= +github.com/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFdHoLuM= +github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +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/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= +github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= +github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +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.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/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/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= +github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= +github.com/oapi-codegen/testutil v1.0.0 h1:1GI2IiMMLh2vDHr1OkNacaYU/VaApKdcmfgl4aeXAa8= +github.com/oapi-codegen/testutil v1.0.0/go.mod h1:ttCaYbHvJtHuiyeBF0tPIX+4uhEPTeizXKx28okijLw= +github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= +github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/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.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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= +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/fasthttp v1.54.0 h1:cCL+ZZR3z3HPLMVfEYVUMtJqVaui0+gu7Lx63unHwS0= -github.com/valyala/fasthttp v1.54.0/go.mod h1:6dt4/8olwq9QARP/TDuPmWyWcl4byhpvTJ4AAtcz+QM= -github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +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/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +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= diff --git a/backend/main.go b/backend/main.go index 20f704e..66cc987 100644 --- a/backend/main.go +++ b/backend/main.go @@ -1,21 +1,8 @@ package main -import ( - "log" - - "github.com/gofiber/fiber/v3" -) +import "github.com/kstm-su/Member-Portal/backend/router" func main() { - // Initialize a new Fiber app - app := fiber.New() - - // Define a route for the GET method on the root path '/' - app.Get("/", func(c fiber.Ctx) error { - // Send a string response to the client - return c.SendString("Hello, World 👋!") - }) - - // Start the server on port 3000 - log.Fatal(app.Listen(":3000")) + // Initialize a Router + router.Execute() } diff --git a/backend/router/oauth2/authorize_endpoint.go b/backend/router/oauth2/authorize_endpoint.go new file mode 100644 index 0000000..6741001 --- /dev/null +++ b/backend/router/oauth2/authorize_endpoint.go @@ -0,0 +1,10 @@ +package oauth2 + +import "github.com/labstack/echo/v4" + +func AuthorizationEndpointHandler(c echo.Context) error { + + // TODO implement this function + return nil + +} diff --git a/backend/router/oauth2/introspect_endpoint.go b/backend/router/oauth2/introspect_endpoint.go new file mode 100644 index 0000000..b7cf625 --- /dev/null +++ b/backend/router/oauth2/introspect_endpoint.go @@ -0,0 +1,10 @@ +package oauth2 + +import "github.com/labstack/echo/v4" + +func IntrospectTokenEndpointHandler(c echo.Context) error { + + // TODO implement this function + return nil + +} diff --git a/backend/router/oauth2/oauth.go b/backend/router/oauth2/oauth.go new file mode 100644 index 0000000..07647d7 --- /dev/null +++ b/backend/router/oauth2/oauth.go @@ -0,0 +1,14 @@ +package oauth2 + +import "github.com/labstack/echo/v4" + +func Setup(e *echo.Group) { + e.GET("/authorize", AuthorizationEndpointHandler) + e.POST("/authorize", AuthorizationEndpointHandler) + e.POST("/token", TokenEndpointHandler) + e.POST("/revoke", RevokeTokenEndpointHandler) + e.POST("/introspect", IntrospectTokenEndpointHandler) + e.GET("/userinfo", UserInfoEndpointHandler) + //e.GET("/.well-known/openid-configuration", OpenIDConfigurationHandler) + //e.GET("/.well-known/jwks.json", JwksHandler) +} diff --git a/backend/router/oauth2/revoke_token_endpoint.go b/backend/router/oauth2/revoke_token_endpoint.go new file mode 100644 index 0000000..e50f3e4 --- /dev/null +++ b/backend/router/oauth2/revoke_token_endpoint.go @@ -0,0 +1,9 @@ +package oauth2 + +import "github.com/labstack/echo/v4" + +func RevokeTokenEndpointHandler(c echo.Context) error { + + // TODO implement this function + return nil +} diff --git a/backend/router/oauth2/token_endpoint.go b/backend/router/oauth2/token_endpoint.go new file mode 100644 index 0000000..3ffbd94 --- /dev/null +++ b/backend/router/oauth2/token_endpoint.go @@ -0,0 +1,10 @@ +package oauth2 + +import "github.com/labstack/echo/v4" + +func TokenEndpointHandler(c echo.Context) error { + + // TODO implement this function + return nil + +} diff --git a/backend/router/oauth2/user_info_endpoint.go b/backend/router/oauth2/user_info_endpoint.go new file mode 100644 index 0000000..e682946 --- /dev/null +++ b/backend/router/oauth2/user_info_endpoint.go @@ -0,0 +1,9 @@ +package oauth2 + +import "github.com/labstack/echo/v4" + +func UserInfoEndpointHandler(c echo.Context) error { + + // TODO implement this function + return nil +} diff --git a/backend/router/root.go b/backend/router/root.go new file mode 100644 index 0000000..03031bd --- /dev/null +++ b/backend/router/root.go @@ -0,0 +1,23 @@ +package router + +import ( + "github.com/kstm-su/Member-Portal/backend/router/oauth2" + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" +) + +func Execute() { + e := echo.New() + e.Use(middleware.Logger()) + e.Use(middleware.Recover()) + + e.GET("/", func(c echo.Context) error { + return c.String(200, "Hello, World!") + }) + + oauth2Router := e.Group("/oauth2") + oauth2.Setup(oauth2Router) + + e.Logger.Fatal(e.Start(":8080")) + +} From c602c980add50dd02a0bae0cc1bd60511fec2207 Mon Sep 17 00:00:00 2001 From: Nlkomaru Date: Wed, 12 Jun 2024 20:09:51 +0900 Subject: [PATCH 03/13] Add docs for server start --- backend/README.md | 15 ++++++++++++ backend/go.mod | 19 +-------------- backend/go.sum | 62 ----------------------------------------------- 3 files changed, 16 insertions(+), 80 deletions(-) diff --git a/backend/README.md b/backend/README.md index c8174cf..be0f403 100644 --- a/backend/README.md +++ b/backend/README.md @@ -4,8 +4,23 @@ This is the backend part of kstm-member-portal.
It is implemented by golang and [echo](https://github.com/gofiber/fiber). ## Getting Started +### Go ```bash go run main.go ``` +or + +```bash +go build -o kstm-member-portal +./kstm-member-portal +``` + +### Docker +```bash +docker build -t kstm-member-portal . +docker run -p 8080:8080 kstm-member-portal +``` + + diff --git a/backend/go.mod b/backend/go.mod index e0d85fc..53ed582 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -2,28 +2,13 @@ module github.com/kstm-su/Member-Portal/backend go 1.22.3 -require ( - github.com/deepmap/oapi-codegen v1.16.3 - github.com/labstack/echo/v4 v4.12.0 - github.com/oapi-codegen/runtime v1.1.1 -) +require github.com/labstack/echo/v4 v4.12.0 require ( - github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect - github.com/getkin/kin-openapi v0.118.0 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/swag v0.19.5 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/google/uuid v1.5.0 // indirect - github.com/gorilla/mux v1.8.0 // indirect - github.com/invopop/yaml v0.1.0 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/labstack/gommon v0.4.2 // indirect - github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect - github.com/perimeterx/marshmallow v1.1.4 // indirect github.com/stretchr/testify v1.9.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect @@ -32,6 +17,4 @@ require ( golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/backend/go.sum b/backend/go.sum index 433c729..1c586ad 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,74 +1,20 @@ -github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= -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/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deepmap/oapi-codegen v1.16.3 h1:GT9G86SbQtT1r8ZB+4Cybi9VGdu1P5ieNvNdEoCSbrA= -github.com/deepmap/oapi-codegen v1.16.3/go.mod h1:JD6ErqeX0nYnhdciLc61Konj3NBASREMlkHOgHn8WAM= -github.com/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFdHoLuM= -github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -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/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= -github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= -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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= -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.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/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/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= -github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= -github.com/oapi-codegen/testutil v1.0.0 h1:1GI2IiMMLh2vDHr1OkNacaYU/VaApKdcmfgl4aeXAa8= -github.com/oapi-codegen/testutil v1.0.0/go.mod h1:ttCaYbHvJtHuiyeBF0tPIX+4uhEPTeizXKx28okijLw= -github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= -github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/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.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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= -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= @@ -85,13 +31,5 @@ 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/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -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= From dcad5c20d00afa2e39c1d58057e7aba727bb881c Mon Sep 17 00:00:00 2001 From: Nlkomaru Date: Fri, 12 Jul 2024 14:38:02 +0900 Subject: [PATCH 04/13] =?UTF-8?q?=F0=9F=91=8D=20Add=20load=20config=20meth?= =?UTF-8?q?od?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/dependabot.yml | 12 --- backend/go.mod | 20 ----- backend/go.sum | 35 -------- backend/main.go | 8 -- {backend => member-portal-backend}/Dockerfile | 60 ++++++------- {backend => member-portal-backend}/README.md | 58 ++++++------ member-portal-backend/cmd/root.go | 36 ++++++++ member-portal-backend/config/config.go | 76 ++++++++++++++++ member-portal-backend/go.mod | 42 +++++++++ member-portal-backend/go.sum | 88 +++++++++++++++++++ member-portal-backend/main.go | 14 +++ .../router/oauth2/authorize_endpoint.go | 0 .../router/oauth2/introspect_endpoint.go | 0 .../router/oauth2/oauth.go | 0 .../router/oauth2/revoke_token_endpoint.go | 0 .../router/oauth2/token_endpoint.go | 0 .../router/oauth2/user_info_endpoint.go | 0 .../router/root.go | 7 +- 18 files changed, 323 insertions(+), 133 deletions(-) delete mode 100644 .github/dependabot.yml delete mode 100644 backend/go.mod delete mode 100644 backend/go.sum delete mode 100644 backend/main.go rename {backend => member-portal-backend}/Dockerfile (95%) rename {backend => member-portal-backend}/README.md (67%) create mode 100644 member-portal-backend/cmd/root.go create mode 100644 member-portal-backend/config/config.go create mode 100644 member-portal-backend/go.mod create mode 100644 member-portal-backend/go.sum create mode 100644 member-portal-backend/main.go rename {backend => member-portal-backend}/router/oauth2/authorize_endpoint.go (100%) rename {backend => member-portal-backend}/router/oauth2/introspect_endpoint.go (100%) rename {backend => member-portal-backend}/router/oauth2/oauth.go (100%) rename {backend => member-portal-backend}/router/oauth2/revoke_token_endpoint.go (100%) rename {backend => member-portal-backend}/router/oauth2/token_endpoint.go (100%) rename {backend => member-portal-backend}/router/oauth2/user_info_endpoint.go (100%) rename {backend => member-portal-backend}/router/root.go (69%) diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index f33a02c..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,12 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for more information: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates -# https://containers.dev/guide/dependabot - -version: 2 -updates: - - package-ecosystem: "devcontainers" - directory: "/" - schedule: - interval: weekly diff --git a/backend/go.mod b/backend/go.mod deleted file mode 100644 index 53ed582..0000000 --- a/backend/go.mod +++ /dev/null @@ -1,20 +0,0 @@ -module github.com/kstm-su/Member-Portal/backend - -go 1.22.3 - -require github.com/labstack/echo/v4 v4.12.0 - -require ( - github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/labstack/gommon v0.4.2 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/stretchr/testify v1.9.0 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasttemplate v1.2.2 // indirect - golang.org/x/crypto v0.22.0 // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.5.0 // indirect -) diff --git a/backend/go.sum b/backend/go.sum deleted file mode 100644 index 1c586ad..0000000 --- a/backend/go.sum +++ /dev/null @@ -1,35 +0,0 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= -github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= -github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= -github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/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= -github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -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/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/backend/main.go b/backend/main.go deleted file mode 100644 index 66cc987..0000000 --- a/backend/main.go +++ /dev/null @@ -1,8 +0,0 @@ -package main - -import "github.com/kstm-su/Member-Portal/backend/router" - -func main() { - // Initialize a Router - router.Execute() -} diff --git a/backend/Dockerfile b/member-portal-backend/Dockerfile similarity index 95% rename from backend/Dockerfile rename to member-portal-backend/Dockerfile index 772cef2..fe16f8a 100644 --- a/backend/Dockerfile +++ b/member-portal-backend/Dockerfile @@ -1,30 +1,30 @@ -FROM --platform=$BUILDPLATFORM golang:1.22.4 as build -RUN mkdir /storage - -WORKDIR /go/src/github.com/kstm-su/Member-Portal/backend/ - -COPY ./go.* ./ - -RUN --mount=type=cache,target=/go/pkg/mod go mod download - -ENV GOCACHE=/tmp/go/cache -ENV CGO_ENABLED=0 - -ARG TARGETOS -ARG TARGETARCH -ENV GOOS=$TARGETOS -ENV GOARCH=$TARGETARCH - -COPY . . -RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/tmp/go/cache \ - go build -o /Member-Portal - -FROM gcr.io/distroless/static-debian11 -WORKDIR /app -EXPOSE 8080 - -COPY --from=build /storage/ /app/storage/ -VOLUME /app/storage - -COPY --from=build /Member-Portal ./ -CMD ["./Member-Portal"] +FROM --platform=$BUILDPLATFORM golang:1.22.4 as build +RUN mkdir /storage + +WORKDIR /go/src/github.com/kstm-su/Member-Portal/backend/ + +COPY ./go.* ./ + +RUN --mount=type=cache,target=/go/pkg/mod go mod download + +ENV GOCACHE=/tmp/go/cache +ENV CGO_ENABLED=0 + +ARG TARGETOS +ARG TARGETARCH +ENV GOOS=$TARGETOS +ENV GOARCH=$TARGETARCH + +COPY . . +RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/tmp/go/cache \ + go build -o /Member-Portal + +FROM gcr.io/distroless/static-debian11 +WORKDIR /app +EXPOSE 8080 + +COPY --from=build /storage/ /app/storage/ +VOLUME /app/storage + +COPY --from=build /Member-Portal ./ +CMD ["./Member-Portal"] diff --git a/backend/README.md b/member-portal-backend/README.md similarity index 67% rename from backend/README.md rename to member-portal-backend/README.md index be0f403..eb890c3 100644 --- a/backend/README.md +++ b/member-portal-backend/README.md @@ -1,26 +1,32 @@ -# kstm-member-portal backend side - -This is the backend part of kstm-member-portal.
-It is implemented by golang and [echo](https://github.com/gofiber/fiber). - -## Getting Started -### Go -```bash -go run main.go -``` - -or - -```bash -go build -o kstm-member-portal -./kstm-member-portal -``` - -### Docker -```bash -docker build -t kstm-member-portal . -docker run -p 8080:8080 kstm-member-portal -``` - - - +# kstm-member-portal backend side + +This is the backend part of kstm-member-portal.
+It is implemented by golang and [echo](https://github.com/gofiber/fiber). + +## Getting Started +### Go +```bash +go run main.go +``` + +or + +```bash +go build -o kstm-member-portal +./kstm-member-portal +``` + +### Docker +```bash +docker build -t kstm-member-portal . +docker run -p 8080:8080 kstm-member-portal +``` + +## Configuration +引数で設定ファイルを指定することができます。デフォルトは`/app/config.yaml`です。 +```bash +./kstm-member-portal --config /path/to/config.yaml +``` + + + diff --git a/member-portal-backend/cmd/root.go b/member-portal-backend/cmd/root.go new file mode 100644 index 0000000..29b8c3d --- /dev/null +++ b/member-portal-backend/cmd/root.go @@ -0,0 +1,36 @@ +package cmd + +import ( + "github.com/kstm-su/Member-Portal/backend/config" + "github.com/kstm-su/Member-Portal/backend/router" + "github.com/spf13/cobra" +) + +var configFile string + +const name = "member-portal" +const file = "." + name + ".yaml" + +var rootCmd = &cobra.Command{ + Use: name, + Short: "Backend server for the OAuth2 server", +} + +func init() { + flags := rootCmd.PersistentFlags() + flags.StringVarP(&configFile, "config", "c", "/app/config.yaml", "config file path (default is /app/config.yaml)") +} + +func Execute() error { + err := rootCmd.Execute() + if err != nil { + return err + } + c, err := config.Load(configFile) + if err != nil { + print(err.Error()) + return err + } + router.Execute(c) + return nil +} diff --git a/member-portal-backend/config/config.go b/member-portal-backend/config/config.go new file mode 100644 index 0000000..ed907ab --- /dev/null +++ b/member-portal-backend/config/config.go @@ -0,0 +1,76 @@ +package config + +import ( + "fmt" + "github.com/spf13/viper" + "log/slog" + "os" +) + +type Config struct { + Server struct { + Port int `yaml:"port"` + Host string `yaml:"host"` + } `yaml:"server"` + + Database struct { + Host string `yaml:"host"` + Port int `yaml:"port"` + User string `yaml:"user"` + Password string `yaml:"password"` + } `yaml:"database"` + + JWT struct { + Key struct { + PrivateKey string `yaml:"private_key"` + PublicKey string `yaml:"public_key"` + } `yaml:"key"` + Issuer string `yaml:"issuer"` + Realm string `yaml:"realm"` + KeyId string `yaml:"key_id"` + } `yaml:"jwt"` +} + +func init() { + viper.SetDefault("server.port", 8080) + viper.SetDefault("server.host", "localhost") + + viper.SetDefault("database.host", "localhost") + viper.SetDefault("database.port", 5432) + viper.SetDefault("database.user", "postgres") + viper.SetDefault("database.password", "password") + + viper.SetDefault("jwt.key.private_key", "private.pem") + viper.SetDefault("jwt.key.public_key", "public.pem") + viper.SetDefault("jwt.issuer", "localhost") + viper.SetDefault("jwt.realm", "localhost") + viper.SetDefault("jwt.key_id", "key") +} + +func Load(configFile string) (*Config, error) { + viper.SetConfigType("yaml") + viper.SetConfigFile(configFile) + + var cfg Config + if _, err := os.Stat(configFile); os.IsNotExist(err) { + err = viper.WriteConfig() + if err != nil { + return nil, fmt.Errorf("failed to write default config: %s \n", err) + } + slog.Info("設定ファイルが存在しないため、デフォルト設定ファイルを作成しました。") + } + + err := viper.ReadInConfig() + if err != nil { + return nil, fmt.Errorf("設定ファイル読み込みエラー: %s \n", err) + } + slog.Info("設定ファイルを読み込みました。") + + err = viper.Unmarshal(&cfg) + if err != nil { + return nil, fmt.Errorf("unmarshal error: %s \n", err) + } + slog.Info("設定ファイルを構造体に変換しました。") + + return &cfg, nil +} diff --git a/member-portal-backend/go.mod b/member-portal-backend/go.mod new file mode 100644 index 0000000..64dbec0 --- /dev/null +++ b/member-portal-backend/go.mod @@ -0,0 +1,42 @@ +module github.com/kstm-su/Member-Portal/backend + +go 1.22.3 + +require ( + github.com/labstack/echo/v4 v4.12.0 + github.com/spf13/cobra v1.8.1 +) + +require ( + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/labstack/gommon v0.4.2 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.19.0 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/crypto v0.22.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.20.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/member-portal-backend/go.sum b/member-portal-backend/go.sum new file mode 100644 index 0000000..73515a4 --- /dev/null +++ b/member-portal-backend/go.sum @@ -0,0 +1,88 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= +github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +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/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +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= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +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= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +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/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/member-portal-backend/main.go b/member-portal-backend/main.go new file mode 100644 index 0000000..acd68be --- /dev/null +++ b/member-portal-backend/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "github.com/kstm-su/Member-Portal/backend/cmd" +) + +func main() { + // Initialize a Router + err := cmd.Execute() + if err != nil { + err.Error() + return + } +} diff --git a/backend/router/oauth2/authorize_endpoint.go b/member-portal-backend/router/oauth2/authorize_endpoint.go similarity index 100% rename from backend/router/oauth2/authorize_endpoint.go rename to member-portal-backend/router/oauth2/authorize_endpoint.go diff --git a/backend/router/oauth2/introspect_endpoint.go b/member-portal-backend/router/oauth2/introspect_endpoint.go similarity index 100% rename from backend/router/oauth2/introspect_endpoint.go rename to member-portal-backend/router/oauth2/introspect_endpoint.go diff --git a/backend/router/oauth2/oauth.go b/member-portal-backend/router/oauth2/oauth.go similarity index 100% rename from backend/router/oauth2/oauth.go rename to member-portal-backend/router/oauth2/oauth.go diff --git a/backend/router/oauth2/revoke_token_endpoint.go b/member-portal-backend/router/oauth2/revoke_token_endpoint.go similarity index 100% rename from backend/router/oauth2/revoke_token_endpoint.go rename to member-portal-backend/router/oauth2/revoke_token_endpoint.go diff --git a/backend/router/oauth2/token_endpoint.go b/member-portal-backend/router/oauth2/token_endpoint.go similarity index 100% rename from backend/router/oauth2/token_endpoint.go rename to member-portal-backend/router/oauth2/token_endpoint.go diff --git a/backend/router/oauth2/user_info_endpoint.go b/member-portal-backend/router/oauth2/user_info_endpoint.go similarity index 100% rename from backend/router/oauth2/user_info_endpoint.go rename to member-portal-backend/router/oauth2/user_info_endpoint.go diff --git a/backend/router/root.go b/member-portal-backend/router/root.go similarity index 69% rename from backend/router/root.go rename to member-portal-backend/router/root.go index 03031bd..a8ad261 100644 --- a/backend/router/root.go +++ b/member-portal-backend/router/root.go @@ -1,12 +1,14 @@ package router import ( + "github.com/kstm-su/Member-Portal/backend/config" "github.com/kstm-su/Member-Portal/backend/router/oauth2" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" + "strconv" ) -func Execute() { +func Execute(c *config.Config) { e := echo.New() e.Use(middleware.Logger()) e.Use(middleware.Recover()) @@ -18,6 +20,7 @@ func Execute() { oauth2Router := e.Group("/oauth2") oauth2.Setup(oauth2Router) - e.Logger.Fatal(e.Start(":8080")) + var port = c.Server.Port + e.Logger.Fatal(e.Start(":" + strconv.Itoa(port))) } From c047b2cc07e622142622b6e893e62342e26d817f Mon Sep 17 00:00:00 2001 From: Nlkomaru Date: Mon, 22 Jul 2024 14:42:24 +0900 Subject: [PATCH 05/13] =?UTF-8?q?=F0=9F=91=8D=20Add=20authorize=20get=20en?= =?UTF-8?q?dpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- member-portal-backend/config/config.go | 13 +- .../public/view/authorize.html | 127 ++++++++++++++++++ .../router/oauth2/authorize_endpoint.go | 75 ++++++++++- member-portal-backend/router/oauth2/oauth.go | 4 +- member-portal-backend/router/root.go | 16 +++ 5 files changed, 227 insertions(+), 8 deletions(-) create mode 100644 member-portal-backend/public/view/authorize.html diff --git a/member-portal-backend/config/config.go b/member-portal-backend/config/config.go index ed907ab..e76f8dd 100644 --- a/member-portal-backend/config/config.go +++ b/member-portal-backend/config/config.go @@ -13,6 +13,10 @@ type Config struct { Host string `yaml:"host"` } `yaml:"server"` + File struct { + Base string `yaml:"base"` + } `yaml:"file"` + Database struct { Host string `yaml:"host"` Port int `yaml:"port"` @@ -35,6 +39,8 @@ func init() { viper.SetDefault("server.port", 8080) viper.SetDefault("server.host", "localhost") + viper.SetDefault("file.base", "/app") + viper.SetDefault("database.host", "localhost") viper.SetDefault("database.port", 5432) viper.SetDefault("database.user", "postgres") @@ -47,11 +53,12 @@ func init() { viper.SetDefault("jwt.key_id", "key") } +var Cfg Config + func Load(configFile string) (*Config, error) { viper.SetConfigType("yaml") viper.SetConfigFile(configFile) - var cfg Config if _, err := os.Stat(configFile); os.IsNotExist(err) { err = viper.WriteConfig() if err != nil { @@ -66,11 +73,11 @@ func Load(configFile string) (*Config, error) { } slog.Info("設定ファイルを読み込みました。") - err = viper.Unmarshal(&cfg) + err = viper.Unmarshal(&Cfg) if err != nil { return nil, fmt.Errorf("unmarshal error: %s \n", err) } slog.Info("設定ファイルを構造体に変換しました。") - return &cfg, nil + return &Cfg, nil } diff --git a/member-portal-backend/public/view/authorize.html b/member-portal-backend/public/view/authorize.html new file mode 100644 index 0000000..f0355cc --- /dev/null +++ b/member-portal-backend/public/view/authorize.html @@ -0,0 +1,127 @@ +{{define "authorize"}} + + + + kstm member portal auth + + + + +

+
+
+
+ logo + kstm member portal +
+
+
+

+ +
+ + + + + + + + +
+ ユーザーネーム + +
+
+ パスワード +
+ + +
+
+
+ +
+
+
+
+
+
+ + + +{{end}} \ No newline at end of file diff --git a/member-portal-backend/router/oauth2/authorize_endpoint.go b/member-portal-backend/router/oauth2/authorize_endpoint.go index 6741001..9e1b7b5 100644 --- a/member-portal-backend/router/oauth2/authorize_endpoint.go +++ b/member-portal-backend/router/oauth2/authorize_endpoint.go @@ -1,10 +1,79 @@ package oauth2 -import "github.com/labstack/echo/v4" +import ( + "encoding/json" + "net/http" + "os" + "strings" -func AuthorizationEndpointHandler(c echo.Context) error { + "github.com/kstm-su/Member-Portal/backend/config" + "github.com/labstack/echo/v4" +) + +type ClientData struct { + ClientId string `json:"clientId"` + ClientName string `json:"clientName"` + RedirectUri string `json:"redirectUri"` + ApplicationName string `json:"applicationName"` +} + +func AuthorizationGetEndpointHandler(c echo.Context) error { + responseType := c.QueryParam("response_type") + clientId := c.QueryParam("client_id") + redirectUri := c.QueryParam("redirect_uri") + scope := c.QueryParam("scope") + state := c.QueryParam("state") + codeChallenge := c.QueryParam("code_challenge") + codeChallengeMethod := c.QueryParam("code_challenge_method") + nonce := c.QueryParam("nonce") + + if clientId == "" || redirectUri == "" || scope == "" || responseType != "code" || state == "" || nonce == "" { + return c.String(http.StatusBadRequest, "Invalid request") + } + + if codeChallenge != "" && codeChallengeMethod != "S256" { + return c.String(http.StatusBadRequest, "Invalid request") + } + + conf := config.Cfg + + clientDataFile := conf.File.Base + "/clients/" + clientId + ".json" + if _, err := os.Stat(clientDataFile); os.IsNotExist(err) { + return c.String(http.StatusBadRequest, "Invalid client") + } + + fileContent, err := os.ReadFile(clientDataFile) + if err != nil { + return c.String(http.StatusInternalServerError, "Internal server error") + } + + var clientData ClientData + if err := json.Unmarshal(fileContent, &clientData); err != nil { + return err // Handle error appropriately + } + + if strings.HasPrefix(redirectUri, clientData.RedirectUri) { + return c.String(http.StatusBadRequest, "Invalid request") + } + + model := map[string]interface{}{ + "clientId": clientData.ClientId, + "clientName": clientData.ClientName, + "redirectUri": redirectUri, + "responseType": "code", + "state": state, + "scope": scope, + "codeChallenge": codeChallenge, + "codeChallengeMethod": codeChallengeMethod, + "nonce": nonce, + "applicationName": clientData.ApplicationName, + } + + return c.Render(http.StatusOK, "authorize", model) +} + +func AuthorizationPostEndpointHandler(c echo.Context) error { // TODO implement this function return nil - } diff --git a/member-portal-backend/router/oauth2/oauth.go b/member-portal-backend/router/oauth2/oauth.go index 07647d7..68b56e0 100644 --- a/member-portal-backend/router/oauth2/oauth.go +++ b/member-portal-backend/router/oauth2/oauth.go @@ -3,8 +3,8 @@ package oauth2 import "github.com/labstack/echo/v4" func Setup(e *echo.Group) { - e.GET("/authorize", AuthorizationEndpointHandler) - e.POST("/authorize", AuthorizationEndpointHandler) + e.GET("/authorize", AuthorizationGetEndpointHandler) + e.POST("/authorize", AuthorizationPostEndpointHandler) e.POST("/token", TokenEndpointHandler) e.POST("/revoke", RevokeTokenEndpointHandler) e.POST("/introspect", IntrospectTokenEndpointHandler) diff --git a/member-portal-backend/router/root.go b/member-portal-backend/router/root.go index a8ad261..34384e1 100644 --- a/member-portal-backend/router/root.go +++ b/member-portal-backend/router/root.go @@ -5,11 +5,27 @@ import ( "github.com/kstm-su/Member-Portal/backend/router/oauth2" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" + "io" "strconv" + "text/template" ) +type TemplateRegistry struct { + templates *template.Template +} + +func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c echo.Context) error { + return t.templates.ExecuteTemplate(w, name, data) +} + func Execute(c *config.Config) { e := echo.New() + + reg := &TemplateRegistry{ + templates: template.Must(template.ParseGlob("public/view/*.html")), + } + e.Renderer = reg + e.Use(middleware.Logger()) e.Use(middleware.Recover()) From 88981e8cc1abf56dc1b9b4d110fc28f2c9b8c6b4 Mon Sep 17 00:00:00 2001 From: Nlkomaru Date: Wed, 7 Aug 2024 14:25:24 +0900 Subject: [PATCH 06/13] :recycle: Change to binding query to struct --- member-portal-backend/cmd/root.go | 5 +- member-portal-backend/config/config.go | 6 ++ member-portal-backend/main.go | 3 +- .../router/oauth2/authorize_endpoint.go | 65 +++++++++++++------ member-portal-backend/router/root.go | 5 ++ 5 files changed, 61 insertions(+), 23 deletions(-) diff --git a/member-portal-backend/cmd/root.go b/member-portal-backend/cmd/root.go index 29b8c3d..8d9d535 100644 --- a/member-portal-backend/cmd/root.go +++ b/member-portal-backend/cmd/root.go @@ -9,7 +9,6 @@ import ( var configFile string const name = "member-portal" -const file = "." + name + ".yaml" var rootCmd = &cobra.Command{ Use: name, @@ -17,11 +16,14 @@ var rootCmd = &cobra.Command{ } func init() { + // コマンドフラグの設定 flags := rootCmd.PersistentFlags() + // 設定ファイルのパスを指定するフラグ --config, -c flags.StringVarP(&configFile, "config", "c", "/app/config.yaml", "config file path (default is /app/config.yaml)") } func Execute() error { + // コマンドの実行 err := rootCmd.Execute() if err != nil { return err @@ -31,6 +33,7 @@ func Execute() error { print(err.Error()) return err } + // ルーターの実行 router.Execute(c) return nil } diff --git a/member-portal-backend/config/config.go b/member-portal-backend/config/config.go index e76f8dd..0e20e99 100644 --- a/member-portal-backend/config/config.go +++ b/member-portal-backend/config/config.go @@ -7,6 +7,7 @@ import ( "os" ) +// 設定ファイルの構造体 type Config struct { Server struct { Port int `yaml:"port"` @@ -36,6 +37,7 @@ type Config struct { } func init() { + //デフォルト設定ファイルの設定 viper.SetDefault("server.port", 8080) viper.SetDefault("server.host", "localhost") @@ -56,9 +58,11 @@ func init() { var Cfg Config func Load(configFile string) (*Config, error) { + //設定ファイの初期化 viper.SetConfigType("yaml") viper.SetConfigFile(configFile) + //設定ファイルの存在チェック ない場合はデフォルト設定ファイルを作成 if _, err := os.Stat(configFile); os.IsNotExist(err) { err = viper.WriteConfig() if err != nil { @@ -67,12 +71,14 @@ func Load(configFile string) (*Config, error) { slog.Info("設定ファイルが存在しないため、デフォルト設定ファイルを作成しました。") } + //設定ファイルの読み込み err := viper.ReadInConfig() if err != nil { return nil, fmt.Errorf("設定ファイル読み込みエラー: %s \n", err) } slog.Info("設定ファイルを読み込みました。") + //設定ファイルを構造体に変換 err = viper.Unmarshal(&Cfg) if err != nil { return nil, fmt.Errorf("unmarshal error: %s \n", err) diff --git a/member-portal-backend/main.go b/member-portal-backend/main.go index acd68be..29d292f 100644 --- a/member-portal-backend/main.go +++ b/member-portal-backend/main.go @@ -5,7 +5,8 @@ import ( ) func main() { - // Initialize a Router + // コマンドの実行 + // flow: コマンド -> コンフィグ -> ルーター err := cmd.Execute() if err != nil { err.Error() diff --git a/member-portal-backend/router/oauth2/authorize_endpoint.go b/member-portal-backend/router/oauth2/authorize_endpoint.go index 9e1b7b5..45f6bd6 100644 --- a/member-portal-backend/router/oauth2/authorize_endpoint.go +++ b/member-portal-backend/router/oauth2/authorize_endpoint.go @@ -17,58 +17,81 @@ type ClientData struct { ApplicationName string `json:"applicationName"` } +type AuthorizeRequest struct { + ResponseType string `query:"response_type"` + ClientId string `query:"client_id"` + RedirectUri string `query:"redirect_uri"` + Scope string `query:"scope"` + State string `query:"state"` + CodeChallenge string `query:"code_challenge"` + CodeChallengeMethod string `query:"code_challenge_method"` + Nonce string `query:"nonce"` +} + func AuthorizationGetEndpointHandler(c echo.Context) error { - responseType := c.QueryParam("response_type") - clientId := c.QueryParam("client_id") - redirectUri := c.QueryParam("redirect_uri") - scope := c.QueryParam("scope") - state := c.QueryParam("state") - codeChallenge := c.QueryParam("code_challenge") - codeChallengeMethod := c.QueryParam("code_challenge_method") - nonce := c.QueryParam("nonce") - - if clientId == "" || redirectUri == "" || scope == "" || responseType != "code" || state == "" || nonce == "" { - return c.String(http.StatusBadRequest, "Invalid request") + var r AuthorizeRequest + //構造体にリクエストの値をバインド + if err := c.Bind(&r); err != nil { + return err + } + //クエリパラメータの値をチェック + if r.ClientId == "" || r.RedirectUri == "" || r.Scope == "" { + return c.String(http.StatusBadRequest, "Invalid request(ClientId, RedirectUri, Scope is required)") } - if codeChallenge != "" && codeChallengeMethod != "S256" { - return c.String(http.StatusBadRequest, "Invalid request") + if r.ResponseType != "code" { + return c.String(http.StatusBadRequest, "Response type is not supported(Only code is supported)") + } + + if r.State == "" || r.Nonce == "" { + return c.String(http.StatusBadRequest, "Invalid request(State, Nonce is required for security)") } + if r.CodeChallenge != "" && r.CodeChallengeMethod != "S256" { + return c.String(http.StatusBadRequest, "This server only supports S256 code challenge method") + } + //設定ファイルの読み込み conf := config.Cfg - clientDataFile := conf.File.Base + "/clients/" + clientId + ".json" + //クライアントIDのファイルパス + clientDataFile := conf.File.Base + "/clients/" + r.ClientId + ".json" + //クライアントIDのファイルが存在しない場合 if _, err := os.Stat(clientDataFile); os.IsNotExist(err) { return c.String(http.StatusBadRequest, "Invalid client") } + //クライアントIDのファイルを読み込む fileContent, err := os.ReadFile(clientDataFile) if err != nil { return c.String(http.StatusInternalServerError, "Internal server error") } + //クライアントIDのファイルを構造体にバインド var clientData ClientData if err := json.Unmarshal(fileContent, &clientData); err != nil { return err // Handle error appropriately } - if strings.HasPrefix(redirectUri, clientData.RedirectUri) { + //リダイレクトURIが一致しない場合 + if strings.HasPrefix(r.RedirectUri, clientData.RedirectUri) { return c.String(http.StatusBadRequest, "Invalid request") } + //モデルの作成 model := map[string]interface{}{ "clientId": clientData.ClientId, "clientName": clientData.ClientName, - "redirectUri": redirectUri, + "redirectUri": r.RedirectUri, "responseType": "code", - "state": state, - "scope": scope, - "codeChallenge": codeChallenge, - "codeChallengeMethod": codeChallengeMethod, - "nonce": nonce, + "state": r.State, + "scope": r.Scope, + "codeChallenge": r.CodeChallenge, + "codeChallengeMethod": r.CodeChallengeMethod, + "nonce": r.Nonce, "applicationName": clientData.ApplicationName, } + //テンプレートをレンダリング 認証画面を表示 return c.Render(http.StatusOK, "authorize", model) } diff --git a/member-portal-backend/router/root.go b/member-portal-backend/router/root.go index 34384e1..d579977 100644 --- a/member-portal-backend/router/root.go +++ b/member-portal-backend/router/root.go @@ -21,21 +21,26 @@ func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c func Execute(c *config.Config) { e := echo.New() + //テンプレートエンジンの設定 reg := &TemplateRegistry{ templates: template.Must(template.ParseGlob("public/view/*.html")), } e.Renderer = reg + //ミドルウェアの設定 e.Use(middleware.Logger()) e.Use(middleware.Recover()) + //ルーティングの設定 e.GET("/", func(c echo.Context) error { return c.String(200, "Hello, World!") }) + //OAuth2のエンドポイントの設定 oauth2Router := e.Group("/oauth2") oauth2.Setup(oauth2Router) + //サーバーの起動 var port = c.Server.Port e.Logger.Fatal(e.Start(":" + strconv.Itoa(port))) From 5704d667db05d375da054472dbcb3f545779895a Mon Sep 17 00:00:00 2001 From: Nlkomaru Date: Sat, 24 Aug 2024 16:23:50 +0900 Subject: [PATCH 07/13] :construction_worker: Add docker image build actions. --- .github/workflows/build.yml | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..a31ae32 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,42 @@ +name: Build docker image + +on: + workflow_dispatch: + push: + branches: + - main + +jobs: + push: + name: "member-portal:${{ matrix.tag }}" + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + tag: + - backend + - frontend + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-qemu-action@v3 + - uses: docker/setup-buildx-action@v3 + with: + buildkitd-flags: --debug + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name : Add short sha + run: echo "GITHUB_SHA_SHORT=$(echo $GITHUB_SHA | head -c7)" >> $GITHUB_ENV + - uses: docker/build-push-action@v6 + with: + context: . + file: "./member-portal-${{ matrix.tag }}/Dockerfile" + platforms: linux/amd64 + push: true + tags: | + ghcr.io/kstm-su/member-portal/${{ matrix.tag }}:latest + ghcr.io/kstm-su/member-portal/${{ matrix.tag }}:${{ env.GITHUB_SHA_SHORT }} + cache-from: type=gha,scope=member-portal-${{ matrix.tag }} + cache-to: type=gha,mode=max,scope=member-portal-${{ matrix.tag }} From 81f33698bb563fd076466b55ddd45a418d326ccc Mon Sep 17 00:00:00 2001 From: Nlkomaru Date: Thu, 29 Aug 2024 03:13:58 +0900 Subject: [PATCH 08/13] =?UTF-8?q?=F0=9F=91=8D=20Add=20database=20initializ?= =?UTF-8?q?e=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 4 +- member-portal-backend/Dockerfile | 11 +++- member-portal-backend/cmd/root.go | 3 + member-portal-backend/config/config.go | 45 ++++++++++++--- member-portal-backend/database/database.go | 35 ++++++++++++ member-portal-backend/go.mod | 20 +++++-- member-portal-backend/go.sum | 51 ++++++++++++++--- member-portal-backend/models/user.go | 66 ++++++++++++++++++++++ 8 files changed, 209 insertions(+), 26 deletions(-) create mode 100644 member-portal-backend/database/database.go create mode 100644 member-portal-backend/models/user.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a31ae32..cfe8f9a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,8 +31,8 @@ jobs: run: echo "GITHUB_SHA_SHORT=$(echo $GITHUB_SHA | head -c7)" >> $GITHUB_ENV - uses: docker/build-push-action@v6 with: - context: . - file: "./member-portal-${{ matrix.tag }}/Dockerfile" + context: ./member-portal-${{ matrix.tag }} + file: ./member-portal-${{ matrix.tag }}/Dockerfile platforms: linux/amd64 push: true tags: | diff --git a/member-portal-backend/Dockerfile b/member-portal-backend/Dockerfile index fe16f8a..bc92ebe 100644 --- a/member-portal-backend/Dockerfile +++ b/member-portal-backend/Dockerfile @@ -8,16 +8,18 @@ COPY ./go.* ./ RUN --mount=type=cache,target=/go/pkg/mod go mod download ENV GOCACHE=/tmp/go/cache -ENV CGO_ENABLED=0 +ENV CGO_ENABLED=1 ARG TARGETOS ARG TARGETARCH ENV GOOS=$TARGETOS ENV GOARCH=$TARGETARCH +RUN apt update && apt install -y gcc + COPY . . RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/tmp/go/cache \ - go build -o /Member-Portal + go build -ldflags '-extldflags "-static"' -o /Member-Portal FROM gcr.io/distroless/static-debian11 WORKDIR /app @@ -26,5 +28,8 @@ EXPOSE 8080 COPY --from=build /storage/ /app/storage/ VOLUME /app/storage +COPY --from=build /go/src/github.com/kstm-su/Member-Portal/backend/public /app/public + COPY --from=build /Member-Portal ./ -CMD ["./Member-Portal"] + +CMD ["./Member-Portal"] \ No newline at end of file diff --git a/member-portal-backend/cmd/root.go b/member-portal-backend/cmd/root.go index 8d9d535..a838d2a 100644 --- a/member-portal-backend/cmd/root.go +++ b/member-portal-backend/cmd/root.go @@ -2,6 +2,7 @@ package cmd import ( "github.com/kstm-su/Member-Portal/backend/config" + "github.com/kstm-su/Member-Portal/backend/database" "github.com/kstm-su/Member-Portal/backend/router" "github.com/spf13/cobra" ) @@ -33,6 +34,8 @@ func Execute() error { print(err.Error()) return err } + // データベースの初期化 + database.InitDatabase(c) // ルーターの実行 router.Execute(c) return nil diff --git a/member-portal-backend/config/config.go b/member-portal-backend/config/config.go index 0e20e99..7425f80 100644 --- a/member-portal-backend/config/config.go +++ b/member-portal-backend/config/config.go @@ -1,9 +1,11 @@ package config import ( + "crypto/rand" "fmt" "github.com/spf13/viper" "log/slog" + "math/big" "os" ) @@ -19,10 +21,16 @@ type Config struct { } `yaml:"file"` Database struct { - Host string `yaml:"host"` - Port int `yaml:"port"` - User string `yaml:"user"` - Password string `yaml:"password"` + Type string `yaml:"type"` + SQLite struct { + Path string `yaml:"path"` + } `yaml:"sqlite"` + Postgres struct { + Host string `yaml:"host"` + Port int `yaml:"port"` + User string `yaml:"user"` + Password string `yaml:"password"` + } `yaml:"postgres"` } `yaml:"database"` JWT struct { @@ -34,6 +42,11 @@ type Config struct { Realm string `yaml:"realm"` KeyId string `yaml:"key_id"` } `yaml:"jwt"` + + Password struct { + Pepper string `yaml:"pepper"` + Algorithm string `yaml:"algorithm"` + } `yaml:"password"` } func init() { @@ -43,16 +56,18 @@ func init() { viper.SetDefault("file.base", "/app") - viper.SetDefault("database.host", "localhost") - viper.SetDefault("database.port", 5432) - viper.SetDefault("database.user", "postgres") - viper.SetDefault("database.password", "password") + viper.SetDefault("database.type", "sqlite") + + viper.SetDefault("database.sqlite.path", "/app/db.sqlite3") viper.SetDefault("jwt.key.private_key", "private.pem") viper.SetDefault("jwt.key.public_key", "public.pem") viper.SetDefault("jwt.issuer", "localhost") viper.SetDefault("jwt.realm", "localhost") viper.SetDefault("jwt.key_id", "key") + + viper.SetDefault("password.pepper", generateRandomString(30)) + viper.SetDefault("password.algorithm", "argon2") } var Cfg Config @@ -87,3 +102,17 @@ func Load(configFile string) (*Config, error) { return &Cfg, nil } + +func generateRandomString(n int) string { + const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-" + ret := make([]byte, n) + for i := 0; i < n; i++ { + num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters)))) + if err != nil { + return "" + } + ret[i] = letters[num.Int64()] + } + + return string(ret) +} diff --git a/member-portal-backend/database/database.go b/member-portal-backend/database/database.go new file mode 100644 index 0000000..5d32c77 --- /dev/null +++ b/member-portal-backend/database/database.go @@ -0,0 +1,35 @@ +package database + +import ( + "fmt" + "github.com/kstm-su/Member-Portal/backend/config" + "github.com/kstm-su/Member-Portal/backend/models" + "gorm.io/driver/postgres" + "gorm.io/driver/sqlite" + "gorm.io/gorm" + "log" +) + +var DB *gorm.DB + +func InitDatabase(c *config.Config) { + var err error + log.Println("Connecting to database") + switch c.Database.Type { + case "sqlite": + DB, err = gorm.Open(sqlite.Open(c.Database.SQLite.Path), &gorm.Config{}) + case "postgres": + dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=postgres port=%d", c.Database.Postgres.Host, c.Database.Postgres.User, c.Database.Postgres.Password, c.Database.Postgres.Port) + DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{}) + } + if err != nil { + log.Fatal("failed to connect database") + } + log.Println("Connected to database") + + // Auto-migrate the models + err = DB.AutoMigrate(&models.User{}, &models.Auth{}, &models.Role{}, &models.Affiliation{}, &models.Faculty{}, &models.Contact{}, &models.Name{}, &models.Profile{}, &models.ActivityLog{}) + if err != nil { + log.Fatal("failed to migrate database") + } +} diff --git a/member-portal-backend/go.mod b/member-portal-backend/go.mod index 64dbec0..f6babe2 100644 --- a/member-portal-backend/go.mod +++ b/member-portal-backend/go.mod @@ -5,6 +5,10 @@ go 1.22.3 require ( github.com/labstack/echo/v4 v4.12.0 github.com/spf13/cobra v1.8.1 + github.com/spf13/viper v1.19.0 + gorm.io/driver/postgres v1.5.9 + gorm.io/driver/sqlite v1.5.6 + gorm.io/gorm v1.25.11 ) require ( @@ -12,10 +16,17 @@ require ( github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.6.0 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect github.com/labstack/gommon v0.4.2 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect @@ -24,18 +35,17 @@ require ( github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.19.0 // indirect - github.com/stretchr/testify v1.9.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.22.0 // indirect + golang.org/x/crypto v0.26.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.20.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/member-portal-backend/go.sum b/member-portal-backend/go.sum index 73515a4..b7aed26 100644 --- a/member-portal-backend/go.sum +++ b/member-portal-backend/go.sum @@ -1,16 +1,36 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= +github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= @@ -22,13 +42,17 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= @@ -51,6 +75,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= @@ -66,23 +91,33 @@ 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= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= -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.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8= +gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE= +gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= +gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= +gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= diff --git a/member-portal-backend/models/user.go b/member-portal-backend/models/user.go new file mode 100644 index 0000000..40b9920 --- /dev/null +++ b/member-portal-backend/models/user.go @@ -0,0 +1,66 @@ +package models + +type User struct { + UserID string `gorm:"primaryKey;column:user_id"` + Nickname string `gorm:"column:nickname"` + Auth Auth + Affiliation Affiliation + Contact Contact + Name Name + Profile Profile + ActivityLogs []ActivityLog +} + +type Auth struct { + UserID string `gorm:"primaryKey;column:user_id"` + HashedPassword string `gorm:"column:hashed_password"` + RoleID int `gorm:"column:role_id"` + Role Role `gorm:"foreignKey:RoleID"` +} + +type Role struct { + RoleID int `gorm:"primaryKey;column:role_id"` + RoleName string `gorm:"column:role_name"` +} + +type Affiliation struct { + UserID string `gorm:"primaryKey;column:user_id"` + FacultyID int `gorm:"column:faculty_id"` + Grade int `gorm:"column:grade"` + Faculty Faculty `gorm:"foreignKey:FacultyID"` +} + +type Faculty struct { + FacultyID int `gorm:"primaryKey;column:faculty_id"` + FacultyName string `gorm:"column:faculty_name"` + DepartmentName string `gorm:"column:department_name"` +} + +type Contact struct { + UserID string `gorm:"primaryKey;column:user_id"` + SchoolEmail string `gorm:"column:school_email"` + SubEmail string `gorm:"column:sub_email"` + DiscordID string `gorm:"column:discord_id"` + GithubID string `gorm:"column:github_id"` + PhoneNumber string `gorm:"column:phone_number"` +} + +type Name struct { + UserID string `gorm:"primaryKey;column:user_id"` + FirstName string `gorm:"column:first_name"` + LastName string `gorm:"column:last_name"` + MiddleName string `gorm:"column:middle_name"` +} + +type Profile struct { + UserID string `gorm:"primaryKey;column:user_id"` + ProfileImage string `gorm:"column:profile_image"` + Bio string `gorm:"column:bio"` +} + +type ActivityLog struct { + ActivityID int `gorm:"primaryKey;column:activity_id"` + UserID string `gorm:"column:user_id"` + ActivityDate string `gorm:"column:activity_date"` + ActivityDescription string `gorm:"column:activity_description"` +} From 70598b97b268e28855c73aebcbbec8b09710092f Mon Sep 17 00:00:00 2001 From: Nlkomaru Date: Thu, 29 Aug 2024 04:55:33 +0900 Subject: [PATCH 09/13] =?UTF-8?q?=F0=9F=91=8DAdd=20password=20check=20meth?= =?UTF-8?q?od=20and=20authorize=20post=20endpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- member-portal-backend/config/config.go | 2 +- member-portal-backend/crypto/crypto.go | 82 +++++++++++++ member-portal-backend/crypto/crypto_test.go | 34 ++++++ .../public/view/authorize.html | 8 +- .../router/oauth2/authorize_endpoint.go | 115 +++++++++++++++++- 5 files changed, 232 insertions(+), 9 deletions(-) create mode 100644 member-portal-backend/crypto/crypto.go create mode 100644 member-portal-backend/crypto/crypto_test.go diff --git a/member-portal-backend/config/config.go b/member-portal-backend/config/config.go index 7425f80..4ac4f9e 100644 --- a/member-portal-backend/config/config.go +++ b/member-portal-backend/config/config.go @@ -77,7 +77,7 @@ func Load(configFile string) (*Config, error) { viper.SetConfigType("yaml") viper.SetConfigFile(configFile) - //設定ファイルの存在チェック ない場合はデフォルト設定ファイルを作成 + //設定ファイルの存在チェック ない場合はデフォルト設定ファイルを作成 if _, err := os.Stat(configFile); os.IsNotExist(err) { err = viper.WriteConfig() if err != nil { diff --git a/member-portal-backend/crypto/crypto.go b/member-portal-backend/crypto/crypto.go new file mode 100644 index 0000000..2019033 --- /dev/null +++ b/member-portal-backend/crypto/crypto.go @@ -0,0 +1,82 @@ +package crypto + +import ( + "bytes" + "crypto/rand" + "encoding/base64" + "fmt" + "github.com/kstm-su/Member-Portal/backend/config" + "golang.org/x/crypto/argon2" + _ "golang.org/x/crypto/argon2" + "math/big" + "strings" +) + +func PasswordEncrypt(password string) string { + salt := []byte(GenerateRandomString(30)) + pepper := config.Cfg.Password.Pepper + + memory := 64 * 1024 + iterations := 4 + parallelism := 1 + keyLen := 32 + + encoded := passwordEncryptWithParams(password, string(salt), pepper, memory, parallelism, iterations, keyLen) + + return encoded +} + +func passwordEncryptWithParams(password string, salt string, pepper string, memory int, parallelism int, iterations int, keyLen int) string { + withPepper := password + pepper + + hash := argon2.IDKey([]byte(withPepper), []byte(salt), uint32(iterations), uint32(memory), uint8(parallelism), uint32(keyLen)) + + b64Salt := base64.RawStdEncoding.EncodeToString([]byte(salt)) + b64Hash := base64.RawStdEncoding.EncodeToString(hash) + + encodedHash := fmt.Sprintf("$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s", + argon2.Version, memory, iterations, parallelism, b64Salt, b64Hash) + + return encodedHash +} + +func VerifyPassword(hash string, password string) bool { + parts := strings.Split(hash, "$") + if len(parts) != 6 { + return false + } + + salt, err := base64.RawStdEncoding.DecodeString(parts[4]) + if err != nil { + return false + } + + decodedHash, err := base64.RawStdEncoding.DecodeString(parts[5]) + if err != nil { + return false + } + + withPepper := password + config.Cfg.Password.Pepper + + genHash := argon2.IDKey([]byte(withPepper), salt, uint32(4), uint32(64*1024), uint8(1), uint32(32)) + + if bytes.Equal(decodedHash, genHash) { + return true + } + + return false +} + +func GenerateRandomString(n int) string { + const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-" + ret := make([]byte, n) + for i := 0; i < n; i++ { + num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters)))) + if err != nil { + return "" + } + ret[i] = letters[num.Int64()] + } + + return string(ret) +} diff --git a/member-portal-backend/crypto/crypto_test.go b/member-portal-backend/crypto/crypto_test.go new file mode 100644 index 0000000..1db2b29 --- /dev/null +++ b/member-portal-backend/crypto/crypto_test.go @@ -0,0 +1,34 @@ +package crypto + +import ( + "testing" +) + +func TestPasswordEncrypt(t *testing.T) { + password := "password" + encryptedPassword := passwordEncryptWithParams(password, "salt-salt", "pepper", 64*1024, 1, 4, 32) + if encryptedPassword != "$argon2id$v=19$m=65536,t=4,p=1$c2FsdC1zYWx0$6JNmlGvpjNKYpQNSJdGNfAJQ7+upIXwebdDMWcJf30g" { + t.Errorf("PasswordEncrypt failed, expected $argon2id$v=19$m=65536,t=4,p=1$c2FsdC1zYWx0$6JNmlGvpjNKYpQNSJdGNfAJQ7+upIXwebdDMWcJf30g, got %s", encryptedPassword) + } +} + +func TestVerifyPassword(t *testing.T) { + password := "password" + encryptedPassword := "$argon2id$v=19$m=65536,t=4,p=1$cmFmamRhc2poZmRzYWpoa2xhc2Rma2po$i39rjDYap4n6eA2XhCusp5wHGPKBBpM0Lg9S82kw0Ec" + if !VerifyPassword(encryptedPassword, password) { + t.Errorf("VerifyPassword failed, expected true, got false") + } + + wrongPassword := "wrongpassword" + if VerifyPassword(encryptedPassword, wrongPassword) { + t.Errorf("VerifyPassword failed, expected false, got true") + } +} + +func TestGenerateRandomString(t *testing.T) { + length := 30 + randomString := GenerateRandomString(length) + if len(randomString) != length { + t.Errorf("GenerateRandomString failed, expected length %d, got %d", length, len(randomString)) + } +} diff --git a/member-portal-backend/public/view/authorize.html b/member-portal-backend/public/view/authorize.html index f0355cc..3b8f21f 100644 --- a/member-portal-backend/public/view/authorize.html +++ b/member-portal-backend/public/view/authorize.html @@ -57,13 +57,13 @@

- ユーザーネーム + ユーザーID
diff --git a/member-portal-backend/router/oauth2/authorize_endpoint.go b/member-portal-backend/router/oauth2/authorize_endpoint.go index 45f6bd6..99af11f 100644 --- a/member-portal-backend/router/oauth2/authorize_endpoint.go +++ b/member-portal-backend/router/oauth2/authorize_endpoint.go @@ -2,9 +2,13 @@ package oauth2 import ( "encoding/json" + "github.com/kstm-su/Member-Portal/backend/crypto" + "github.com/kstm-su/Member-Portal/backend/database" + "github.com/kstm-su/Member-Portal/backend/models" "net/http" "os" "strings" + "time" "github.com/kstm-su/Member-Portal/backend/config" "github.com/labstack/echo/v4" @@ -17,7 +21,7 @@ type ClientData struct { ApplicationName string `json:"applicationName"` } -type AuthorizeRequest struct { +type AuthorizeGetRequest struct { ResponseType string `query:"response_type"` ClientId string `query:"client_id"` RedirectUri string `query:"redirect_uri"` @@ -28,8 +32,31 @@ type AuthorizeRequest struct { Nonce string `query:"nonce"` } +type AuthorizePostRequest struct { + ResponseType string `form:"response_type"` //code + UserId string `form:"userid"` //ユーザーID + Password string `form:"password"` //パスワード + ClientId string `form:"client_id"` //クライアントID + RedirectUri string `form:"redirect_uri"` //リダイレクトURI + Scope string `form:"scope"` //スコープ + State string `form:"state"` //CSRF対策 + CodeChallenge string `form:"code_challenge"` //PKCE + Nonce string `form:"nonce"` //Nonce +} + +type Record struct { + Code string //認可コード + ClientId string //public clientのチェックのため + RedirectUri string //public clientのチェックのため + Scope string //scopeのチェックのため(tokenに入れる) + Challenge string //pkceのチェックのため(hash化されたもの あとで元の値と比較) + Nonce string //nonceのチェックのため(tokenに入れる) RFC 7636 + User string //ユーザーID + NotAfter time.Time //有効期限 +} + func AuthorizationGetEndpointHandler(c echo.Context) error { - var r AuthorizeRequest + var r AuthorizeGetRequest //構造体にリクエストの値をバインド if err := c.Bind(&r); err != nil { return err @@ -96,7 +123,87 @@ func AuthorizationGetEndpointHandler(c echo.Context) error { } func AuthorizationPostEndpointHandler(c echo.Context) error { + var r AuthorizePostRequest + //構造体にリクエストの値をバインド + if err := c.Bind(&r); err != nil { + return err + } + + //クエリパラメータの値をチェック + if r.UserId == "" || r.Password == "" || r.ResponseType != "code" || r.ClientId == "" || r.RedirectUri == "" || r.Scope == "" || r.State == "" || r.Nonce == "" || r.CodeChallenge == "" { + return redirectWithError(c, r.RedirectUri, "invalid_request", "It does not have the required parameters", r.State) + } + + userId := r.UserId + if !isUserRegistered(userId) { + return redirectWithError(c, r.RedirectUri, "access_denied", "This player is not registered", r.State) + } + + hashedPassword := getUserHashedPassword(userId) + if !crypto.VerifyPassword(hashedPassword, r.Password) { + return redirectWithError(c, r.RedirectUri, "access_denied", "Password is incorrect", r.State) + } + + code := crypto.GenerateRandomString(10) + storeAuthorizedData(code, r.ClientId, r.RedirectUri, r.Scope, r.CodeChallenge, r.Nonce, userId) + + return redirectWithCode(c, r.RedirectUri, code, r.State) +} + +func storeAuthorizedData(code string, ClientId string, uri string, scope string, challenge string, nonce string, user string) { + current := time.Now() + record := Record{ + Code: code, + ClientId: ClientId, + RedirectUri: uri, + Scope: scope, + Challenge: challenge, + Nonce: nonce, + User: user, + NotAfter: current.Add(time.Minute * 10), + } + + AuthorizedData[code] = record + + //認可コードの有効期限が切れた場合削除 + go func() { + time.Sleep(time.Minute * 10) + delete(AuthorizedData, code) + }() +} + +var AuthorizedData = make(map[string]Record) + +func getUserHashedPassword(id string) string { + var auth models.Auth + database.DB.Select(models.Auth{}).Where("id = ?", id).First(&auth) + return auth.HashedPassword +} + +func isUserRegistered(id string) bool { + var user models.User + result := database.DB.Where("id = ?", id).First(&user) + return result.RowsAffected > 0 +} + +func redirectWithError(c echo.Context, redirectUri, error, errorDescription, state string) error { + uri := strings.Builder{} + uri.WriteString(redirectUri) + uri.WriteString("?error=") + uri.WriteString(error) + uri.WriteString("&error_description=") + uri.WriteString(errorDescription) + uri.WriteString("&state=") + uri.WriteString(state) + return c.Redirect(http.StatusFound, uri.String()) +} - // TODO implement this function - return nil +func redirectWithCode(c echo.Context, redirectUri, code, state string) error { + uri := strings.Builder{} + uri.WriteString(redirectUri) + uri.WriteString("?code=") + uri.WriteString(code) + uri.WriteString("&state=") + uri.WriteString(state) + return c.Redirect(http.StatusFound, uri.String()) } From e6e43a22b71587de9aca886a12988489aa24afbd Mon Sep 17 00:00:00 2001 From: Nlkomaru Date: Thu, 29 Aug 2024 14:54:50 +0900 Subject: [PATCH 10/13] =?UTF-8?q?=F0=9F=91=8DAdd=20token=20endpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- member-portal-backend/cmd/root.go | 7 + member-portal-backend/config/config.go | 8 + member-portal-backend/crypto/crypto.go | 43 +++- member-portal-backend/crypto/crypto_test.go | 7 +- member-portal-backend/crypto/key.go | 131 ++++++++++++ member-portal-backend/crypto/key_test.go | 27 +++ member-portal-backend/go.mod | 5 +- member-portal-backend/go.sum | 2 + member-portal-backend/models/user.go | 34 +-- .../router/oauth2/authorize_endpoint.go | 56 +++-- .../router/oauth2/token_endpoint.go | 196 +++++++++++++++++- 11 files changed, 459 insertions(+), 57 deletions(-) create mode 100644 member-portal-backend/crypto/key.go create mode 100644 member-portal-backend/crypto/key_test.go diff --git a/member-portal-backend/cmd/root.go b/member-portal-backend/cmd/root.go index a838d2a..e473905 100644 --- a/member-portal-backend/cmd/root.go +++ b/member-portal-backend/cmd/root.go @@ -2,6 +2,7 @@ package cmd import ( "github.com/kstm-su/Member-Portal/backend/config" + "github.com/kstm-su/Member-Portal/backend/crypto" "github.com/kstm-su/Member-Portal/backend/database" "github.com/kstm-su/Member-Portal/backend/router" "github.com/spf13/cobra" @@ -34,6 +35,12 @@ func Execute() error { print(err.Error()) return err } + //キーペアの生成 + err = crypto.GenKey(2048, *c) + if err != nil { + print(err.Error()) + return err + } // データベースの初期化 database.InitDatabase(c) // ルーターの実行 diff --git a/member-portal-backend/config/config.go b/member-portal-backend/config/config.go index 4ac4f9e..194429b 100644 --- a/member-portal-backend/config/config.go +++ b/member-portal-backend/config/config.go @@ -66,6 +66,8 @@ func init() { viper.SetDefault("jwt.realm", "localhost") viper.SetDefault("jwt.key_id", "key") + //pepperについては一時的にconfigに保存するが、実環境ではHashiCorp Vaultなどのシークレット管理ツールを使用することを検討 + //Databaseに保存はしない viper.SetDefault("password.pepper", generateRandomString(30)) viper.SetDefault("password.algorithm", "argon2") } @@ -100,6 +102,12 @@ func Load(configFile string) (*Config, error) { } slog.Info("設定ファイルを構造体に変換しました。") + baseDir := Cfg.File.Base + err = os.MkdirAll(baseDir, 0700) + if err != nil { + return nil, fmt.Errorf("failed to create base directory: %s \n", err) + } + return &Cfg, nil } diff --git a/member-portal-backend/crypto/crypto.go b/member-portal-backend/crypto/crypto.go index 2019033..2ecec46 100644 --- a/member-portal-backend/crypto/crypto.go +++ b/member-portal-backend/crypto/crypto.go @@ -12,54 +12,77 @@ import ( "strings" ) -func PasswordEncrypt(password string) string { +func PasswordEncrypt(password string, config *config.Config) string { salt := []byte(GenerateRandomString(30)) - pepper := config.Cfg.Password.Pepper + + pepper := config.Password.Pepper memory := 64 * 1024 iterations := 4 - parallelism := 1 + threads := 1 keyLen := 32 - encoded := passwordEncryptWithParams(password, string(salt), pepper, memory, parallelism, iterations, keyLen) + encoded := passwordEncryptWithParams(password, string(salt), pepper, memory, threads, iterations, keyLen) return encoded } -func passwordEncryptWithParams(password string, salt string, pepper string, memory int, parallelism int, iterations int, keyLen int) string { +func passwordEncryptWithParams(password string, salt string, pepper string, memory int, threads int, iterations int, keyLen int) string { + // パスワードとpepperを連結 withPepper := password + pepper - hash := argon2.IDKey([]byte(withPepper), []byte(salt), uint32(iterations), uint32(memory), uint8(parallelism), uint32(keyLen)) + // ハッシュ値を生成 + hash := argon2.IDKey([]byte(withPepper), []byte(salt), uint32(iterations), uint32(memory), uint8(threads), uint32(keyLen)) + // ハッシュ値とsaltをbase64エンコード b64Salt := base64.RawStdEncoding.EncodeToString([]byte(salt)) b64Hash := base64.RawStdEncoding.EncodeToString(hash) + // データベースに保存するための文字列を生成 encodedHash := fmt.Sprintf("$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s", - argon2.Version, memory, iterations, parallelism, b64Salt, b64Hash) + argon2.Version, memory, iterations, threads, b64Salt, b64Hash) return encodedHash } -func VerifyPassword(hash string, password string) bool { +func VerifyPassword(hash string, password string, config config.Config) bool { + // 例: $argon2id$v=19$m=65536,t=4,p=1$c2FsdC1zYWx0$6JNmlGvpjNKYpQNSJdGNfAJQ7+upIXwebdDMWcJf30g + // このような形式の文字列をパースする parts := strings.Split(hash, "$") if len(parts) != 6 { return false } + // パースした文字列から必要な情報を取り出す + // saltがbase64エンコードされているのでデコードする salt, err := base64.RawStdEncoding.DecodeString(parts[4]) if err != nil { return false } + // ハッシュ値もbase64エンコードされているのでデコードする decodedHash, err := base64.RawStdEncoding.DecodeString(parts[5]) if err != nil { return false } - withPepper := password + config.Cfg.Password.Pepper + // ハッシュ値を生成する際に使用したパラメータを取り出す + // m=65536,t=4,p=1 + var iterator int + var memory uint32 + var threads uint8 + _, err = fmt.Sscanf(parts[3], "m=%d,t=%d,p=%d", &memory, &iterator, &threads) + + // ハッシュ値の長さを取得 + keyLen := len(decodedHash) + + // パスワードとpepperを連結してハッシュ値を生成 + withPepper := password + config.Password.Pepper - genHash := argon2.IDKey([]byte(withPepper), salt, uint32(4), uint32(64*1024), uint8(1), uint32(32)) + // ハッシュ値を生成 + genHash := argon2.IDKey([]byte(withPepper), salt, uint32(iterator), memory, threads, uint32(keyLen)) + // 生成したハッシュ値とデータベースに保存されているハッシュ値を比較 if bytes.Equal(decodedHash, genHash) { return true } diff --git a/member-portal-backend/crypto/crypto_test.go b/member-portal-backend/crypto/crypto_test.go index 1db2b29..a197b8e 100644 --- a/member-portal-backend/crypto/crypto_test.go +++ b/member-portal-backend/crypto/crypto_test.go @@ -1,9 +1,12 @@ package crypto import ( + "github.com/kstm-su/Member-Portal/backend/config" "testing" ) +var cfg = config.Config{} + func TestPasswordEncrypt(t *testing.T) { password := "password" encryptedPassword := passwordEncryptWithParams(password, "salt-salt", "pepper", 64*1024, 1, 4, 32) @@ -15,12 +18,12 @@ func TestPasswordEncrypt(t *testing.T) { func TestVerifyPassword(t *testing.T) { password := "password" encryptedPassword := "$argon2id$v=19$m=65536,t=4,p=1$cmFmamRhc2poZmRzYWpoa2xhc2Rma2po$i39rjDYap4n6eA2XhCusp5wHGPKBBpM0Lg9S82kw0Ec" - if !VerifyPassword(encryptedPassword, password) { + if !VerifyPassword(encryptedPassword, password, cfg) { t.Errorf("VerifyPassword failed, expected true, got false") } wrongPassword := "wrongpassword" - if VerifyPassword(encryptedPassword, wrongPassword) { + if VerifyPassword(encryptedPassword, wrongPassword, cfg) { t.Errorf("VerifyPassword failed, expected false, got true") } } diff --git a/member-portal-backend/crypto/key.go b/member-portal-backend/crypto/key.go new file mode 100644 index 0000000..9a6735b --- /dev/null +++ b/member-portal-backend/crypto/key.go @@ -0,0 +1,131 @@ +package crypto + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "github.com/kstm-su/Member-Portal/backend/config" + "log" + "os" +) + +type Key struct { + PrivateKey *rsa.PrivateKey + PublicKey *rsa.PublicKey +} + +func GenKey(bits int, config config.Config) error { + baseDir := config.File.Base + keyDir := baseDir + "/key" + + // キー保存用のディレクトリを作成 + err := os.MkdirAll(keyDir, 0700) + if err != nil { + return err + } + privateKeyFileName := keyDir + "/private_key.pem" + pubKeyFileName := keyDir + "/public_key.pem" + + // 秘密鍵および公開鍵が既に存在する場合は終了 + if _, err := os.Stat(privateKeyFileName); err == nil { + log.Println("秘密鍵が既に存在します") + return nil + } + + if _, err := os.Stat(pubKeyFileName); err == nil { + return nil + } + + log.Println("秘密鍵が存在しないため新規作成します") + // 秘密鍵を生成 + privateKey, err := rsa.GenerateKey(rand.Reader, bits) + if err != nil { + return err + } + + // 秘密鍵をファイルに保存 + privateKeyFile, err := os.Create(keyDir + "/private_key.pem") + if err != nil { + return err + } + defer func(privateKeyFile *os.File) { + err := privateKeyFile.Close() + if err != nil { + panic(err) + } + }(privateKeyFile) + + privateKeyPEM := &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(privateKey), + } + if err := pem.Encode(privateKeyFile, privateKeyPEM); err != nil { + return err + } + + // 公開鍵をファイルを取得 + publicKey := &privateKey.PublicKey + + // 公開鍵をファイルに保存 + publicKeyFile, err := os.Create(keyDir + "/public_key.pem") + if err != nil { + return err + } + defer func(publicKeyFile *os.File) { + err := publicKeyFile.Close() + if err != nil { + panic(err) + } + }(publicKeyFile) + + publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey) + if err != nil { + return err + } + + publicKeyPEM := &pem.Block{ + Type: "RSA PUBLIC KEY", + Bytes: publicKeyBytes, + } + if err := pem.Encode(publicKeyFile, publicKeyPEM); err != nil { + return err + } + + return nil +} + +func GetKeys(config config.Config) Key { + var keys Key + // 秘密鍵を読み込む + privateKeyFile, err := os.ReadFile(config.File.Base + "/key/private_key.pem") + if err != nil { + panic(err) + } + block, _ := pem.Decode(privateKeyFile) + if block == nil { + panic("failed to parse PEM block containing the private key") + } + privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + panic(err) + } + keys.PrivateKey = privateKey + + // 公開鍵を読み込む + publicKeyFile, err := os.ReadFile(config.File.Base + "/key/public_key.pem") + if err != nil { + panic(err) + } + block, _ = pem.Decode(publicKeyFile) + if block == nil { + panic("failed to parse PEM block containing the public key") + } + publicKey, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + panic(err) + } + keys.PublicKey = publicKey.(*rsa.PublicKey) + + return keys +} diff --git a/member-portal-backend/crypto/key_test.go b/member-portal-backend/crypto/key_test.go new file mode 100644 index 0000000..6c6c3a2 --- /dev/null +++ b/member-portal-backend/crypto/key_test.go @@ -0,0 +1,27 @@ +package crypto + +import ( + "github.com/kstm-su/Member-Portal/backend/config" + "os" + "testing" +) + +func TestGenKey(t *testing.T) { + var cfg = config.Config{} + cfg.File.Base = "/tmp" + err := GenKey(2048, cfg) + if err != nil { + t.Errorf("GenKey failed, expected nil, got %v", err) + } + + // 生成されたキーが存在するか確認 + _, err = os.Stat("/tmp/key/private_key.pem") + if err != nil { + t.Errorf("GenKey failed, private_key.pem not found") + } + + _, err = os.Stat("/tmp/key/public_key.pem") + if err != nil { + t.Errorf("GenKey failed, public_key.pem not found") + } +} diff --git a/member-portal-backend/go.mod b/member-portal-backend/go.mod index f6babe2..0374590 100644 --- a/member-portal-backend/go.mod +++ b/member-portal-backend/go.mod @@ -3,9 +3,12 @@ module github.com/kstm-su/Member-Portal/backend go 1.22.3 require ( + github.com/golang-jwt/jwt v3.2.2+incompatible + github.com/google/uuid v1.4.0 github.com/labstack/echo/v4 v4.12.0 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 + golang.org/x/crypto v0.26.0 gorm.io/driver/postgres v1.5.9 gorm.io/driver/sqlite v1.5.6 gorm.io/gorm v1.25.11 @@ -13,7 +16,6 @@ require ( require ( github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect @@ -40,7 +42,6 @@ require ( github.com/valyala/fasttemplate v1.2.2 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.26.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/net v0.24.0 // indirect golang.org/x/sync v0.8.0 // indirect diff --git a/member-portal-backend/go.sum b/member-portal-backend/go.sum index b7aed26..30859e1 100644 --- a/member-portal-backend/go.sum +++ b/member-portal-backend/go.sum @@ -11,6 +11,8 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= diff --git a/member-portal-backend/models/user.go b/member-portal-backend/models/user.go index 40b9920..18544e3 100644 --- a/member-portal-backend/models/user.go +++ b/member-portal-backend/models/user.go @@ -1,8 +1,8 @@ package models type User struct { - UserID string `gorm:"primaryKey;column:user_id"` - Nickname string `gorm:"column:nickname"` + UserID string `gorm:"primaryKey;column:user_id;type:varchar(20)"` + Nickname string `gorm:"column:nickname;type:varchar(20)"` Auth Auth Affiliation Affiliation Contact Contact @@ -12,19 +12,19 @@ type User struct { } type Auth struct { - UserID string `gorm:"primaryKey;column:user_id"` - HashedPassword string `gorm:"column:hashed_password"` + UserID string `gorm:"primaryKey;column:user_id;type:varchar(20)"` + HashedPassword string `gorm:"column:hashed_password;type:varchar(100)"` RoleID int `gorm:"column:role_id"` Role Role `gorm:"foreignKey:RoleID"` } type Role struct { RoleID int `gorm:"primaryKey;column:role_id"` - RoleName string `gorm:"column:role_name"` + RoleName string `gorm:"column:role_name;type:varchar(20)"` } type Affiliation struct { - UserID string `gorm:"primaryKey;column:user_id"` + UserID string `gorm:"primaryKey;column:user_id;type:varchar(20)"` FacultyID int `gorm:"column:faculty_id"` Grade int `gorm:"column:grade"` Faculty Faculty `gorm:"foreignKey:FacultyID"` @@ -32,35 +32,35 @@ type Affiliation struct { type Faculty struct { FacultyID int `gorm:"primaryKey;column:faculty_id"` - FacultyName string `gorm:"column:faculty_name"` - DepartmentName string `gorm:"column:department_name"` + FacultyName string `gorm:"column:faculty_name;type:varchar(20)"` + DepartmentName string `gorm:"column:department_name;type:varchar(20)"` } type Contact struct { - UserID string `gorm:"primaryKey;column:user_id"` + UserID string `gorm:"primaryKey;column:user_id;type:varchar(20)"` SchoolEmail string `gorm:"column:school_email"` SubEmail string `gorm:"column:sub_email"` DiscordID string `gorm:"column:discord_id"` GithubID string `gorm:"column:github_id"` - PhoneNumber string `gorm:"column:phone_number"` + PhoneNumber string `gorm:"column:phone_number;type:varchar(20)"` } type Name struct { - UserID string `gorm:"primaryKey;column:user_id"` - FirstName string `gorm:"column:first_name"` - LastName string `gorm:"column:last_name"` - MiddleName string `gorm:"column:middle_name"` + UserID string `gorm:"primaryKey;column:user_id;type:varchar(20)"` + FirstName string `gorm:"column:first_name;type:varchar(20)"` + LastName string `gorm:"column:last_name;type:varchar(20)"` + MiddleName string `gorm:"column:middle_name;type:varchar(40)"` } type Profile struct { - UserID string `gorm:"primaryKey;column:user_id"` + UserID string `gorm:"primaryKey;column:user_id;type:varchar(20)"` ProfileImage string `gorm:"column:profile_image"` - Bio string `gorm:"column:bio"` + Bio string `gorm:"column:bio;size:200"` } type ActivityLog struct { ActivityID int `gorm:"primaryKey;column:activity_id"` - UserID string `gorm:"column:user_id"` + UserID string `gorm:"column:user_id;type:varchar(20)"` ActivityDate string `gorm:"column:activity_date"` ActivityDescription string `gorm:"column:activity_description"` } diff --git a/member-portal-backend/router/oauth2/authorize_endpoint.go b/member-portal-backend/router/oauth2/authorize_endpoint.go index 99af11f..7d3d01f 100644 --- a/member-portal-backend/router/oauth2/authorize_endpoint.go +++ b/member-portal-backend/router/oauth2/authorize_endpoint.go @@ -6,6 +6,7 @@ import ( "github.com/kstm-su/Member-Portal/backend/database" "github.com/kstm-su/Member-Portal/backend/models" "net/http" + "net/url" "os" "strings" "time" @@ -44,7 +45,7 @@ type AuthorizePostRequest struct { Nonce string `form:"nonce"` //Nonce } -type Record struct { +type RecordAuthData struct { Code string //認可コード ClientId string //public clientのチェックのため RedirectUri string //public clientのチェックのため @@ -140,7 +141,7 @@ func AuthorizationPostEndpointHandler(c echo.Context) error { } hashedPassword := getUserHashedPassword(userId) - if !crypto.VerifyPassword(hashedPassword, r.Password) { + if !crypto.VerifyPassword(hashedPassword, r.Password, config.Cfg) { return redirectWithError(c, r.RedirectUri, "access_denied", "Password is incorrect", r.State) } @@ -152,7 +153,7 @@ func AuthorizationPostEndpointHandler(c echo.Context) error { func storeAuthorizedData(code string, ClientId string, uri string, scope string, challenge string, nonce string, user string) { current := time.Now() - record := Record{ + record := RecordAuthData{ Code: code, ClientId: ClientId, RedirectUri: uri, @@ -168,42 +169,51 @@ func storeAuthorizedData(code string, ClientId string, uri string, scope string, //認可コードの有効期限が切れた場合削除 go func() { time.Sleep(time.Minute * 10) + println("delete authorize code " + code) delete(AuthorizedData, code) }() } -var AuthorizedData = make(map[string]Record) +var AuthorizedData = make(map[string]RecordAuthData) func getUserHashedPassword(id string) string { var auth models.Auth - database.DB.Select(models.Auth{}).Where("id = ?", id).First(&auth) + database.DB.Where("user_id = ?", id).First(&auth) + println(auth.HashedPassword) return auth.HashedPassword } func isUserRegistered(id string) bool { - var user models.User - result := database.DB.Where("id = ?", id).First(&user) + var auth models.Auth + result := database.DB.Where("user_id = ?", id).First(&auth) return result.RowsAffected > 0 } func redirectWithError(c echo.Context, redirectUri, error, errorDescription, state string) error { - uri := strings.Builder{} - uri.WriteString(redirectUri) - uri.WriteString("?error=") - uri.WriteString(error) - uri.WriteString("&error_description=") - uri.WriteString(errorDescription) - uri.WriteString("&state=") - uri.WriteString(state) - return c.Redirect(http.StatusFound, uri.String()) + parsedURL, err := url.Parse(redirectUri) + if err != nil { + return err + } + + params := url.Values{} + params.Add("error", error) + params.Add("error_description", errorDescription) + params.Add("state", state) + + parsedURL.RawQuery = params.Encode() + return c.Redirect(http.StatusFound, parsedURL.String()) } func redirectWithCode(c echo.Context, redirectUri, code, state string) error { - uri := strings.Builder{} - uri.WriteString(redirectUri) - uri.WriteString("?code=") - uri.WriteString(code) - uri.WriteString("&state=") - uri.WriteString(state) - return c.Redirect(http.StatusFound, uri.String()) + parsedURL, err := url.Parse(redirectUri) + if err != nil { + return err + } + + params := url.Values{} + params.Add("code", code) + params.Add("state", state) + + parsedURL.RawQuery = params.Encode() + return c.Redirect(http.StatusFound, parsedURL.String()) } diff --git a/member-portal-backend/router/oauth2/token_endpoint.go b/member-portal-backend/router/oauth2/token_endpoint.go index 3ffbd94..2da97b0 100644 --- a/member-portal-backend/router/oauth2/token_endpoint.go +++ b/member-portal-backend/router/oauth2/token_endpoint.go @@ -1,10 +1,200 @@ package oauth2 -import "github.com/labstack/echo/v4" +import ( + "crypto/sha256" + "encoding/base64" + "encoding/json" + "github.com/golang-jwt/jwt" + "github.com/google/uuid" + "github.com/kstm-su/Member-Portal/backend/config" + "github.com/kstm-su/Member-Portal/backend/crypto" + "github.com/labstack/echo/v4" + "net/http" + "os" + "strings" + "time" +) + +const EXPIRES_IN = 300 + +type TokenData struct { + Token string `json:"access_token"` + TokenType string `json:"token_type"` + ExpiresIn int `json:"expires_in"` + Nonce string `json:"nonce"` + RefreshToken string `json:"refresh_token"` +} + +type RefreshTokenRequest struct { + GrantType string `form:"grant_type"` + ClientId string `form:"client_id"` + ClientSecret string `form:"client_secret"` + RefreshToken string `form:"refresh_token"` + RedirectUri string `form:"redirect_uri"` +} + +type AuthorizeCodeRequest struct { + Code string `form:"code"` + ClientId string `form:"client_id"` + ClientSecret string `form:"client_secret"` + RedirectUri string `form:"redirect_uri"` + CodeVerifier string `form:"code_verifier"` +} + +type TokenClaims struct { + ClientId string + RedirectUri string + Nonce string + Scope string +} func TokenEndpointHandler(c echo.Context) error { + formParameters, _ := c.FormParams() + grantType := formParameters.Get("grant_type") + + if grantType == "refresh_token" { + var r RefreshTokenRequest + if err := c.Bind(&r); err != nil { + return c.JSON(http.StatusBadRequest, "Invalid request") + } + if r.RefreshToken == "" { + return c.JSON(http.StatusBadRequest, "Invalid request") + } + data, err := getClientData(r.ClientId, c) + if err != nil { + return err + } + + if r.ClientSecret != "" { + + //if data.ClientSecret != clientSecret { + // return c.JSON(http.StatusBadRequest, "Invalid client_secret") + //} + + token, state := issueTokenWithRefreshToken(r.RefreshToken, config.Cfg) + return c.JSON(http.StatusOK, TokenData{token, "Bearer", EXPIRES_IN, state, r.RefreshToken}) + } else { + redirectUri := r.RedirectUri + if redirectUri == "" { + return c.JSON(http.StatusBadRequest, "Invalid request") + } + + if data.RedirectUri != redirectUri { + return c.JSON(http.StatusBadRequest, "Invalid redirect_uri") + } + + token, state := issueTokenWithRefreshToken(r.RefreshToken, config.Cfg) + return c.JSON(http.StatusOK, TokenData{token, "Bearer", EXPIRES_IN, state, r.RefreshToken}) + } + } else if grantType == "authorization_code" { + var r AuthorizeCodeRequest + if err := c.Bind(&r); err != nil { + return c.JSON(http.StatusBadRequest, "Invalid request") + } - // TODO implement this function - return nil + data, exists := AuthorizedData[r.Code] + if !exists { + delete(AuthorizedData, r.Code) + return c.JSON(http.StatusBadRequest, "Invalid code") + } + delete(AuthorizedData, r.Code) + + if r.Code == "" || r.RedirectUri == "" || r.ClientId == "" || r.CodeVerifier == "" { + return c.JSON(http.StatusBadRequest, "Invalid request") + } + + //if clientSecret != "" { + // clientData := getClientData(clientId).(ConfidentialClientData) + // if clientData.ClientSecret != clientSecret { + // return c.JSON(http.StatusBadRequest, "Invalid client_secret") + // } + //} + + if data.ClientId != r.ClientId || !strings.HasPrefix(r.RedirectUri, data.RedirectUri) { + return c.JSON(http.StatusBadRequest, "Invalid client_id or redirect_uri") + } + + hash := sha256.Sum256([]byte(r.CodeVerifier)) + base64Hash := strings.ReplaceAll(base64.URLEncoding.EncodeToString(hash[:]), "=", "") + if data.Challenge != base64Hash { + return c.JSON(http.StatusBadRequest, "Invalid code_verifier") + } + + claims := TokenClaims{r.ClientId, r.RedirectUri, data.Nonce, data.Scope} + token := issueToken(claims, config.Cfg) + refreshToken := issueRefreshToken(claims, config.Cfg) + return c.JSON(http.StatusOK, TokenData{token, "Bearer", EXPIRES_IN, data.Nonce, refreshToken}) + } else { + return c.JSON(http.StatusBadRequest, "Invalid grant_type") + } +} + +func getClientData(id string, c echo.Context) (ClientData, error) { + clientDataFile := config.Cfg.File.Base + "/clients/" + id + ".json" + //クライアントIDのファイルが存在しない場合 + if _, err := os.Stat(clientDataFile); os.IsNotExist(err) { + return ClientData{}, c.String(http.StatusBadRequest, "Invalid client") + } + + //クライアントIDのファイルを読み込む + fileContent, err := os.ReadFile(clientDataFile) + if err != nil { + return ClientData{}, c.String(http.StatusInternalServerError, "Internal server error") + } + + //クライアントIDのファイルを構造体にバインド + var clientData ClientData + if err := json.Unmarshal(fileContent, &clientData); err != nil { + return ClientData{}, err // Handle error appropriately + } + + return clientData, nil +} + +func issueToken(data TokenClaims, config config.Config) string { + token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{ + "iss": config.JWT.Issuer, + "aud": data.ClientId, + "nbf": time.Now().Unix(), + "exp": time.Now().Add(time.Second * EXPIRES_IN).Unix(), + "iat": time.Now().Unix(), + "jti": uuid.New().String(), + "client_id": data.ClientId, + "scope": data.Scope, + "nonce": data.Nonce, + "token_type": "token", + }) + tokenString, _ := token.SignedString(crypto.GetKeys(config).PrivateKey) + return tokenString +} + +func issueRefreshToken(data TokenClaims, config config.Config) string { + token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{ + "iss": config.JWT.Issuer, + "aud": data.ClientId, + "nbf": time.Now().Unix(), + "exp": time.Now().Add(time.Hour * 24 * 30).Unix(), + "iat": time.Now().Unix(), + "jti": uuid.New().String(), + "client_id": data.ClientId, + "scope": data.Scope, + "nonce": data.Nonce, + "token_type": "refresh_token", + }) + tokenString, _ := token.SignedString(crypto.GetKeys(config).PrivateKey) + return tokenString +} +func issueTokenWithRefreshToken(refreshToken string, config config.Config) (string, string) { + token, _ := jwt.Parse(refreshToken, func(token *jwt.Token) (interface{}, error) { + return crypto.GetKeys(config), nil + }) + claims := token.Claims.(jwt.MapClaims) + clientId := claims["client_id"].(string) + redirectUri := claims["redirect_uri"].(string) + scope := claims["scope"].(string) + nonce := claims["nonce"].(string) + data := TokenClaims{clientId, redirectUri, nonce, scope} + newToken := issueToken(data, config) + return newToken, nonce } From 5d860172ff134744a33f44dba6549ccf1dea5bc7 Mon Sep 17 00:00:00 2001 From: Nlkomaru Date: Sat, 31 Aug 2024 18:49:47 +0900 Subject: [PATCH 11/13] =?UTF-8?q?=F0=9F=91=8D=20Add=20jwks=20endpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- member-portal-backend/cmd/root.go | 6 +- member-portal-backend/crypto/key.go | 140 ++++++++++++++++-- member-portal-backend/go.mod | 9 ++ member-portal-backend/go.sum | 19 +++ member-portal-backend/router/jwks_endpoint.go | 12 ++ member-portal-backend/router/oauth2/oauth.go | 7 +- member-portal-backend/router/root.go | 3 + 7 files changed, 174 insertions(+), 22 deletions(-) create mode 100644 member-portal-backend/router/jwks_endpoint.go diff --git a/member-portal-backend/cmd/root.go b/member-portal-backend/cmd/root.go index e473905..3ab3f94 100644 --- a/member-portal-backend/cmd/root.go +++ b/member-portal-backend/cmd/root.go @@ -36,11 +36,7 @@ func Execute() error { return err } //キーペアの生成 - err = crypto.GenKey(2048, *c) - if err != nil { - print(err.Error()) - return err - } + crypto.Init(*c) // データベースの初期化 database.InitDatabase(c) // ルーターの実行 diff --git a/member-portal-backend/crypto/key.go b/member-portal-backend/crypto/key.go index 9a6735b..c500d1c 100644 --- a/member-portal-backend/crypto/key.go +++ b/member-portal-backend/crypto/key.go @@ -4,10 +4,17 @@ import ( "crypto/rand" "crypto/rsa" "crypto/x509" + "crypto/x509/pkix" + "encoding/json" "encoding/pem" + "github.com/google/uuid" "github.com/kstm-su/Member-Portal/backend/config" - "log" + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwk" + "log/slog" + "math/big" "os" + "time" ) type Key struct { @@ -15,7 +22,25 @@ type Key struct { PublicKey *rsa.PublicKey } -func GenKey(bits int, config config.Config) error { +func Init(config config.Config) { + slog.Info("キーペアの生成を開始します") + err := genKey(config) + if err != nil { + return + } + slog.Info("キーペアの生成が完了しました") + + slog.Info("キーペアより証明書を取得します") + keys := GetKeys(config) + err = generateCertificate(keys.PrivateKey, keys.PublicKey) + slog.Info("証明書の取得が完了しました") + + slog.Info("jwks.jsonを生成します") + generateJWKs(config) + slog.Info("jwks.jsonの生成が完了しました") +} + +func genKey(config config.Config) error { baseDir := config.File.Base keyDir := baseDir + "/key" @@ -24,22 +49,20 @@ func GenKey(bits int, config config.Config) error { if err != nil { return err } - privateKeyFileName := keyDir + "/private_key.pem" + privKeyFileName := keyDir + "/private_key.pem" pubKeyFileName := keyDir + "/public_key.pem" // 秘密鍵および公開鍵が既に存在する場合は終了 - if _, err := os.Stat(privateKeyFileName); err == nil { - log.Println("秘密鍵が既に存在します") - return nil - } - - if _, err := os.Stat(pubKeyFileName); err == nil { - return nil + if _, err := os.Stat(privKeyFileName); err == nil { + if _, err := os.Stat(pubKeyFileName); err == nil { + slog.Info("キーペアが既に存在します") + return nil + } } - log.Println("秘密鍵が存在しないため新規作成します") + slog.Info("キーペアが存在しないため新規作成します") // 秘密鍵を生成 - privateKey, err := rsa.GenerateKey(rand.Reader, bits) + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return err } @@ -95,6 +118,95 @@ func GenKey(bits int, config config.Config) error { return nil } +func generateCertificate(privateKey *rsa.PrivateKey, publicKey *rsa.PublicKey) error { + startDate := time.Now() + endDate := startDate.AddDate(1, 0, 0) // 1 year validity + serialNumber := big.NewInt(time.Now().Unix()) + subject := pkix.Name{ + CommonName: "Test Certificate", + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: subject, + Issuer: subject, // self-signed + NotBefore: startDate, + NotAfter: endDate, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + } + + certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey, privateKey) + if err != nil { + return err + } + + certFile, err := os.Create("certificate.pem") + if err != nil { + return err + } + defer func(certFile *os.File) { + err := certFile.Close() + if err != nil { + + } + }(certFile) + + err = pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes}) + if err != nil { + return err + } + + return nil +} + +func generateJWKs(config config.Config) { + jwksFile := config.File.Base + "/key/jwks.json" + + //既にファイルが存在している場合 + if _, err := os.Stat(jwksFile); err == nil { + slog.Info("jwks.jsonが既に存在します") + return + } + + //そうでない場合 + file, err := os.Create(jwksFile) + if err != nil { + slog.Warn("jwks.jsonの生成に失敗しました: %v", err) + } + defer func(file *os.File) { + err := file.Close() + if err != nil { + + } + }(file) + + privateKey := GetKeys(config).PrivateKey + + key, err := jwk.PublicKeyOf(privateKey) + if err != nil { + slog.Warn("jwksのpublickey生成に失敗しました: %v", err) + } + + uniqueId := uuid.New() + + _ = key.Set(jwk.KeyIDKey, uniqueId) + _ = key.Set(jwk.KeyUsageKey, jwk.ForSignature) + _ = key.Set(jwk.KeyTypeKey, jwa.RS256) + + // Create a JWK set and add the JWK to the set + jwkSet := jwk.NewSet() + err = jwkSet.AddKey(key) + if err != nil { + return + } + + encoded, _ := json.Marshal(jwkSet) + + // Save the JWK set to a file + _, err = file.Write(encoded) +} + func GetKeys(config config.Config) Key { var keys Key // 秘密鍵を読み込む @@ -104,7 +216,7 @@ func GetKeys(config config.Config) Key { } block, _ := pem.Decode(privateKeyFile) if block == nil { - panic("failed to parse PEM block containing the private key") + panic("秘密鍵の読み込みに失敗しました") } privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { @@ -119,7 +231,7 @@ func GetKeys(config config.Config) Key { } block, _ = pem.Decode(publicKeyFile) if block == nil { - panic("failed to parse PEM block containing the public key") + panic("公開鍵の読み取りに失敗しました") } publicKey, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { diff --git a/member-portal-backend/go.mod b/member-portal-backend/go.mod index 0374590..144b96e 100644 --- a/member-portal-backend/go.mod +++ b/member-portal-backend/go.mod @@ -6,6 +6,7 @@ require ( github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/uuid v1.4.0 github.com/labstack/echo/v4 v4.12.0 + github.com/lestrrat-go/jwx/v2 v2.1.1 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 golang.org/x/crypto v0.26.0 @@ -15,7 +16,9 @@ require ( ) require ( + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/goccy/go-json v0.10.3 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect @@ -25,6 +28,11 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/labstack/gommon v0.4.2 // indirect + github.com/lestrrat-go/blackmagic v1.0.2 // indirect + github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/httprc v1.0.6 // indirect + github.com/lestrrat-go/iter v1.0.2 // indirect + github.com/lestrrat-go/option v1.0.1 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -33,6 +41,7 @@ require ( github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/segmentio/asm v1.2.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect diff --git a/member-portal-backend/go.sum b/member-portal-backend/go.sum index 30859e1..6691908 100644 --- a/member-portal-backend/go.sum +++ b/member-portal-backend/go.sum @@ -3,10 +3,14 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= @@ -37,6 +41,18 @@ github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+k github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= +github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= +github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= +github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k= +github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= +github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= +github.com/lestrrat-go/jwx/v2 v2.1.1 h1:Y2ltVl8J6izLYFs54BVcpXLv5msSW4o8eXwnzZLI32E= +github.com/lestrrat-go/jwx/v2 v2.1.1/go.mod h1:4LvZg7oxu6Q5VJwn7Mk/UwooNRnTHUpXBj2C4j3HNx0= +github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= +github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -60,6 +76,8 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= @@ -77,6 +95,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= diff --git a/member-portal-backend/router/jwks_endpoint.go b/member-portal-backend/router/jwks_endpoint.go new file mode 100644 index 0000000..ea79ef7 --- /dev/null +++ b/member-portal-backend/router/jwks_endpoint.go @@ -0,0 +1,12 @@ +package router + +import ( + "github.com/kstm-su/Member-Portal/backend/config" + "github.com/labstack/echo/v4" +) + +func JWKsHandler(c echo.Context) error { + println("access ") + file := config.Cfg.File.Base + "/key/jwks.json" + return c.File(file) +} diff --git a/member-portal-backend/router/oauth2/oauth.go b/member-portal-backend/router/oauth2/oauth.go index 68b56e0..5628d0f 100644 --- a/member-portal-backend/router/oauth2/oauth.go +++ b/member-portal-backend/router/oauth2/oauth.go @@ -1,6 +1,8 @@ package oauth2 -import "github.com/labstack/echo/v4" +import ( + "github.com/labstack/echo/v4" +) func Setup(e *echo.Group) { e.GET("/authorize", AuthorizationGetEndpointHandler) @@ -9,6 +11,5 @@ func Setup(e *echo.Group) { e.POST("/revoke", RevokeTokenEndpointHandler) e.POST("/introspect", IntrospectTokenEndpointHandler) e.GET("/userinfo", UserInfoEndpointHandler) - //e.GET("/.well-known/openid-configuration", OpenIDConfigurationHandler) - //e.GET("/.well-known/jwks.json", JwksHandler) + } diff --git a/member-portal-backend/router/root.go b/member-portal-backend/router/root.go index d579977..d47846f 100644 --- a/member-portal-backend/router/root.go +++ b/member-portal-backend/router/root.go @@ -40,6 +40,9 @@ func Execute(c *config.Config) { oauth2Router := e.Group("/oauth2") oauth2.Setup(oauth2Router) + //e.GET("/.well-known/openid-configuration", OpenIDConfigurationHandler) + e.GET("/.well-known/jwks.json", JWKsHandler) + //サーバーの起動 var port = c.Server.Port e.Logger.Fatal(e.Start(":" + strconv.Itoa(port))) From e55caa3f61833216dfbca3375a7bddf9b3308101 Mon Sep 17 00:00:00 2001 From: Nlkomaru Date: Sun, 1 Sep 2024 00:26:30 +0900 Subject: [PATCH 12/13] =?UTF-8?q?=F0=9F=91=8D=20Add=20userinfo=20endpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- member-portal-backend/config/config.go | 2 +- member-portal-backend/database/database.go | 12 +++--- member-portal-backend/go.mod | 2 + member-portal-backend/go.sum | 4 ++ member-portal-backend/models/user.go | 12 ++---- .../public/view/authorize.html | 2 +- member-portal-backend/router/jwks_endpoint.go | 1 - .../router/oauth2/authorize_endpoint.go | 7 ++-- member-portal-backend/router/oauth2/oauth.go | 42 ++++++++++++++++++- .../router/oauth2/token_endpoint.go | 20 +++++---- .../router/oauth2/user_info_endpoint.go | 34 +++++++++++++-- 11 files changed, 105 insertions(+), 33 deletions(-) diff --git a/member-portal-backend/config/config.go b/member-portal-backend/config/config.go index 194429b..effafa7 100644 --- a/member-portal-backend/config/config.go +++ b/member-portal-backend/config/config.go @@ -105,7 +105,7 @@ func Load(configFile string) (*Config, error) { baseDir := Cfg.File.Base err = os.MkdirAll(baseDir, 0700) if err != nil { - return nil, fmt.Errorf("failed to create base directory: %s \n", err) + return nil, fmt.Errorf("baseディレクトリを作成するのに失敗しました: %s \n", err) } return &Cfg, nil diff --git a/member-portal-backend/database/database.go b/member-portal-backend/database/database.go index 5d32c77..c4e5cb7 100644 --- a/member-portal-backend/database/database.go +++ b/member-portal-backend/database/database.go @@ -7,14 +7,14 @@ import ( "gorm.io/driver/postgres" "gorm.io/driver/sqlite" "gorm.io/gorm" - "log" + "log/slog" ) var DB *gorm.DB func InitDatabase(c *config.Config) { var err error - log.Println("Connecting to database") + slog.Info("データベースへの接続を開始します") switch c.Database.Type { case "sqlite": DB, err = gorm.Open(sqlite.Open(c.Database.SQLite.Path), &gorm.Config{}) @@ -23,13 +23,13 @@ func InitDatabase(c *config.Config) { DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{}) } if err != nil { - log.Fatal("failed to connect database") + slog.Error("データベースへの接続に失敗しました") } - log.Println("Connected to database") + slog.Info("データベースへの接続が完了しました") // Auto-migrate the models - err = DB.AutoMigrate(&models.User{}, &models.Auth{}, &models.Role{}, &models.Affiliation{}, &models.Faculty{}, &models.Contact{}, &models.Name{}, &models.Profile{}, &models.ActivityLog{}) + err = DB.AutoMigrate(&models.Users{}, &models.Auth{}, &models.Role{}, &models.Affiliation{}, &models.Faculty{}, &models.Contact{}, &models.Name{}, &models.Profile{}, &models.ActivityLog{}) if err != nil { - log.Fatal("failed to migrate database") + slog.Error("データベースのマイグレーションに失敗しました") } } diff --git a/member-portal-backend/go.mod b/member-portal-backend/go.mod index 144b96e..17e7138 100644 --- a/member-portal-backend/go.mod +++ b/member-portal-backend/go.mod @@ -5,6 +5,7 @@ go 1.22.3 require ( github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/uuid v1.4.0 + github.com/labstack/echo-jwt/v4 v4.2.0 github.com/labstack/echo/v4 v4.12.0 github.com/lestrrat-go/jwx/v2 v2.1.1 github.com/spf13/cobra v1.8.1 @@ -19,6 +20,7 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/goccy/go-json v0.10.3 // indirect + github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect diff --git a/member-portal-backend/go.sum b/member-portal-backend/go.sum index 6691908..0ea3641 100644 --- a/member-portal-backend/go.sum +++ b/member-portal-backend/go.sum @@ -13,6 +13,8 @@ github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= @@ -37,6 +39,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo-jwt/v4 v4.2.0 h1:odSISV9JgcSCuhgQSV/6Io3i7nUmfM/QkBeR5GVJj5c= +github.com/labstack/echo-jwt/v4 v4.2.0/go.mod h1:MA2RqdXdEn4/uEglx0HcUOgQSyBaTh5JcaHIan3biwU= github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= diff --git a/member-portal-backend/models/user.go b/member-portal-backend/models/user.go index 18544e3..f8a6d6c 100644 --- a/member-portal-backend/models/user.go +++ b/member-portal-backend/models/user.go @@ -1,14 +1,8 @@ package models -type User struct { - UserID string `gorm:"primaryKey;column:user_id;type:varchar(20)"` - Nickname string `gorm:"column:nickname;type:varchar(20)"` - Auth Auth - Affiliation Affiliation - Contact Contact - Name Name - Profile Profile - ActivityLogs []ActivityLog +type Users struct { + UserID string `gorm:"primaryKey;column:user_id;type:varchar(20)"` + Nickname string `gorm:"column:nickname;type:varchar(20)"` } type Auth struct { diff --git a/member-portal-backend/public/view/authorize.html b/member-portal-backend/public/view/authorize.html index 3b8f21f..dbc0cf6 100644 --- a/member-portal-backend/public/view/authorize.html +++ b/member-portal-backend/public/view/authorize.html @@ -63,7 +63,7 @@

name="userid" id="userid" class="bg-gray-50 border text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5" - placeholder="userid" + placeholder="user_id" required="" /> diff --git a/member-portal-backend/router/jwks_endpoint.go b/member-portal-backend/router/jwks_endpoint.go index ea79ef7..6ace8e0 100644 --- a/member-portal-backend/router/jwks_endpoint.go +++ b/member-portal-backend/router/jwks_endpoint.go @@ -6,7 +6,6 @@ import ( ) func JWKsHandler(c echo.Context) error { - println("access ") file := config.Cfg.File.Base + "/key/jwks.json" return c.File(file) } diff --git a/member-portal-backend/router/oauth2/authorize_endpoint.go b/member-portal-backend/router/oauth2/authorize_endpoint.go index 7d3d01f..037a8ae 100644 --- a/member-portal-backend/router/oauth2/authorize_endpoint.go +++ b/member-portal-backend/router/oauth2/authorize_endpoint.go @@ -5,6 +5,7 @@ import ( "github.com/kstm-su/Member-Portal/backend/crypto" "github.com/kstm-su/Member-Portal/backend/database" "github.com/kstm-su/Member-Portal/backend/models" + "github.com/labstack/echo/v4" "net/http" "net/url" "os" @@ -12,7 +13,6 @@ import ( "time" "github.com/kstm-su/Member-Portal/backend/config" - "github.com/labstack/echo/v4" ) type ClientData struct { @@ -101,8 +101,8 @@ func AuthorizationGetEndpointHandler(c echo.Context) error { } //リダイレクトURIが一致しない場合 - if strings.HasPrefix(r.RedirectUri, clientData.RedirectUri) { - return c.String(http.StatusBadRequest, "Invalid request") + if !strings.HasPrefix(r.RedirectUri, clientData.RedirectUri) { + return c.String(http.StatusBadRequest, "Invalid redirect_uri") } //モデルの作成 @@ -179,7 +179,6 @@ var AuthorizedData = make(map[string]RecordAuthData) func getUserHashedPassword(id string) string { var auth models.Auth database.DB.Where("user_id = ?", id).First(&auth) - println(auth.HashedPassword) return auth.HashedPassword } diff --git a/member-portal-backend/router/oauth2/oauth.go b/member-portal-backend/router/oauth2/oauth.go index 5628d0f..44e79c3 100644 --- a/member-portal-backend/router/oauth2/oauth.go +++ b/member-portal-backend/router/oauth2/oauth.go @@ -1,15 +1,55 @@ package oauth2 import ( + "errors" + "github.com/golang-jwt/jwt" + "github.com/kstm-su/Member-Portal/backend/config" + "github.com/kstm-su/Member-Portal/backend/crypto" + echojwt "github.com/labstack/echo-jwt/v4" "github.com/labstack/echo/v4" + "log/slog" + "net/http" ) +var key interface{} + func Setup(e *echo.Group) { e.GET("/authorize", AuthorizationGetEndpointHandler) e.POST("/authorize", AuthorizationPostEndpointHandler) e.POST("/token", TokenEndpointHandler) e.POST("/revoke", RevokeTokenEndpointHandler) e.POST("/introspect", IntrospectTokenEndpointHandler) - e.GET("/userinfo", UserInfoEndpointHandler) + key = crypto.GetKeys(config.Cfg).PublicKey + + e.GET("/userinfo", UserInfoEndpointHandler, oauth2Middleware) +} + +func oauth2Middleware(next echo.HandlerFunc) echo.HandlerFunc { + return echojwt.WithConfig(echojwt.Config{ + SigningKey: key, + SigningMethod: jwt.SigningMethodRS256.Name, + ErrorHandler: func(c echo.Context, err error) error { + var ve *jwt.ValidationError + if errors.As(err, &ve) { + if ve.Errors&jwt.ValidationErrorMalformed != 0 { + slog.Error("token is malformed") + return c.JSON(http.StatusBadRequest, "token is malformed") + } else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 { + slog.Warn("token is expired") + return c.JSON(http.StatusUnauthorized, "token is expired or not valid yet") + } else { + slog.Warn("token is invalid") + return c.JSON(http.StatusBadRequest, "token is invalid") + } + } + println(err.Error()) + return err + }, + ParseTokenFunc: func(c echo.Context, auth string) (interface{}, error) { + return jwt.Parse(auth, func(token *jwt.Token) (interface{}, error) { + return key, nil + }) + }, + })(next) } diff --git a/member-portal-backend/router/oauth2/token_endpoint.go b/member-portal-backend/router/oauth2/token_endpoint.go index 2da97b0..0c9ea26 100644 --- a/member-portal-backend/router/oauth2/token_endpoint.go +++ b/member-portal-backend/router/oauth2/token_endpoint.go @@ -15,7 +15,7 @@ import ( "time" ) -const EXPIRES_IN = 300 +const expiresIn = 300 type TokenData struct { Token string `json:"access_token"` @@ -46,6 +46,7 @@ type TokenClaims struct { RedirectUri string Nonce string Scope string + UserId string } func TokenEndpointHandler(c echo.Context) error { @@ -72,7 +73,7 @@ func TokenEndpointHandler(c echo.Context) error { //} token, state := issueTokenWithRefreshToken(r.RefreshToken, config.Cfg) - return c.JSON(http.StatusOK, TokenData{token, "Bearer", EXPIRES_IN, state, r.RefreshToken}) + return c.JSON(http.StatusOK, TokenData{token, "Bearer", expiresIn, state, r.RefreshToken}) } else { redirectUri := r.RedirectUri if redirectUri == "" { @@ -84,7 +85,7 @@ func TokenEndpointHandler(c echo.Context) error { } token, state := issueTokenWithRefreshToken(r.RefreshToken, config.Cfg) - return c.JSON(http.StatusOK, TokenData{token, "Bearer", EXPIRES_IN, state, r.RefreshToken}) + return c.JSON(http.StatusOK, TokenData{token, "Bearer", expiresIn, state, r.RefreshToken}) } } else if grantType == "authorization_code" { var r AuthorizeCodeRequest @@ -120,10 +121,10 @@ func TokenEndpointHandler(c echo.Context) error { return c.JSON(http.StatusBadRequest, "Invalid code_verifier") } - claims := TokenClaims{r.ClientId, r.RedirectUri, data.Nonce, data.Scope} + claims := TokenClaims{r.ClientId, r.RedirectUri, data.Nonce, data.Scope, data.User} token := issueToken(claims, config.Cfg) refreshToken := issueRefreshToken(claims, config.Cfg) - return c.JSON(http.StatusOK, TokenData{token, "Bearer", EXPIRES_IN, data.Nonce, refreshToken}) + return c.JSON(http.StatusOK, TokenData{token, "Bearer", expiresIn, data.Nonce, refreshToken}) } else { return c.JSON(http.StatusBadRequest, "Invalid grant_type") } @@ -154,15 +155,17 @@ func getClientData(id string, c echo.Context) (ClientData, error) { func issueToken(data TokenClaims, config config.Config) string { token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{ "iss": config.JWT.Issuer, + "sub": data.UserId, "aud": data.ClientId, "nbf": time.Now().Unix(), - "exp": time.Now().Add(time.Second * EXPIRES_IN).Unix(), + "exp": time.Now().Add(time.Second * expiresIn).Unix(), "iat": time.Now().Unix(), "jti": uuid.New().String(), "client_id": data.ClientId, "scope": data.Scope, "nonce": data.Nonce, "token_type": "token", + "user_id": data.UserId, }) tokenString, _ := token.SignedString(crypto.GetKeys(config).PrivateKey) return tokenString @@ -171,6 +174,7 @@ func issueToken(data TokenClaims, config config.Config) string { func issueRefreshToken(data TokenClaims, config config.Config) string { token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{ "iss": config.JWT.Issuer, + "sub": data.UserId, "aud": data.ClientId, "nbf": time.Now().Unix(), "exp": time.Now().Add(time.Hour * 24 * 30).Unix(), @@ -180,6 +184,7 @@ func issueRefreshToken(data TokenClaims, config config.Config) string { "scope": data.Scope, "nonce": data.Nonce, "token_type": "refresh_token", + "user_id": data.UserId, }) tokenString, _ := token.SignedString(crypto.GetKeys(config).PrivateKey) return tokenString @@ -194,7 +199,8 @@ func issueTokenWithRefreshToken(refreshToken string, config config.Config) (stri redirectUri := claims["redirect_uri"].(string) scope := claims["scope"].(string) nonce := claims["nonce"].(string) - data := TokenClaims{clientId, redirectUri, nonce, scope} + userId := claims["user_id"].(string) + data := TokenClaims{clientId, redirectUri, nonce, scope, userId} newToken := issueToken(data, config) return newToken, nonce } diff --git a/member-portal-backend/router/oauth2/user_info_endpoint.go b/member-portal-backend/router/oauth2/user_info_endpoint.go index e682946..4c83e01 100644 --- a/member-portal-backend/router/oauth2/user_info_endpoint.go +++ b/member-portal-backend/router/oauth2/user_info_endpoint.go @@ -1,9 +1,37 @@ package oauth2 -import "github.com/labstack/echo/v4" +import ( + "github.com/golang-jwt/jwt" + "github.com/kstm-su/Member-Portal/backend/database" + "github.com/labstack/echo/v4" +) + +// OIDC Standard Claims https://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html#StandardClaims +type userInfoClaim struct { + Sub string `json:"sub" gorm:"column:user_id"` + Nickname string `json:"nickname" gorm:"column:nickname"` + Picture string `json:"picture" gorm:"column:profile_image"` + Email string `json:"email" gorm:"column:school_email"` +} func UserInfoEndpointHandler(c echo.Context) error { + token, ok := c.Get("user").(*jwt.Token) // by default token is stored under `user` key + if !ok { + return c.String(500, "user not found") + } + + claims := token.Claims.(jwt.MapClaims) // by default claims is of type `jwt.MapClaims` + + userId := claims["user_id"].(string) + + profile := userInfoClaim{} + + database.DB.Table("users"). + Select("users.user_id, users.nickname, profiles.profile_image, contacts.school_email"). + Joins("JOIN profiles ON users.user_id = profiles.user_id"). + Joins("JOIN contacts ON users.user_id = contacts.user_id"). + Where("users.user_id = ?", userId). + Scan(&profile) - // TODO implement this function - return nil + return c.JSON(200, profile) } From fa77f2197be2131a1648114f957d9894c4bbc055 Mon Sep 17 00:00:00 2001 From: Nlkomaru Date: Mon, 2 Sep 2024 21:44:56 +0900 Subject: [PATCH 13/13] =?UTF-8?q?=F0=9F=91=8DAdd=20swagger=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- swagger/README.md | 19 ++++++++++ swagger/components/auth/client-data.yml | 38 ++++++++++++++++++++ swagger/components/auth/token-data.yml | 19 ++++++++++ swagger/components/common/profile-data.yml | 19 ++++++++++ swagger/documentation.yml | 40 ++++++++++++++++++++++ swagger/paths/oauth2/introspect.yml | 14 ++++++++ swagger/paths/oauth2/revoke.yml | 14 ++++++++ swagger/paths/oauth2/userinfo.yml | 13 +++++++ 8 files changed, 176 insertions(+) create mode 100644 swagger/README.md create mode 100644 swagger/components/auth/client-data.yml create mode 100644 swagger/components/auth/token-data.yml create mode 100644 swagger/components/common/profile-data.yml create mode 100644 swagger/documentation.yml create mode 100644 swagger/paths/oauth2/introspect.yml create mode 100644 swagger/paths/oauth2/revoke.yml create mode 100644 swagger/paths/oauth2/userinfo.yml diff --git a/swagger/README.md b/swagger/README.md new file mode 100644 index 0000000..e82f1f1 --- /dev/null +++ b/swagger/README.md @@ -0,0 +1,19 @@ +# OpenAPI documentation + +## Getting started + +```bash +npm install -g @stoplight/prism-cli +``` + +## Generate API documentation + +```bash +npx @redocly/cli preview-docs documentation.yml +``` + +## Mock API + +```bash +prism mock documentation.yml +``` \ No newline at end of file diff --git a/swagger/components/auth/client-data.yml b/swagger/components/auth/client-data.yml new file mode 100644 index 0000000..7d9eb6c --- /dev/null +++ b/swagger/components/auth/client-data.yml @@ -0,0 +1,38 @@ +ClientData: + type: object + required: + - clientId + - clientName + - redirectUri + properties: + clientId: + type: string + clientName: + type: string + redirectUri: + type: string + discriminator: + propertyName: clientType + mapping: + public: '#/PublicClientData' + confidential: '#/ConfidentialClientData' +PublicClientData: + allOf: + - $ref: '#/ClientData' + - type: object + properties: + clientType: + type: string + enum: + - public +ConfidentialClientData: + allOf: + - $ref: '#/ClientData' + - type: object + properties: + clientSecret: + type: string + clientType: + type: string + enum: + - confidential \ No newline at end of file diff --git a/swagger/components/auth/token-data.yml b/swagger/components/auth/token-data.yml new file mode 100644 index 0000000..52124e7 --- /dev/null +++ b/swagger/components/auth/token-data.yml @@ -0,0 +1,19 @@ +TokenData: + type: object + required: + - access_token + - token_type + - expires_in + - state + - refresh_token + properties: + access_token: + type: string + token_type: + type: string + expires_in: + type: integer + state: + type: string + refresh_token: + type: string \ No newline at end of file diff --git a/swagger/components/common/profile-data.yml b/swagger/components/common/profile-data.yml new file mode 100644 index 0000000..cb42b62 --- /dev/null +++ b/swagger/components/common/profile-data.yml @@ -0,0 +1,19 @@ +ProfileData: + type: object + properties: + sub: + type: string + description: "User ID" + example: "kstm_user" + nickname: + type: string + description: "User nickname" + example: "かすたむ" + picture: + type: string + description: "User picture URL" + example: "https://example.com/user/kstm_user/picture" + email: + type: string + description: "User email" + example: "kstm@example.com" \ No newline at end of file diff --git a/swagger/documentation.yml b/swagger/documentation.yml new file mode 100644 index 0000000..7075223 --- /dev/null +++ b/swagger/documentation.yml @@ -0,0 +1,40 @@ +openapi: '3.0.2' +info: + title: "kstm member portal" + description: | + This is the API documentation for the kstm member portal. + license: + name: CC0-1.0 + url: "https://creativecommons.org/publicdomain/zero/1.0/" + version: "1.0.0" + +servers: + - url: "http://localhost:8080" + description: "Local server" + - url: "https://api.example.com" + description: "Production server" + +components: + securitySchemes: + oauth2: + type: oauth2 + flows: + authorizationCode: + authorizationUrl: /oauth2/authorize + tokenUrl: /oauth2/token + refreshUrl: /oauth2/token + scopes: + openid: Grants read access for OpenID Connect + profile: Grants read access for my profile + write: Grants write access for all + read: Grants read access for all + +paths: + /oauth2/userinfo: + $ref: './paths/oauth2/userinfo.yml' +# /oauth2/revoke: # TODO - This is not implemented yet +# $ref: './paths/oauth2/revoke.yml' +# /oauth2/introspect: # TODO - This is not implemented yet +# $ref: './paths/oauth2/introspect.yml' + + diff --git a/swagger/paths/oauth2/introspect.yml b/swagger/paths/oauth2/introspect.yml new file mode 100644 index 0000000..71112ee --- /dev/null +++ b/swagger/paths/oauth2/introspect.yml @@ -0,0 +1,14 @@ +post: + responses: + '200': + description: OK + summary: Introspect access token + tags: + - oauth2 + parameters: + - name: token + in: query + required: true + schema: + type: string +# TODO - This is not implemented yet \ No newline at end of file diff --git a/swagger/paths/oauth2/revoke.yml b/swagger/paths/oauth2/revoke.yml new file mode 100644 index 0000000..d8c33e1 --- /dev/null +++ b/swagger/paths/oauth2/revoke.yml @@ -0,0 +1,14 @@ +post: + responses: + '200': + description: OK + summary: Revoke access token + tags: + - oauth2 + parameters: + - name: token + in: query + required: true + schema: + type: string +# TODO - This is not implemented yet \ No newline at end of file diff --git a/swagger/paths/oauth2/userinfo.yml b/swagger/paths/oauth2/userinfo.yml new file mode 100644 index 0000000..dc6a438 --- /dev/null +++ b/swagger/paths/oauth2/userinfo.yml @@ -0,0 +1,13 @@ +get: + summary: Get user information + tags: + - oauth2 + security: + - oauth2: [ profile ] + responses: + default: + content: + application/json: + schema: + $ref: '../../components/common/profile-data.yml#/ProfileData' + description: "User information" \ No newline at end of file