Commit 2529f43f82dc7685272d6ef5db33ecb858cccf8a

Authored by Marko Tikvić
1 parent 33d137a671
Exists in master and in 1 other branch v2

removed pritnfs

Showing 1 changed file with 0 additions and 2 deletions   Show diff stats
1 package webutility 1 package webutility
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 "net/http" 10 "net/http"
11 11
12 "github.com/dgrijalva/jwt-go" 12 "github.com/dgrijalva/jwt-go"
13 "fmt"
14 ) 13 )
15 14
16 const OneDay = time.Hour*24 15 const OneDay = time.Hour*24
17 const OneWeek = OneDay*7 16 const OneWeek = OneDay*7
18 const saltSize = 32 17 const saltSize = 32
19 const appName = "korisnicki-centar" 18 const appName = "korisnicki-centar"
20 const secret = "korisnicki-centar-api" 19 const secret = "korisnicki-centar-api"
21 20
22 const RoleAdmin string = "ADMINISTRATOR" 21 const RoleAdmin string = "ADMINISTRATOR"
23 const RoleManager string = "RUKOVODILAC" 22 const RoleManager string = "RUKOVODILAC"
24 const RoleReporter string = "REPORTER" 23 const RoleReporter string = "REPORTER"
25 const RoleOperator string = "OPERATER" 24 const RoleOperator string = "OPERATER"
26 25
27 // TokenClaims are JWT token claims. 26 // TokenClaims are JWT token claims.
28 type TokenClaims struct { 27 type TokenClaims struct {
29 Username string `json:"username"` 28 Username string `json:"username"`
30 Role string `json:"role"` 29 Role string `json:"role"`
31 jwt.StandardClaims 30 jwt.StandardClaims
32 } 31 }
33 32
34 // CredentialsStruct is an instace of username/password values. 33 // CredentialsStruct is an instace of username/password values.
35 type CredentialsStruct struct { 34 type CredentialsStruct struct {
36 Username string `json:"username"` 35 Username string `json:"username"`
37 Password string `json:"password"` 36 Password string `json:"password"`
38 } 37 }
39 38
40 // generateSalt returns a string of random characters of 'saltSize' length. 39 // generateSalt returns a string of random characters of 'saltSize' length.
41 func generateSalt() (salt string, err error) { 40 func generateSalt() (salt string, err error) {
42 rawsalt := make([]byte, saltSize) 41 rawsalt := make([]byte, saltSize)
43 42
44 _, err = rand.Read(rawsalt) 43 _, err = rand.Read(rawsalt)
45 if err != nil { 44 if err != nil {
46 return "", err 45 return "", err
47 } 46 }
48 47
49 salt = hex.EncodeToString(rawsalt) 48 salt = hex.EncodeToString(rawsalt)
50 return salt, nil 49 return salt, nil
51 } 50 }
52 51
53 // HashString hashes input string with SHA256 algorithm. 52 // HashString hashes input string with SHA256 algorithm.
54 // If the presalt parameter is not provided HashString will generate new salt string. 53 // If the presalt parameter is not provided HashString will generate new salt string.
55 // Returns hash and salt string or an error if it fails. 54 // Returns hash and salt string or an error if it fails.
56 func HashString(str string, presalt string) (hash, salt string, err error) { 55 func HashString(str string, presalt string) (hash, salt string, err error) {
57 // chech if message is presalted 56 // chech if message is presalted
58 if presalt == "" { 57 if presalt == "" {
59 salt, err = generateSalt() 58 salt, err = generateSalt()
60 if err != nil { 59 if err != nil {
61 return "", "", err 60 return "", "", err
62 } 61 }
63 } else { 62 } else {
64 salt = presalt 63 salt = presalt
65 } 64 }
66 65
67 // convert strings to raw byte slices 66 // convert strings to raw byte slices
68 rawstr := []byte(str) 67 rawstr := []byte(str)
69 rawsalt, err := hex.DecodeString(salt) 68 rawsalt, err := hex.DecodeString(salt)
70 if err != nil { 69 if err != nil {
71 return "", "", err 70 return "", "", err
72 } 71 }
73 72
74 rawdata := make([]byte, len(rawstr) + len(rawsalt)) 73 rawdata := make([]byte, len(rawstr) + len(rawsalt))
75 rawdata = append(rawdata, rawstr...) 74 rawdata = append(rawdata, rawstr...)
76 rawdata = append(rawdata, rawsalt...) 75 rawdata = append(rawdata, rawsalt...)
77 76
78 // hash message + salt 77 // hash message + salt
79 hasher := sha256.New() 78 hasher := sha256.New()
80 hasher.Write(rawdata) 79 hasher.Write(rawdata)
81 rawhash := hasher.Sum(nil) 80 rawhash := hasher.Sum(nil)
82 81
83 hash = hex.EncodeToString(rawhash) 82 hash = hex.EncodeToString(rawhash)
84 return hash, salt, nil 83 return hash, salt, nil
85 } 84 }
86 85
87 // CreateAPIToken returns JWT token with encoded username, role, expiration date and issuer claims. 86 // CreateAPIToken returns JWT token with encoded username, role, expiration date and issuer claims.
88 // It returns an error if it fails. 87 // It returns an error if it fails.
89 func CreateAPIToken(username, role string) (string, error) { 88 func CreateAPIToken(username, role string) (string, error) {
90 var apiToken string 89 var apiToken string
91 var err error 90 var err error
92 91
93 if err != nil { 92 if err != nil {
94 return "", err 93 return "", err
95 } 94 }
96 95
97 claims := TokenClaims{ 96 claims := TokenClaims{
98 username, 97 username,
99 role, 98 role,
100 jwt.StandardClaims{ 99 jwt.StandardClaims{
101 ExpiresAt: (time.Now().Add(OneWeek)).Unix(), 100 ExpiresAt: (time.Now().Add(OneWeek)).Unix(),
102 Issuer: appName, 101 Issuer: appName,
103 }, 102 },
104 } 103 }
105 104
106 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 105 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
107 apiToken, err = jwtToken.SignedString([]byte(secret)) 106 apiToken, err = jwtToken.SignedString([]byte(secret))
108 if err != nil { 107 if err != nil {
109 return "", err 108 return "", err
110 } 109 }
111 return apiToken, nil 110 return apiToken, nil
112 } 111 }
113 112
114 // RefreshAPIToken prolongs JWT token's expiration date for one week. 113 // RefreshAPIToken prolongs JWT token's expiration date for one week.
115 // It returns new JWT token or an error if it fails. 114 // It returns new JWT token or an error if it fails.
116 func RefreshAPIToken(tokenString string) (string, error) { 115 func RefreshAPIToken(tokenString string) (string, error) {
117 var newToken string 116 var newToken string
118 tokenString = strings.TrimPrefix(tokenString, "Bearer ") 117 tokenString = strings.TrimPrefix(tokenString, "Bearer ")
119 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc) 118 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc)
120 if err != nil { 119 if err != nil {
121 return "", err 120 return "", err
122 } 121 }
123 122
124 // type assertion 123 // type assertion
125 claims, ok := token.Claims.(*TokenClaims) 124 claims, ok := token.Claims.(*TokenClaims)
126 if !ok || !token.Valid { 125 if !ok || !token.Valid {
127 return "", errors.New("token is not valid") 126 return "", errors.New("token is not valid")
128 } 127 }
129 128
130 claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix() 129 claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix()
131 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 130 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
132 131
133 newToken, err = jwtToken.SignedString([]byte(secret)) 132 newToken, err = jwtToken.SignedString([]byte(secret))
134 if err != nil { 133 if err != nil {
135 return "", err 134 return "", err
136 } 135 }
137 136
138 return newToken, nil 137 return newToken, nil
139 } 138 }
140 139
141 // ParseAPIToken parses JWT token claims. 140 // ParseAPIToken parses JWT token claims.
142 // It returns a pointer to TokenClaims struct or an error if it fails. 141 // It returns a pointer to TokenClaims struct or an error if it fails.
143 func ParseAPIToken(tokenString string) (*TokenClaims, error) { 142 func ParseAPIToken(tokenString string) (*TokenClaims, error) {
144 if ok := strings.HasPrefix(tokenString, "Bearer "); ok { 143 if ok := strings.HasPrefix(tokenString, "Bearer "); ok {
145 tokenString = strings.TrimPrefix(tokenString, "Bearer ") 144 tokenString = strings.TrimPrefix(tokenString, "Bearer ")
146 } else { 145 } else {
147 return &TokenClaims{}, errors.New("Authorization header is incomplete") 146 return &TokenClaims{}, errors.New("Authorization header is incomplete")
148 } 147 }
149 148
150 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc) 149 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc)
151 if err != nil { 150 if err != nil {
152 return &TokenClaims{}, err 151 return &TokenClaims{}, err
153 } 152 }
154 153
155 // type assertion 154 // type assertion
156 claims, ok := token.Claims.(*TokenClaims) 155 claims, ok := token.Claims.(*TokenClaims)
157 if !ok || !token.Valid { 156 if !ok || !token.Valid {
158 return &TokenClaims{}, errors.New("token is not valid") 157 return &TokenClaims{}, errors.New("token is not valid")
159 } 158 }
160 return claims, nil 159 return claims, nil
161 } 160 }
162 161
163 func GetTokenClaims(r *http.Request) (claims *TokenClaims, err error) { 162 func GetTokenClaims(r *http.Request) (claims *TokenClaims, err error) {
164 token := r.Header.Get("Authorization") 163 token := r.Header.Get("Authorization")
165 if ok := strings.HasPrefix(token, "Bearer "); ok { 164 if ok := strings.HasPrefix(token, "Bearer "); ok {
166 token = strings.TrimPrefix(token, "Bearer ") 165 token = strings.TrimPrefix(token, "Bearer ")
167 } else { 166 } else {
168 return &TokenClaims{}, errors.New("Authorization header is incomplete") 167 return &TokenClaims{}, errors.New("Authorization header is incomplete")
169 } 168 }
170 169
171 parsedToken, err := jwt.ParseWithClaims(token, &TokenClaims{}, secretFunc) 170 parsedToken, err := jwt.ParseWithClaims(token, &TokenClaims{}, secretFunc)
172 if err != nil { 171 if err != nil {
173 return &TokenClaims{}, err 172 return &TokenClaims{}, err
174 } 173 }
175 174
176 // type assertion 175 // type assertion
177 claims, ok := parsedToken.Claims.(*TokenClaims) 176 claims, ok := parsedToken.Claims.(*TokenClaims)
178 if !ok || !parsedToken.Valid { 177 if !ok || !parsedToken.Valid {
179 return &TokenClaims{}, errors.New("token is not valid") 178 return &TokenClaims{}, errors.New("token is not valid")
180 } 179 }
181 return claims, err 180 return claims, err
182 } 181 }
183 182
184 // secretFunc returns byte slice of API secret keyword. 183 // secretFunc returns byte slice of API secret keyword.
185 func secretFunc(token *jwt.Token) (interface{}, error) { 184 func secretFunc(token *jwt.Token) (interface{}, error) {
186 return []byte(secret), nil 185 return []byte(secret), nil
187 } 186 }
188 187
189 // roleAuthorized returns true if role from userClaims matches any of the authorizedRoles 188 // roleAuthorized returns true if role from userClaims matches any of the authorizedRoles
190 // or if authorizedRoles contains "*". 189 // or if authorizedRoles contains "*".
191 func roleAuthorized(authorizedRoles []string, userClaims *TokenClaims) bool { 190 func roleAuthorized(authorizedRoles []string, userClaims *TokenClaims) bool {
192 if userClaims == nil { 191 if userClaims == nil {
193 return false 192 return false
194 } 193 }
195 for _, r := range authorizedRoles { 194 for _, r := range authorizedRoles {
196 fmt.Printf("comparing %s with %s\n", userClaims.Role, r)
197 if userClaims.Role == r || r == "*" { 195 if userClaims.Role == r || r == "*" {
198 return true 196 return true
199 } 197 }
200 } 198 }
201 return false 199 return false
202 } 200 }
203 201