Commit 4b4ea384f8c90564e40be2d90ed474b41b4a0947

Authored by Marko Tikvić
1 parent 17a4d04476
Exists in master and in 1 other branch v2

hmm

1 package restutility 1 package restutility
2 2
3 import ( 3 import (
4 "errors" 4 "errors"
5 "time" 5 "time"
6 "crypto/sha256" 6 "crypto/sha256"
7 "crypto/rand" 7 "crypto/rand"
8 "encoding/hex" 8 "encoding/hex"
9 "strings" 9 "strings"
10 "github.com/dgrijalva/jwt-go" 10 "github.com/dgrijalva/jwt-go"
11 ) 11 )
12 12
13 const OneDay = time.Hour*24 13 const OneDay = time.Hour*24
14 const OneWeek = OneDay*7 14 const OneWeek = OneDay*7
15 const saltSize = 32 15 const saltSize = 32
16 const appName = "korisnicki-centar" 16 const appName = "korisnicki-centar"
17 const secret = "korisnicki-centar-api" 17 const secret = "korisnicki-centar-api"
18 18
19 type TokenClaims struct { 19 type TokenClaims struct {
20 Username string `json:"username"` 20 Username string `json:"username"`
21 Role string `json:"role"` 21 Role string `json:"role"`
22 jwt.StandardClaims 22 jwt.StandardClaims
23 } 23 }
24 24
25 type CredentialsStruct struct { 25 type CredentialsStruct struct {
26 Username string `json:"username"` 26 Username string `json:"username"`
27 Password string `json:"password"` 27 Password string `json:"password"`
28 } 28 }
29 29
30 func generateSalt() (salt string, error) { 30 func generateSalt() (salt string, err error) {
31 rawsalt := make([]byte, saltSize) 31 rawsalt := make([]byte, saltSize)
32 32
33 _, err := rand.Read(rawsalt) 33 _, err = rand.Read(rawsalt)
34 if err != nil { 34 if err != nil {
35 return "", err 35 return "", err
36 } 36 }
37 37
38 salt = hex.EncodeToString(rawsalt) 38 salt = hex.EncodeToString(rawsalt)
39 return salt, nil 39 return salt, nil
40 } 40 }
41 41
42 func HashString(str string, presalt string) (hash, salt string, err error) { 42 func HashString(str string, presalt string) (hash, salt string, err error) {
43 // chech if message is presalted 43 // chech if message is presalted
44 if presalt == "" { 44 if presalt == "" {
45 salt, err = generateSalt() 45 salt, err = generateSalt()
46 if err != nil { 46 if err != nil {
47 return "", "", err 47 return "", "", err
48 } 48 }
49 } else { 49 } else {
50 salt = presalt 50 salt = presalt
51 } 51 }
52 52
53 // convert strings to raw byte slices 53 // convert strings to raw byte slices
54 rawstr := []byte(str) 54 rawstr := []byte(str)
55 rawsalt, err := hex.DecodeString(salt) 55 rawsalt, err := hex.DecodeString(salt)
56 if err != nil { 56 if err != nil {
57 return "", "", err 57 return "", "", err
58 } 58 }
59 59
60 rawdata := make([]byte, len(rawstr) + len(rawsalt)) 60 rawdata := make([]byte, len(rawstr) + len(rawsalt))
61 rawdata = append(rawdata, rawstr...) 61 rawdata = append(rawdata, rawstr...)
62 rawdata = append(rawdata, rawsalt...) 62 rawdata = append(rawdata, rawsalt...)
63 63
64 // hash message + salt 64 // hash message + salt
65 hasher := sha256.New() 65 hasher := sha256.New()
66 hasher.Write(rawdata) 66 hasher.Write(rawdata)
67 rawhash := hasher.Sum(nil) 67 rawhash := hasher.Sum(nil)
68 68
69 hash = hex.EncodeToString(rawhash) 69 hash = hex.EncodeToString(rawhash)
70 return hash, salt, nil 70 return hash, salt, nil
71 } 71 }
72 72
73 func CreateAPIToken(username, role string) (string, error) { 73 func CreateAPIToken(username, role string) (string, error) {
74 var apiToken string 74 var apiToken string
75 var err error 75 var err error
76 76
77 if err != nil { 77 if err != nil {
78 return "", err 78 return "", err
79 } 79 }
80 80
81 claims := TokenClaims{ 81 claims := TokenClaims{
82 username, 82 username,
83 role, 83 role,
84 jwt.StandardClaims{ 84 jwt.StandardClaims{
85 ExpiresAt: (time.Now().Add(OneWeek)).Unix(), 85 ExpiresAt: (time.Now().Add(OneWeek)).Unix(),
86 Issuer: appName, 86 Issuer: appName,
87 }, 87 },
88 } 88 }
89 89
90 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 90 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
91 apiToken, err = jwtToken.SignedString([]byte(secret)) 91 apiToken, err = jwtToken.SignedString([]byte(secret))
92 if err != nil { 92 if err != nil {
93 return "", err 93 return "", err
94 } 94 }
95 return apiToken, nil 95 return apiToken, nil
96 } 96 }
97 97
98 func RefreshAPIToken(tokenString string) (string, error) { 98 func RefreshAPIToken(tokenString string) (string, error) {
99 var newToken string 99 var newToken string
100 tokenString = strings.TrimPrefix(tokenString, "Bearer ") 100 tokenString = strings.TrimPrefix(tokenString, "Bearer ")
101 token, err := parseTokenFunc(tokenString) 101 token, err := parseTokenFunc(tokenString)
102 if err != nil { 102 if err != nil {
103 return "", err 103 return "", err
104 } 104 }
105 105
106 // type assertion 106 // type assertion
107 claims, ok := token.Claims.(*TokenClaims) 107 claims, ok := token.Claims.(*TokenClaims)
108 if !ok || !token.Valid { 108 if !ok || !token.Valid {
109 return "", errors.New("token is not valid") 109 return "", errors.New("token is not valid")
110 } 110 }
111 111
112 claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix() 112 claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix()
113 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 113 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
114 114
115 newToken, err = jwtToken.SignedString([]byte(secret)) 115 newToken, err = jwtToken.SignedString([]byte(secret))
116 if err != nil { 116 if err != nil {
117 return "", err 117 return "", err
118 } 118 }
119 119
120 return newToken, nil 120 return newToken, nil
121 } 121 }
122 122
123 func ParseAPIToken(tokenString string) (*TokenClaims, error) { 123 func ParseAPIToken(tokenString string) (*TokenClaims, error) {
124 if ok := strings.HasPrefix(tokenString, "Bearer"); ok { 124 if ok := strings.HasPrefix(tokenString, "Bearer"); ok {
125 tokenString = strings.TrimPrefix(tokenString, "Bearer ") 125 tokenString = strings.TrimPrefix(tokenString, "Bearer ")
126 } else { 126 } else {
127 return &TokenClaims{}, errors.New("Authorization header is incomplete") 127 return &TokenClaims{}, errors.New("Authorization header is incomplete")
128 } 128 }
129 129
130 token, err := parseTokenFunc(tokenString) 130 token, err := parseTokenFunc(tokenString)
131 if err != nil { 131 if err != nil {
132 return &TokenClaims{}, err 132 return &TokenClaims{}, err
133 } 133 }
134 134
135 // type assertion 135 // type assertion
136 claims, ok := token.Claims.(*TokenClaims) 136 claims, ok := token.Claims.(*TokenClaims)
137 if !ok || !token.Valid { 137 if !ok || !token.Valid {
138 return &TokenClaims{}, errors.New("token is not valid") 138 return &TokenClaims{}, errors.New("token is not valid")
139 } 139 }
140 return claims, nil 140 return claims, nil
141 } 141 }
142 142
143 func parseTokenFunc(tokenString string) (*jwt.Token, error) { 143 func parseTokenFunc(tokenString string) (*jwt.Token, error) {
144 token, err := jwt.ParseWithClaims(tokenString, 144 token, err := jwt.ParseWithClaims(tokenString,
145 &TokenClaims{}, 145 &TokenClaims{},
146 func(token *jwt.Token) (interface{}, error) { 146 func(token *jwt.Token) (interface{}, error) {
147 return []byte(secret), nil 147 return []byte(secret), nil
148 }, 148 },
149 ) 149 )
150 return token, err 150 return token, err
151 } 151 }
152 152
format_utility.go
1 package restutility 1 package restutility
2 2
3 import ( 3 import (
4 "time" 4 "time"
5 "fmt"
5 ) 6 )
6 7
7 func UnixToDate(unix int64) time.Time { 8 func UnixToDate(unix int64) time.Time {
8 return time.Unix(unix, 0) 9 return time.Unix(unix, 0)
9 } 10 }
10 11
11 func DateToUnix(date interface{}) int64 { 12 func DateToUnix(date interface{}) int64 {
12 if date != nil { 13 if date != nil {
13 t := date.(time.Time) 14 t := date.(time.Time)
14 return t.Unix() 15 return t.Unix()
15 16
16 } 17 }
17 return 0 18 return 0
18 } 19 }
19 20
20 func EqualQuotes(stmt string) string { 21 func EqualQuotes(stmt string) string {
21 if stmt != "" { 22 if stmt != "" {
22 stmt = " = '" + stmt + "'" 23 stmt = fmt.Sprintf(" = '%s'", stmt)
23 } 24 }
24 return stmt 25 return stmt
25 } 26 }
26 27
27 func LikeQuotes(stmt string) string { 28 func LikeQuotes(stmt string) string {
28 if stmt != "" { 29 if stmt != "" {
29 stmt " LIKE UPPER('%" + stmt + "%')" 30 stmt = fmt.Sprintf(" LIKE UPPER('%%s%')", stmt)
30 } 31 }
31 return stmt 32 return stmt
32 } 33 }
33 34
34 35
1 package restutility 1 package restutility
2 2
3 import ( 3 import (
4 "net/http" 4 "net/http"
5 "encoding/json" 5 "encoding/json"
6 ) 6 )
7 7
8 const templateHttpErr500_EN = "An internal server error has occurred." 8 const templateHttpErr500_EN = "An internal server error has occurred."
9 const templateHttpErr500_RS = "Došlo je do greške na serveru." 9 const templateHttpErr500_RS = "Došlo je do greške na serveru."
10 const templateHttpErr400_EN = "Bad request: invalid request body." 10 const templateHttpErr400_EN = "Bad request: invalid request body."
11 const templateHttpErr400_RS = "Neispravan zahtev." 11 const templateHttpErr400_RS = "Neispravan zahtev."
12 const templateHttpErr401_EN = "Unauthorized request." 12 const templateHttpErr401_EN = "Unauthorized request."
13 const templateHttpErr401_RS = "Neautorizovan zahtev." 13 const templateHttpErr401_RS = "Neautorizovan zahtev."
14 14
15 type httpError struct { 15 type httpError struct {
16 Error []HttpErrorDesc `json:"error"` 16 Error []HttpErrorDesc `json:"error"`
17 Request string `json:"request"` 17 Request string `json:"request"`
18 } 18 }
19 19
20 type HttpErrorDesc struct { 20 type HttpErrorDesc struct {
21 Lang string `json:"lang"` 21 Lang string `json:"lang"`
22 Desc string `json:"description"` 22 Desc string `json:"description"`
23 } 23 }
24 24
25 func ErrorResponse(w http.ResponseWriter, r *http.Request, code int, desc []HttpErrorDesc) { 25 func ErrorResponse(w http.ResponseWriter, r *http.Request, code int, desc []HttpErrorDesc) {
26 err := httpError{ desc, r.Method + " " + r.URL.Path } 26 err := httpError{ desc, r.Method + " " + r.URL.Path }
27 w.WriteHeader(code) 27 w.WriteHeader(code)
28 json.NewEncoder(w).Encode(err) 28 json.NewEncoder(w).Encode(err)
29 } 29 }
30 30
31 func BadRequestResponse(w http.ResponseWriter, req *http.Request) { 31 func BadRequestResponse(w http.ResponseWriter, req *http.Request) {
32 ErrorResponse(w, req, http.StatusBadRequest, []HttpErrorDesc{ 32 ErrorResponse(w, req, http.StatusBadRequest, []HttpErrorDesc{
33 { "en", templateHttpErr400_EN }, 33 { "en", templateHttpErr400_EN },
34 { "rs", templateHttpErr400_RS }, 34 { "rs", templateHttpErr400_RS },
35 }) 35 })
36 } 36 }
37 37
38 func InternalServerErrorResponse(w http.ResponseWriter, req *http.Request) { 38 func InternalServerErrorResponse(w http.ResponseWriter, req *http.Request) {
39 ErrorResponse(w, req, http.StatusInternalServerError, []HttpErrorDesc{ 39 ErrorResponse(w, req, http.StatusInternalServerError, []HttpErrorDesc{
40 { "en", templateHttpErr500_EN }, 40 { "en", templateHttpErr500_EN },
41 { "rs", templateHttpErr500_RS }, 41 { "rs", templateHttpErr500_RS },
42 }) 42 })
43 } 43 }
44 44
45 func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) { 45 func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) {
46 ErrorResponse(w, req, http.StatusUnauthorized, []HttpErrorDesc{ 46 ErrorResponse(w, req, http.StatusUnauthorized, []HttpErrorDesc{
47 { "en", templateHttpErr500_EN }, 47 { "en", templateHttpErr500_EN },
48 { "rs", templateHttpErr500_RS }, 48 { "rs", templateHttpErr500_RS },
49 }) 49 })
50 } 50 }
51 51
52 // TODO: Add parameters to enable/disable roles authorization checks 52 // TODO: Add parameters to enable/disable roles authorization checks
53 // TODO: Check for content type 53 // TODO: Check for content type
54 // Sets common headers and checks for token validity. 54 // Sets common headers and checks for token validity.
55 func WrapHandler(handlerFunc http.HandlerFunc, needauth bool) http.HandlerFunc { 55 func WrapHandler(handlerFunc http.HandlerFunc, needauth bool) http.HandlerFunc {
56 return func(w http.ResponseWriter, req *http.Request) { 56 return func(w http.ResponseWriter, req *http.Request) {
57 w.Header().Set("Access-Control-Allow-Origin", "*") 57 w.Header().Set("Access-Control-Allow-Origin", "*")
58 58
59 w.Header().Set("Access-Control-Allow-Methods", 59 w.Header().Set("Access-Control-Allow-Methods",
60 "POST, GET, PUT, DELETE, OPTIONS") 60 "POST, GET, PUT, DELETE, OPTIONS")
61 61
62 w.Header().Set("Access-Control-Allow-Headers", 62 w.Header().Set("Access-Control-Allow-Headers",
63 "Accept, Content-Type, Content-Length, " 63 `Accept, Content-Type, Content-Length,
64 "Accept-Encoding, X-CSRF-Token, Authorization") 64 Accept-Encoding, X-CSRF-Token, Authorization`)
65 65
66 w.Header().Set("Content-Type", "application/json; charset=utf-8") 66 w.Header().Set("Content-Type", "application/json; charset=utf-8")
67 67
68 if req.Method == "OPTIONS" { 68 if req.Method == "OPTIONS" {
69 return 69 return
70 } 70 }
71 71
72 if needauth { 72 if needauth {
73 token := req.Header.Get("Authorization") 73 token := req.Header.Get("Authorization")
74 if _, err := ParseAPIToken(token); err != nil { 74 if _, err := ParseAPIToken(token); err != nil {
75 UnathorizedResponse(w, req, http.StatusUnauthorized) 75 UnauthorizedResponse(w, req)
76 return 76 return
77 } 77 }
78 } 78 }
79 79
80 err := req.ParseForm() 80 err := req.ParseForm()
81 if err != nil { 81 if err != nil {
82 BadRequestResponse(w, req) 82 BadRequestResponse(w, req)
83 return 83 return
84 } 84 }
85 85
86 // execute HandlerFunc 86 // execute HandlerFunc
87 handlerFunc(w, req) 87 handlerFunc(w, req)
88 } 88 }
89 } 89 }
90 90
91 func NotFoundHandler(w http.ResponseWriter, req *http.Request) { 91 func NotFoundHandler(w http.ResponseWriter, req *http.Request) {
92 ErrorResponse(w, req, http.StatusNotFound, []HttpErrorDesc{ 92 ErrorResponse(w, req, http.StatusNotFound, []HttpErrorDesc{
93 { "en", "Not found." }, 93 { "en", "Not found." },
94 { "rs", "Traženi resurs ne postoji." }, 94 { "rs", "Traženi resurs ne postoji." },
95 }) 95 })
96 } 96 }
97 97