├── .env.Example
├── .gitignore
├── LICENSE.md
├── README.md
├── app
└── auth.go
├── controllers
├── authControllers.go
├── fileController.go
└── permissionController.go
├── go.mod
├── go.sum
├── main.go
└── models
├── accounts.go
├── base.go
├── files.go
└── permissions.go
/.env.Example:
--------------------------------------------------------------------------------
1 | db_name = test
2 | db_pass = test
3 | db_user = test
4 | db_type = test
5 | db_host = test
6 | db_port = test
7 | token_password = test
8 | database_url = postgres://"db_name":"db_pass"@"db_host:"db_port"/tests
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .env
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Adigun Hammed Olalekan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HTTP-server-with-auth# HTTP Server With Authentication
2 |
3 | # Introduction
4 |
5 | You are to use gin framework package and concurrency in golang and jwt-go to implement a simple http server.
6 | You should implement an rest api with authentication on some endpoints to upload or download a file.
7 | All registered users can upload and download files. no user can download other user's files. users can
8 | give access to other specific users to download their files.
9 | REST APIs that you should implement are listed below:
10 |
11 | ## `localhost:8080/register`
12 | 1. **json** :
13 | ```json
14 | {
15 | "username" : "string",
16 | "password" : "string"
17 | }
18 | ```
19 | create new user
20 | ## `localhost:8080/login`
21 | 1. **json** :
22 | ```json
23 | {
24 | "username" : "string",
25 | "password" : "string"
26 | }
27 | ```
28 | logins and gives access token
29 |
30 | ## `localhost:8080/uploadFile` with auth
31 |
32 | ***input formats*** :
33 |
34 |
35 | 1. **form** :
36 |
37 | file : []byte
38 |
39 | In this format, `file` is a byte array of the actual file.
40 | uploads file in filesystem.
41 |
42 |
43 | ***output format*** :
44 |
45 | 1. **json** :
46 |
47 | *successful upload* :
48 | ```json
49 | {
50 | "download_url" : "string"
51 | }
52 | ```
53 |
54 | `download_url` is the path of saved file in filesystem.
55 |
56 | *failure upload* :
57 | ```json
58 | {
59 | "error" : "string"
60 | }
61 | ```
62 |
63 | `error` is a description of the occurred error.
64 |
65 |
66 |
67 |
68 |
69 | ## `localhost:8080/downloadFile` with auth
70 |
71 | 1. **json** :
72 | ```json
73 | {
74 | "download_url" : "string"
75 | }
76 | ```
77 |
78 | In this format, `download_url` is an id that we got in successful upload request. it will download the file if user has download premission.
79 |
80 |
81 |
82 |
83 | ***output format*** :
84 |
85 | 1. **json** :
86 |
87 | *successful download* :
88 |
89 | http response with actual file
90 | \
91 | *failure download* :
92 | ```json
93 | {
94 | "error" : "string"
95 | }
96 | ```
97 |
98 | `error` is a description of the occurred error.
99 |
100 |
101 | ## `localhost:8080/addPremission` with auth
102 |
103 | 1. **json** :
104 | ```json
105 | {
106 | "download_url" : "string",
107 | "user_to_be_add" : "string"
108 | }
109 | ```
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/app/auth.go:
--------------------------------------------------------------------------------
1 | package app
2 | import (
3 | "fmt"
4 | "github.com/dgrijalva/jwt-go"
5 | "github.com/gin-gonic/gin"
6 | "http-server-with-auth/models"
7 | "net/http"
8 | "os"
9 | "strings"
10 | )
11 |
12 | func AuthorizeJWT(c *gin.Context) {
13 | tokenHeader := c.GetHeader("Authorization")
14 | if tokenHeader == "" {
15 | c.Header("Content-Type", "application/json")
16 | c.JSON(http.StatusForbidden, gin.H{
17 | "error": true,
18 | "message": "missing token",
19 | })
20 | return
21 | }
22 | //The token normally comes in format `Bearer {token-body}`, we check if the retrieved token matched this requirement
23 | splitted := strings.Split(tokenHeader, " ")
24 | if len(splitted) != 2 {
25 | c.JSON(http.StatusForbidden, gin.H{
26 | "message": "Invalid/Malformed auth token",
27 | })
28 | c.Header("Content-Type", "application/json")
29 | return
30 | }
31 | //Grab the token part
32 | tokenPart := splitted[1]
33 | tk := &models.Token{}
34 | token, err := jwt.ParseWithClaims(tokenPart, tk, func(token *jwt.Token) (interface{}, error) {
35 | return []byte(os.Getenv("token_password")), nil
36 | })
37 |
38 | if err != nil { //Malformed token, returns with http code 403 as usual
39 | c.JSON(403, gin.H{
40 | "error": true,
41 | "message": "Malformed authentication token",
42 | })
43 | c.Header("Content-Type", "application/json")
44 | return
45 | }
46 |
47 | if !token.Valid { //Token is invalid, maybe not signed on this server
48 | c.JSON(http.StatusForbidden, gin.H{
49 | "message": "Malformed authentication token",
50 | })
51 | c.Header("Content-Type", "application/json")
52 | return
53 | }
54 | //Everything went well, proceed with the request and set the caller to the user retrieved from the parsed token
55 | fmt.Sprintf("User %", tk.UserId) //Useful for monitoring
56 |
57 | c.Set("user", tk.UserId)
58 | //c.Request.WithContext(context.WithValue(c.Request.Context(), "user", tk.UserId))
59 | c.Next()
60 | //ctx := context.WithValue(r.Context(), "user", tk.UserId)
61 | //r = r.WithContext(ctx)
62 | //next.ServeHTTP(w, r) //proceed in the middleware chain!
63 | }
64 |
65 |
66 |
--------------------------------------------------------------------------------
/controllers/authControllers.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "http-server-with-auth/models"
6 | )
7 |
8 | var Register = func (c *gin.Context) {
9 | account := &models.Account{}
10 | err := c.ShouldBindJSON(account)
11 | if err != nil {
12 | c.JSON(422, gin.H{
13 | "error": true,
14 | "message": "invalid request body",
15 | })
16 | return
17 | }
18 | account.Validate(c)
19 | }
20 |
21 |
22 | var Authenticate = func(c *gin.Context) {
23 | account := &models.Account{}
24 | err := c.ShouldBindJSON(account)
25 | if err != nil {
26 | c.JSON(404, "invalid request body")
27 | return
28 | }
29 | models.Login(account.Email, account.Password, c)
30 | }
31 |
--------------------------------------------------------------------------------
/controllers/fileController.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "fmt"
5 | "github.com/gin-gonic/gin"
6 | "http-server-with-auth/models"
7 | "io"
8 | "log"
9 | "net/http"
10 | "os"
11 | )
12 |
13 | var Upload = func(c *gin.Context) {
14 | file, header, err := c.Request.FormFile("file")
15 | if err != nil {
16 | c.String(http.StatusBadRequest, fmt.Sprintf("file err : %s", err.Error()))
17 | return
18 | }
19 | filename := header.Filename
20 | out, err := os.Create("public/" + filename)
21 | if err != nil {
22 | log.Fatal(err)
23 | }
24 | defer func(out *os.File) {
25 | err := out.Close()
26 | if err != nil {
27 | log.Fatal(err)
28 | }
29 | }(out)
30 | _, err = io.Copy(out, file)
31 | if err != nil {
32 | log.Fatal(err)
33 | }
34 |
35 | filepath := "http://localhost:8080/file/" + filename
36 |
37 | c.JSON(http.StatusOK, gin.H{
38 | "message": "file uploaded successfully",
39 | "filepath": filepath,
40 | })
41 | models.Validate(c, filename)
42 | }
43 |
44 | var Download = func(c *gin.Context) {
45 | //fmt.Println(c.Request.Context().Value("user").(uint))
46 | id := c.Value("user").(uint)
47 | file := &models.File{}
48 | err := c.ShouldBindJSON(file)
49 | if err != nil {
50 | c.JSON(422, gin.H{
51 | "error": true,
52 | "message": "invalid request body",
53 | })
54 | return
55 | }
56 | if !models.IsPermitted(file.FileName, id) {
57 | c.JSON(http.StatusForbidden, gin.H{
58 | "error": true,
59 | "message": "you are not permitted",
60 | })
61 | return
62 | }
63 | c.JSON(200, gin.H{
64 | "error": false,
65 | "message": "you are allowed",
66 | })
67 |
68 | c.File("./public/" + file.FileName)
69 | }
70 |
--------------------------------------------------------------------------------
/controllers/permissionController.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "http-server-with-auth/models"
6 | )
7 |
8 | var GivePermission = func(c* gin.Context) {
9 | permission := &models.Permission{}
10 | err := c.ShouldBindJSON(permission)
11 | if err != nil{
12 | c.JSON(422, gin.H{
13 | "error": true,
14 | "message": "invalid request body",
15 | })
16 | return
17 | }
18 | permission.Create(c)
19 | }
20 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module http-server-with-auth
2 |
3 | go 1.17
4 |
5 | require (
6 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd // indirect
7 | github.com/dgrijalva/jwt-go v3.2.0+incompatible
8 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect
9 | github.com/go-sql-driver/mysql v1.5.0 // indirect
10 | github.com/gorilla/context v1.1.1 // indirect
11 | github.com/gorilla/mux v1.8.0
12 | github.com/jinzhu/gorm v1.9.16
13 | github.com/jinzhu/inflection v1.0.0 // indirect
14 | github.com/jinzhu/now v1.0.1 // indirect
15 | github.com/joho/godotenv v1.4.0
16 | github.com/lib/pq v1.1.1 // indirect
17 | github.com/mattn/go-sqlite3 v1.14.0 // indirect
18 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
19 | google.golang.org/appengine v1.6.4 // indirect
20 | )
21 |
22 | require (
23 | github.com/gin-contrib/sse v0.1.0 // indirect
24 | github.com/gin-gonic/gin v1.7.4 // indirect
25 | github.com/go-playground/locales v0.13.0 // indirect
26 | github.com/go-playground/universal-translator v0.17.0 // indirect
27 | github.com/go-playground/validator/v10 v10.4.1 // indirect
28 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
29 | github.com/golang/protobuf v1.3.3 // indirect
30 | github.com/json-iterator/go v1.1.9 // indirect
31 | github.com/leodido/go-urn v1.2.0 // indirect
32 | github.com/mattn/go-isatty v0.0.12 // indirect
33 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
34 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
35 | github.com/ugorji/go/codec v1.1.7 // indirect
36 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect
37 | golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect
38 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd // indirect
39 | golang.org/x/text v0.3.2 // indirect
40 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b // indirect
41 | gopkg.in/yaml.v2 v2.2.8 // indirect
42 | )
43 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
2 | github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5 | github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0 h1:epsH3lb7KVbXHYk7LYGN5EiE0MxcevHU85CKITJ0wUY=
6 | github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
7 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
8 | github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
9 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
10 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
11 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
12 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
13 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
14 | github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM=
15 | github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
16 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
17 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
18 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
19 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
20 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
21 | github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
22 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
23 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
24 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
25 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
26 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
27 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
28 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
29 | github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
30 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
31 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
32 | github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
33 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
34 | github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
35 | github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
36 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
37 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
38 | github.com/jinzhu/gorm v1.9.1 h1:lDSDtsCt5AGGSKTs8AHlSDbbgif4G4+CKJ8ETBDVHTA=
39 | github.com/jinzhu/gorm v1.9.1/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
40 | github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
41 | github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
42 | github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k=
43 | github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
44 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
45 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
46 | github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
47 | github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
48 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
49 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
50 | github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
51 | github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
52 | github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
53 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
54 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
55 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
56 | github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
57 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
58 | github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
59 | github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
60 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
61 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
62 | github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
63 | github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
64 | github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
65 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
66 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
67 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
68 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
69 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
70 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
71 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
72 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
73 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
74 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
75 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
76 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
77 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
78 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
79 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
80 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
81 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
82 | golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
83 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
84 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
85 | golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
86 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
87 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
88 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
89 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
90 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
91 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
92 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
93 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
94 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
95 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
96 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
97 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
98 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
99 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
100 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
101 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
102 | google.golang.org/appengine v1.6.4 h1:WiKh4+/eMB2HaY7QhCfW/R7MuRAoA8QMCSJA6jP5/fo=
103 | google.golang.org/appengine v1.6.4/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
104 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
105 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
106 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
107 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
108 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/gin-gonic/gin"
6 | _ "github.com/gorilla/mux"
7 | "http-server-with-auth/app"
8 | "http-server-with-auth/controllers"
9 | "net/http"
10 | "os"
11 | )
12 |
13 | func main() {
14 | router := gin.Default()
15 | router.POST("/user/new", controllers.Register)
16 | router.POST("/user/login", app.AuthorizeJWT, controllers.Authenticate)
17 | router.POST("/user/upload", controllers.Upload)
18 | router.GET("/user/download", app.AuthorizeJWT, controllers.Download)
19 | router.POST("/user/give/permission", controllers.GivePermission)
20 |
21 | port := os.Getenv("PORT")
22 | if port == "" {
23 | port = "8040" //localhost
24 | }
25 |
26 | fmt.Println(port)
27 |
28 | err := http.ListenAndServe(":"+port, router) //Launch the app, visit localhost:8000/api
29 | if err != nil {
30 | fmt.Print(err)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/models/accounts.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "github.com/dgrijalva/jwt-go"
5 | "github.com/gin-gonic/gin"
6 | "github.com/jinzhu/gorm"
7 | "golang.org/x/crypto/bcrypt"
8 | "os"
9 | "strings"
10 | )
11 |
12 | /*
13 | JWT claims struct
14 | */
15 | type Token struct {
16 | UserId uint
17 | Email string
18 | jwt.StandardClaims
19 | }
20 |
21 | //a struct to rep user account
22 | type Account struct {
23 | gorm.Model
24 | Email string `json:"email"`
25 | Password string `json:"password"`
26 | Token string `json:"token"`
27 | }
28 |
29 | //Validate incoming user details...
30 | func (account *Account) Validate(c *gin.Context) {
31 |
32 | if !strings.Contains(account.Email, "@") {
33 | c.JSON(422, gin.H{
34 | "error": true,
35 | "message": "email must contain @",
36 | })
37 | return
38 | }
39 |
40 | if len(account.Password) < 6 {
41 | c.JSON(422, gin.H{
42 | "error": true,
43 | "message": "password is too short",
44 | })
45 | return
46 | }
47 | //Email must be unique
48 | temp := &Account{}
49 |
50 | //check for errors and duplicate emails
51 | err := GetDB().Table("accounts").Where("email = ?", account.Email).First(temp).Error
52 |
53 | if err != nil && err != gorm.ErrRecordNotFound {
54 | c.JSON(422, gin.H{
55 | "error": true,
56 | "message": "connection error, try again",
57 | })
58 | return
59 | }
60 |
61 | if temp.Email != "" {
62 | c.JSON(422, gin.H{
63 | "error": true,
64 | "message": "email address in use",
65 | })
66 | return
67 | }
68 | account.Create(c)
69 |
70 | c.JSON(200, gin.H{
71 | "error": false,
72 | "message": "requirement passed",
73 | })
74 | return
75 | }
76 |
77 | func (account *Account) Create(c *gin.Context) {
78 |
79 | hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(account.Password), bcrypt.DefaultCost)
80 | account.Password = string(hashedPassword)
81 |
82 | GetDB().Create(account)
83 |
84 | if account.ID <= 0 {
85 | c.JSON(422, gin.H{
86 | "error": true,
87 | "message": "connection error, try again",
88 | })
89 | }
90 |
91 | //Create new JWT token for the newly registered account
92 | tk := &Token{UserId: account.ID}
93 | token := jwt.NewWithClaims(jwt.GetSigningMethod("HS256"), tk)
94 | tokenString, _ := token.SignedString([]byte(os.Getenv("token_password")))
95 | account.Token = tokenString
96 | account.Password = "" //delete password
97 | c.JSON(200, gin.H{
98 | "error": false,
99 | "message": "account has been created",
100 | })
101 | c.Header("Postman-Token", account.Token)
102 | c.JSON(200, account)
103 |
104 | }
105 |
106 | func Login(email, password string, c *gin.Context) {
107 |
108 | account := &Account{}
109 | err := GetDB().Table("accounts").Where("email = ?", email).First(account).Error
110 | if err != nil {
111 | if err == gorm.ErrRecordNotFound {
112 | c.JSON(420, gin.H{
113 | "error": true,
114 | "message": "email address not found",
115 | })
116 | return
117 | }
118 | c.JSON(420, gin.H{
119 | "error": true,
120 | "message": "connection error, try again",
121 | })
122 | return
123 | }
124 |
125 | err = bcrypt.CompareHashAndPassword([]byte(account.Password), []byte(password))
126 | if err != nil && err == bcrypt.ErrMismatchedHashAndPassword { //Password does not match!
127 | c.JSON(420, gin.H{
128 | "error": true,
129 | "message": "Invalid login credentials. Please try again",
130 | })
131 | return
132 | }
133 | //Worked! Logged In
134 | account.Password = ""
135 | //Create JWT token
136 | tk := &Token{UserId: account.ID, Email: account.Email}
137 | token := jwt.NewWithClaims(jwt.GetSigningMethod("HS256"), tk)
138 | tokenString, _ := token.SignedString([]byte(os.Getenv("token_password")))
139 | account.Token = tokenString //Store the token in the response
140 | c.JSON(200, gin.H{
141 | "error": false,
142 | "message": "you are logged in",
143 | })
144 | }
145 |
146 | func GetUser(u uint) *Account {
147 | acc := &Account{}
148 | GetDB().Table("accounts").Where("id = ?", u).First(acc)
149 | if acc.Email == "" { //User not found!
150 | return nil
151 | }
152 | acc.Password = ""
153 | return acc
154 | }
155 |
--------------------------------------------------------------------------------
/models/base.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "fmt"
5 | "github.com/jinzhu/gorm"
6 | _ "github.com/jinzhu/gorm/dialects/postgres"
7 | "github.com/joho/godotenv"
8 | "os"
9 | )
10 |
11 | var db *gorm.DB
12 |
13 | func init() {
14 |
15 | e := godotenv.Load()
16 | if e != nil {
17 | fmt.Print(e)
18 | }
19 |
20 | username := os.Getenv("db_user")
21 | password := os.Getenv("db_pass")
22 | dbName := os.Getenv("db_name")
23 | dbHost := os.Getenv("db_host")
24 |
25 | dbUri := fmt.Sprintf("host=%s user=%s dbname=%s sslmode=disable password=%s", dbHost, username, dbName, password)
26 | fmt.Println(dbUri)
27 |
28 | conn, err := gorm.Open("postgres", dbUri)
29 | if err != nil {
30 | fmt.Print(err)
31 | }
32 |
33 | db = conn
34 | db.Debug().AutoMigrate(&Account{}, &File{}, &Permission{})
35 | }
36 |
37 | func GetDB() *gorm.DB {
38 | return db
39 | }
40 |
--------------------------------------------------------------------------------
/models/files.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | )
6 |
7 | type File struct {
8 | FileName string `json:"file_name"`
9 | }
10 |
11 | func Validate(c *gin.Context, fileName string){
12 | temp := &File{}
13 | GetDB().Table("files").Where("file_name =?", fileName).First(temp)
14 | if temp.FileName != "" {
15 | c.JSON(422, gin.H{
16 | "error": true,
17 | "message": "filename is already taken",
18 | })
19 | return
20 | }
21 | c.JSON(200, gin.H{
22 | "error": false,
23 | "message": "file is created successfully",
24 | })
25 | Create(c, fileName)
26 | }
27 |
28 | func Create(c *gin.Context, fileName string) {
29 | file := File{FileName: fileName}
30 | GetDB().Create(&file)
31 | c.JSON(200, gin.H{
32 | "error": false,
33 | "message": file.FileName,
34 | })
35 | }
--------------------------------------------------------------------------------
/models/permissions.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "fmt"
5 | "github.com/gin-gonic/gin"
6 | "github.com/jinzhu/gorm"
7 | )
8 |
9 | type Permission struct {
10 | gorm.Model
11 | FileName string
12 | UserName string
13 | }
14 |
15 | func (permission *Permission) Validate(c *gin.Context) {
16 | temp := &Permission{}
17 | err := GetDB().Table("permissions").Where("username = ?", permission.UserName).First(temp).Error
18 | if err != nil {
19 | c.JSON(422, gin.H{
20 | "error": true,
21 | "message": "connection error, please try again",
22 | })
23 | return
24 | }
25 | err = GetDB().Table("files").Where("permission = ?", permission.FileName).First(temp).Error
26 | if err != nil {
27 | c.JSON(422, gin.H{
28 | "error": true,
29 | "message": "connection error, please try again",
30 | })
31 | return
32 | }
33 |
34 | if temp.UserName == "" {
35 | c.JSON(422, gin.H{
36 | "error": true,
37 | "message": "this username is not registered",
38 | })
39 | return
40 | }
41 |
42 | if temp.FileName == "" {
43 | c.JSON(422, gin.H{
44 | "error": true,
45 | "message": "this file name is not registered",
46 | })
47 | return
48 | }
49 |
50 | c.JSON(200, gin.H{
51 | "error": false,
52 | "message": "permission is given successfully",
53 | })
54 | permission.Create(c)
55 | }
56 |
57 | func (permission *Permission) Create(c *gin.Context) {
58 | GetDB().Create(permission)
59 | c.JSON(200, permission)
60 | }
61 |
62 | func IsPermitted(fileName string, id uint) bool {
63 | account := &Account{}
64 | userName := db.Table("accounts").Select("email").Where("id = ?", id).Find(account)
65 | fmt.Println(account)
66 | err := db.Where(
67 | db.Where("file_name = ?", fileName).Where(db.Where("user_name = ?", userName)),
68 | ).Find(&Permission{}).Error
69 | if err != nil {
70 | fmt.Println(err)
71 | return false
72 | }
73 | return true
74 | }
75 |
--------------------------------------------------------------------------------