Blame view
auth_utility.go
6.02 KB
ea858b8a7 refactoring |
1 |
package webutility |
90fd36e9b resolved some dep... |
2 3 |
import ( |
90fd36e9b resolved some dep... |
4 |
"crypto/rand" |
d2ddf82ef started on new rbac |
5 |
"crypto/sha256" |
90fd36e9b resolved some dep... |
6 |
"encoding/hex" |
d2ddf82ef started on new rbac |
7 |
"errors" |
33d137a67 Functional role c... |
8 |
"net/http" |
d2ddf82ef started on new rbac |
9 10 |
"strings" "time" |
33d137a67 Functional role c... |
11 |
|
90fd36e9b resolved some dep... |
12 |
"github.com/dgrijalva/jwt-go" |
90fd36e9b resolved some dep... |
13 |
) |
d2ddf82ef started on new rbac |
14 15 |
const OneDay = time.Hour * 24 const OneWeek = OneDay * 7 |
90fd36e9b resolved some dep... |
16 |
const saltSize = 32 |
d2ddf82ef started on new rbac |
17 18 |
const appName = "korisnicki-centar" const secret = "korisnicki-centar-api" |
90fd36e9b resolved some dep... |
19 |
|
d2ddf82ef started on new rbac |
20 |
type Role struct { |
077dae33c removed role cons... |
21 22 |
Name string `json:"name"` ID uint32 `json:"id"` |
d2ddf82ef started on new rbac |
23 |
} |
6ec91280b working on docume... |
24 |
// TokenClaims are JWT token claims. |
90fd36e9b resolved some dep... |
25 |
type TokenClaims struct { |
6620591d8 moved DeliverPayl... |
26 27 28 29 30 31 32 33 34 |
Token string `json:"access_token"` TokenType string `json:"token_type"` Username string `json:"username"` Role string `json:"role"` RoleID uint32 `json:"role_id"` ExpiresIn int64 `json:"expires_in"` // extending a struct jwt.StandardClaims |
90fd36e9b resolved some dep... |
35 |
} |
1d0f61553 can't fetch clob |
36 |
// CredentialsStruct is an instace of username/password values. |
90fd36e9b resolved some dep... |
37 38 39 |
type CredentialsStruct struct { Username string `json:"username"` Password string `json:"password"` |
4b3627bba added role ID to ... |
40 |
RoleID uint32 `json:"roleID"` |
90fd36e9b resolved some dep... |
41 |
} |
bc3671b26 refactoring token... |
42 43 44 |
// ValidateCredentials hashes pass and salt and returns comparison result with resultHash func ValidateCredentials(pass, salt, resultHash string) bool { hash, _, err := CreateHash(pass, salt) |
90fd36e9b resolved some dep... |
45 |
if err != nil { |
bc3671b26 refactoring token... |
46 |
return false |
90fd36e9b resolved some dep... |
47 |
} |
bc3671b26 refactoring token... |
48 |
return hash == resultHash |
90fd36e9b resolved some dep... |
49 |
} |
bc3671b26 refactoring token... |
50 51 52 53 |
// CreateHash hashes str using SHA256. // If the presalt parameter is not provided CreateHash will generate new salt string. // Returns hash and salt strings or an error if it fails. func CreateHash(str, presalt string) (hash, salt string, err error) { |
90fd36e9b resolved some dep... |
54 55 |
// chech if message is presalted if presalt == "" { |
bc3671b26 refactoring token... |
56 |
salt, err = randomSalt() |
90fd36e9b resolved some dep... |
57 58 59 60 61 62 63 64 |
if err != nil { return "", "", err } } else { salt = presalt } // convert strings to raw byte slices |
33fd58161 minor changes, sh... |
65 |
rawstr := []byte(str) |
90fd36e9b resolved some dep... |
66 67 68 69 |
rawsalt, err := hex.DecodeString(salt) if err != nil { return "", "", err } |
33fd58161 minor changes, sh... |
70 |
|
d2ddf82ef started on new rbac |
71 |
rawdata := make([]byte, len(rawstr)+len(rawsalt)) |
33fd58161 minor changes, sh... |
72 |
rawdata = append(rawdata, rawstr...) |
90fd36e9b resolved some dep... |
73 74 75 76 77 78 |
rawdata = append(rawdata, rawsalt...) // hash message + salt hasher := sha256.New() hasher.Write(rawdata) rawhash := hasher.Sum(nil) |
33fd58161 minor changes, sh... |
79 |
|
90fd36e9b resolved some dep... |
80 81 82 |
hash = hex.EncodeToString(rawhash) return hash, salt, nil } |
bc3671b26 refactoring token... |
83 |
// CreateAuthToken returns JWT token with encoded username, role, expiration date and issuer claims. |
6ec91280b working on docume... |
84 |
// It returns an error if it fails. |
bc3671b26 refactoring token... |
85 86 87 |
func CreateAuthToken(username string, role Role) (TokenClaims, error) { t0 := (time.Now()).Unix() t1 := (time.Now().Add(OneWeek)).Unix() |
90fd36e9b resolved some dep... |
88 |
claims := TokenClaims{ |
bc3671b26 refactoring token... |
89 90 91 92 93 |
TokenType: "Bearer", Username: username, Role: role.Name, RoleID: role.ID, ExpiresIn: t1 - t0, |
90fd36e9b resolved some dep... |
94 |
} |
bc3671b26 refactoring token... |
95 96 97 98 |
// initialize jwt.StandardClaims fields (anonymous struct) claims.IssuedAt = t0 claims.ExpiresAt = t1 claims.Issuer = appName |
90fd36e9b resolved some dep... |
99 100 |
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) |
bc3671b26 refactoring token... |
101 |
token, err := jwtToken.SignedString([]byte(secret)) |
90fd36e9b resolved some dep... |
102 |
if err != nil { |
bc3671b26 refactoring token... |
103 |
return TokenClaims{}, err |
90fd36e9b resolved some dep... |
104 |
} |
bc3671b26 refactoring token... |
105 106 |
claims.Token = token return claims, nil |
90fd36e9b resolved some dep... |
107 |
} |
bc3671b26 refactoring token... |
108 |
// RefreshAuthToken prolongs JWT token's expiration date for one week. |
6ec91280b working on docume... |
109 |
// It returns new JWT token or an error if it fails. |
bc3671b26 refactoring token... |
110 111 112 113 |
func RefreshAuthToken(req *http.Request) (TokenClaims, error) { authHead := req.Header.Get("Authorization") tokenstr := strings.TrimPrefix(authHead, "Bearer ") token, err := jwt.ParseWithClaims(tokenstr, &TokenClaims{}, secretFunc) |
90fd36e9b resolved some dep... |
114 |
if err != nil { |
bc3671b26 refactoring token... |
115 |
return TokenClaims{}, err |
90fd36e9b resolved some dep... |
116 117 118 119 120 |
} // type assertion claims, ok := token.Claims.(*TokenClaims) if !ok || !token.Valid { |
bc3671b26 refactoring token... |
121 |
return TokenClaims{}, errors.New("token is not valid") |
90fd36e9b resolved some dep... |
122 |
} |
bc3671b26 refactoring token... |
123 124 |
// extend token expiration date return CreateAuthToken(claims.Username, Role{claims.Role, claims.RoleID}) |
90fd36e9b resolved some dep... |
125 |
} |
bc3671b26 refactoring token... |
126 127 128 129 130 131 |
// RbacCheck returns true if role that made HTTP request is authorized to // access the resource it is targeting. // It exctracts user's role from the JWT token located in Authorization header of // http.Request and then compares it with the list of supplied roles and returns // true if there's a match, if "*" is provided or if the authRoles is nil. // Otherwise it returns false. |
2d79a4120 Responses contain... |
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
func RbacCheck(req *http.Request, authRoles []string) bool { if authRoles == nil { return true } // validate token and check expiration date claims, err := GetTokenClaims(req) if err != nil { return false } // check if token has expired if claims.ExpiresAt < (time.Now()).Unix() { return false } // check if role extracted from token matches // any of the provided (allowed) ones for _, r := range authRoles { if claims.Role == r || r == "*" { return true } } return false } // TODO func AuthCheck(req *http.Request, authRoles []string) (*TokenClaims, error) { |
bc3671b26 refactoring token... |
160 161 |
if authRoles == nil { return &TokenClaims{}, nil |
90fd36e9b resolved some dep... |
162 |
} |
bc3671b26 refactoring token... |
163 164 |
// validate token and check expiration date claims, err := GetTokenClaims(req) |
90fd36e9b resolved some dep... |
165 166 167 |
if err != nil { return &TokenClaims{}, err } |
bc3671b26 refactoring token... |
168 169 170 171 |
// check if token has expired if claims.ExpiresAt < (time.Now()).Unix() { return &TokenClaims{}, errors.New("token has expired") } |
90fd36e9b resolved some dep... |
172 |
|
bc3671b26 refactoring token... |
173 174 175 176 177 178 |
// check if role extracted from token matches // any of the provided (allowed) ones for _, r := range authRoles { if claims.Role == r || r == "*" { return claims, nil } |
90fd36e9b resolved some dep... |
179 |
} |
bc3671b26 refactoring token... |
180 |
|
2d79a4120 Responses contain... |
181 |
return claims, errors.New("role is not authorized") |
90fd36e9b resolved some dep... |
182 |
} |
bc3671b26 refactoring token... |
183 184 185 186 187 188 189 |
// GetTokenClaims extracts JWT claims from Authorization header of the request. // Returns token claims or an error. func GetTokenClaims(req *http.Request) (*TokenClaims, error) { // check for and strip 'Bearer' prefix var tokstr string authHead := req.Header.Get("Authorization") if ok := strings.HasPrefix(authHead, "Bearer "); ok { |
2d79a4120 Responses contain... |
190 |
tokstr = strings.TrimPrefix(authHead, "Bearer ") |
33d137a67 Functional role c... |
191 |
} else { |
bc3671b26 refactoring token... |
192 |
return &TokenClaims{}, errors.New("authorization header in incomplete") |
33d137a67 Functional role c... |
193 |
} |
bc3671b26 refactoring token... |
194 |
token, err := jwt.ParseWithClaims(tokstr, &TokenClaims{}, secretFunc) |
33d137a67 Functional role c... |
195 196 197 198 199 |
if err != nil { return &TokenClaims{}, err } // type assertion |
bc3671b26 refactoring token... |
200 201 |
claims, ok := token.Claims.(*TokenClaims) if !ok || !token.Valid { |
33d137a67 Functional role c... |
202 203 |
return &TokenClaims{}, errors.New("token is not valid") } |
33d137a67 Functional role c... |
204 |
|
bc3671b26 refactoring token... |
205 |
return claims, nil |
90fd36e9b resolved some dep... |
206 |
} |
33d137a67 Functional role c... |
207 |
|
bc3671b26 refactoring token... |
208 209 210 |
// randomSalt returns a string of random characters of 'saltSize' length. func randomSalt() (s string, err error) { rawsalt := make([]byte, saltSize) |
d2ddf82ef started on new rbac |
211 |
|
bc3671b26 refactoring token... |
212 |
_, err = rand.Read(rawsalt) |
d2ddf82ef started on new rbac |
213 |
if err != nil { |
bc3671b26 refactoring token... |
214 |
return "", err |
33d137a67 Functional role c... |
215 |
} |
d2ddf82ef started on new rbac |
216 |
|
bc3671b26 refactoring token... |
217 218 |
s = hex.EncodeToString(rawsalt) return s, nil |
33d137a67 Functional role c... |
219 |
} |
d2ddf82ef started on new rbac |
220 |
|
bc3671b26 refactoring token... |
221 222 223 |
// secretFunc returns byte slice of API secret keyword. func secretFunc(token *jwt.Token) (interface{}, error) { return []byte(secret), nil |
d2ddf82ef started on new rbac |
224 |
} |