From 688ae61bc6485771cc342433a78fbdeb8b678ad0 Mon Sep 17 00:00:00 2001 From: kandrusyak Date: Fri, 23 Jun 2023 02:18:00 +0300 Subject: [PATCH] Initial commit --- .gitignore | 2 ++ Dockerfile | 28 ++++++++++++++++ go.mod | 25 +++++++++++++++ go.sum | 67 +++++++++++++++++++++++++++++++++++++++ handler/auth.go | 48 ++++++++++++++++++++++++++++ handler/handler.go | 11 +++++++ handler/health.go | 14 ++++++++ main.go | 40 +++++++++++++++++++++++ model/user.go | 11 +++++++ mw/jwt.go | 26 +++++++++++++++ mw/token_from_cookies.go | 25 +++++++++++++++ test.db | Bin 0 -> 24576 bytes 12 files changed, 297 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 go.mod create mode 100644 go.sum create mode 100644 handler/auth.go create mode 100644 handler/handler.go create mode 100644 handler/health.go create mode 100644 main.go create mode 100644 model/user.go create mode 100644 mw/jwt.go create mode 100644 mw/token_from_cookies.go create mode 100644 test.db diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a36e5a0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +build \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3ccef56 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +FROM golang:1.21-rc-alpine as build +LABEL authors="andrusyakka@dopcore.com" + +RUN apk update +RUN apk upgrade +RUN apk add --update gcc g++ + +WORKDIR /app + +ENV CGO_ENABLED 1 + +COPY go.mod ./ + +RUN go mod download + +COPY . ./ + +RUN go build -o ./app + +FROM alpine:edge + +WORKDIR / + +COPY --from=build /app /app + +EXPOSE 8080 + +CMD ["/app/app"] \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..96202cf --- /dev/null +++ b/go.mod @@ -0,0 +1,25 @@ +module astra-api-gateway + +go 1.21rc1 + +require ( + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/golang-jwt/jwt/v5 v5.0.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/labstack/echo-jwt/v4 v4.2.0 // indirect + github.com/labstack/echo/v4 v4.10.2 // indirect + github.com/labstack/gommon v0.4.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-sqlite3 v1.14.17 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + golang.org/x/time v0.3.0 // indirect + gorm.io/driver/sqlite v1.5.2 // indirect + gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2fcfd28 --- /dev/null +++ b/go.sum @@ -0,0 +1,67 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/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/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/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.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M= +github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= +github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= +github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +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.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= +github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +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.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +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.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/sqlite v1.5.2 h1:TpQ+/dqCY4uCigCFyrfnrJnrW9zjpelWVoEVNy5qJkc= +gorm.io/driver/sqlite v1.5.2/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4= +gorm.io/gorm v1.25.1 h1:nsSALe5Pr+cM3V1qwwQ7rOkw+6UeLrX5O4v3llhHa64= +gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55 h1:sC1Xj4TYrLqg1n3AN10w871An7wJM0gzgcm8jkIkECQ= +gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= diff --git a/handler/auth.go b/handler/auth.go new file mode 100644 index 0000000..d553372 --- /dev/null +++ b/handler/auth.go @@ -0,0 +1,48 @@ +package handler + +import ( + "astra-api-gateway/model" + "crypto/sha256" + "fmt" + "github.com/golang-jwt/jwt/v5" + "github.com/labstack/echo/v4" + "net/http" + "strconv" + "time" +) + +func (h *Handler) Login(c echo.Context) (err error) { + // Bind + u := new(model.User) + if err = c.Bind(u); err != nil { + return + } + + u.Password = fmt.Sprintf("%x", sha256.Sum256([]byte(u.Password))) + + var t = h.DB.Where("user_name = ? AND password = ?", u.UserName, u.Password).First(&u) + + if t.RowsAffected == 0 { + return c.NoContent(http.StatusUnauthorized) + } + + token := jwt.New(jwt.SigningMethodHS256) + + claims := token.Claims.(jwt.MapClaims) + claims["id"] = strconv.Itoa(int(u.ID)) + claims["exp"] = time.Now().Add(time.Hour * 72).Unix() + + var _token, _ = token.SignedString([]byte(Key)) + if err != nil { + return + } + + c.SetCookie(&http.Cookie{ + Name: "accessToken", + Value: _token, + HttpOnly: true, + Path: "/", + }) + + return c.JSON(http.StatusOK, u) +} diff --git a/handler/handler.go b/handler/handler.go new file mode 100644 index 0000000..9bcf13d --- /dev/null +++ b/handler/handler.go @@ -0,0 +1,11 @@ +package handler + +import "gorm.io/gorm" + +type Handler struct { + DB *gorm.DB +} + +const ( + Key = "secret" +) diff --git a/handler/health.go b/handler/health.go new file mode 100644 index 0000000..c1d0429 --- /dev/null +++ b/handler/health.go @@ -0,0 +1,14 @@ +package handler + +import ( + "github.com/labstack/echo/v4" + "net/http" +) + +func (h *Handler) Health(c echo.Context) error { + type Resp struct { + Status string `json:"status"` + } + + return c.JSON(http.StatusOK, Resp{Status: "OK"}) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..3432a39 --- /dev/null +++ b/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "astra-api-gateway/handler" + "astra-api-gateway/model" + "astra-api-gateway/mw" + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" + "gorm.io/driver/sqlite" + "gorm.io/gorm" + "net/http" +) + +func main() { + db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) + if err != nil { + panic("failed to connect database") + } + e := echo.New() + + db.AutoMigrate(&model.User{}) + + // Middleware + e.Use(middleware.Logger()) + e.Use(mw.TokenFromCookies) + e.Use(mw.JWTMW) + + var h = &handler.Handler{DB: db} + + // Routes + e.GET("/*", checkAuth) + e.GET("/auth/login", h.Login) + e.GET("/health", h.Health) + + e.Logger.Fatal(e.Start(":8080")) +} + +func checkAuth(c echo.Context) error { + return c.HTML(http.StatusOK, c.Request().Header.Get("UserId")) +} diff --git a/model/user.go b/model/user.go new file mode 100644 index 0000000..16f09d7 --- /dev/null +++ b/model/user.go @@ -0,0 +1,11 @@ +package model + +import "gorm.io/gorm" + +type ( + User struct { + gorm.Model + UserName string `json:"userName" gorm:"unique"` + Password string `json:"password,omitempty"` + } +) diff --git a/mw/jwt.go b/mw/jwt.go new file mode 100644 index 0000000..2a2c589 --- /dev/null +++ b/mw/jwt.go @@ -0,0 +1,26 @@ +package mw + +import ( + "astra-api-gateway/handler" + "github.com/golang-jwt/jwt/v5" + "github.com/labstack/echo-jwt/v4" + "github.com/labstack/echo/v4" + "strings" +) + +var JWTMW = echojwt.WithConfig(echojwt.Config{ + SigningKey: []byte(handler.Key), + Skipper: func(c echo.Context) bool { + // Skip authentication for signup and login requests + if strings.Contains(c.Path(), "auth") || c.Path() == "/health" { + return true + } + return false + }, + SuccessHandler: func(c echo.Context) { + user := c.Get("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + c.Request().Header.Set("UserId", claims["id"].(string)) + c.Request().Header.Del("Authorization") + }, +}) diff --git a/mw/token_from_cookies.go b/mw/token_from_cookies.go new file mode 100644 index 0000000..bcf22be --- /dev/null +++ b/mw/token_from_cookies.go @@ -0,0 +1,25 @@ +package mw + +import ( + "github.com/labstack/echo/v4" + "net/http" +) + +func TokenFromCookies(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + var token, err = c.Request().Cookie("accessToken") + var authToken = c.Request().Header.Get("Authorization") + + if err != nil { + return next(c) + } + + if authToken != "" { + return c.NoContent(http.StatusBadRequest) + } + + c.Request().Header.Set("Authorization", "Bearer "+token.Value) + + return next(c) + } +} diff --git a/test.db b/test.db new file mode 100644 index 0000000000000000000000000000000000000000..3809ec7e142faa953cd33e5b6af62c8e4cd49669 GIT binary patch literal 24576 zcmeI)&u-H&90zbG*}pQ@Ok8q6ldSEsif+okCT%4o1{A4cTiMtoc8a~lK|%dTn~HMm zUV=y2YjEWi_5!}7%U=-vmHs$?kss!VGkV|@1Rwwb2tWV= z5P$##AOHafkWqbc(b(8f&QB74+7yGn==Fmj5v}f2QG0P%+c~Pyqn+owHOi*WFuKX& zkkPoEi1(sbWM@6W6A=bHVKkJbByNc!8+6n1ueG6Qircm6rw47`5{xEdn9$?>`oVFn z$hy4W|J3QpFH5V7Zw~8wJBKIqb?syvL!nSwS}^Jx3W?jH81_FlxVgQSxk2UAf8 z*3>~d{rr;N38%S5qhu(f`K)~$4kul1!pwT96O8))%i23Cul{yBMjQJRCbr3L`m;ho zhWON&9pX5+xsfkN#xRugy_=EA0{D3}*0Z|xe`y@jNxiCH5&gT|AVB~E5P$##AOHaf zKmY;|fB*y_aGwO`G@_`QMsn%eyqf<;^tRj}K>z{}fB*y_009U<00Izz00i!u!03^> zL>djUHd?Vw+u1U`E!&}{S#|xY<8NDT$+jH(iRn~L^N$VWD;CYpXCb)fmAG4~*rsdS zQ6+ROU$|w@7g2cTMyF+Ny4;GKD2l9zo4%-2%A)K=e&lo8=fWB3N*0vyo1h%q^K5@c zP}2=~*oxbK8_{_E|B~oe`lTG=^gVwh2tWV=5P$##AOHafKmY;|fB*#UtiVHUO}T!K zp)P4gE_;5EuK&v){UbpD0uX=z1Rwwb2tWV=5P$##AaMT$(*N?Cml^B-_kXN$g%E%M V1Rwwb2tWV=5P$##An+dw`~dJNFRuUq literal 0 HcmV?d00001