Commit 33d137a67174c4877300a83d5741f58c3f160c65
1 parent
3a5383589b
Exists in
master
and in
1 other branch
Functional role checking for API endpoints.
Showing
3 changed files
with
61 additions
and
9 deletions
Show diff stats
auth_utility.go
... | ... | @@ -7,7 +7,10 @@ import ( |
7 | 7 | "crypto/rand" |
8 | 8 | "encoding/hex" |
9 | 9 | "strings" |
10 | + "net/http" | |
11 | + | |
10 | 12 | "github.com/dgrijalva/jwt-go" |
13 | + "fmt" | |
11 | 14 | ) |
12 | 15 | |
13 | 16 | const OneDay = time.Hour*24 |
... | ... | @@ -16,6 +19,11 @@ const saltSize = 32 |
16 | 19 | const appName = "korisnicki-centar" |
17 | 20 | const secret = "korisnicki-centar-api" |
18 | 21 | |
22 | +const RoleAdmin string = "ADMINISTRATOR" | |
23 | +const RoleManager string = "RUKOVODILAC" | |
24 | +const RoleReporter string = "REPORTER" | |
25 | +const RoleOperator string = "OPERATER" | |
26 | + | |
19 | 27 | // TokenClaims are JWT token claims. |
20 | 28 | type TokenClaims struct { |
21 | 29 | Username string `json:"username"` |
... | ... | @@ -152,7 +160,43 @@ func ParseAPIToken(tokenString string) (*TokenClaims, error) { |
152 | 160 | return claims, nil |
153 | 161 | } |
154 | 162 | |
163 | +func GetTokenClaims(r *http.Request) (claims *TokenClaims, err error) { | |
164 | + token := r.Header.Get("Authorization") | |
165 | + if ok := strings.HasPrefix(token, "Bearer "); ok { | |
166 | + token = strings.TrimPrefix(token, "Bearer ") | |
167 | + } else { | |
168 | + return &TokenClaims{}, errors.New("Authorization header is incomplete") | |
169 | + } | |
170 | + | |
171 | + parsedToken, err := jwt.ParseWithClaims(token, &TokenClaims{}, secretFunc) | |
172 | + if err != nil { | |
173 | + return &TokenClaims{}, err | |
174 | + } | |
175 | + | |
176 | + // type assertion | |
177 | + claims, ok := parsedToken.Claims.(*TokenClaims) | |
178 | + if !ok || !parsedToken.Valid { | |
179 | + return &TokenClaims{}, errors.New("token is not valid") | |
180 | + } | |
181 | + return claims, err | |
182 | +} | |
183 | + | |
155 | 184 | // secretFunc returns byte slice of API secret keyword. |
156 | 185 | func secretFunc(token *jwt.Token) (interface{}, error) { |
157 | 186 | return []byte(secret), nil |
158 | 187 | } |
188 | + | |
189 | +// roleAuthorized returns true if role from userClaims matches any of the authorizedRoles | |
190 | +// or if authorizedRoles contains "*". | |
191 | +func roleAuthorized(authorizedRoles []string, userClaims *TokenClaims) bool { | |
192 | + if userClaims == nil { | |
193 | + return false | |
194 | + } | |
195 | + for _, r := range authorizedRoles { | |
196 | + fmt.Printf("comparing %s with %s\n", userClaims.Role, r) | |
197 | + if userClaims.Role == r || r == "*" { | |
198 | + return true | |
199 | + } | |
200 | + } | |
201 | + return false | |
202 | +} | ... | ... |
http_utility.go
... | ... | @@ -3,6 +3,7 @@ package webutility |
3 | 3 | import ( |
4 | 4 | "net/http" |
5 | 5 | "encoding/json" |
6 | + "fmt" | |
6 | 7 | ) |
7 | 8 | |
8 | 9 | const templateHttpErr500_EN = "An internal server error has occurred." |
... | ... | @@ -54,9 +55,9 @@ func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) { |
54 | 55 | } |
55 | 56 | |
56 | 57 | // TODO: Check for content type |
57 | -// WrapHandler sets common headers, checks for token validity and performs access control. | |
58 | +// WrapHandler sets common headers, checks for token validity and performs access control checks. | |
58 | 59 | // If authentication passes it calls the handlerFunc. |
59 | -func WrapHandler(handlerFunc http.HandlerFunc, auth bool) http.HandlerFunc { | |
60 | +func WrapHandler(handlerFunc http.HandlerFunc, authorizedRoles []string) http.HandlerFunc { | |
60 | 61 | return func(w http.ResponseWriter, req *http.Request) { |
61 | 62 | w.Header().Set("Access-Control-Allow-Origin", "*") |
62 | 63 | |
... | ... | @@ -73,9 +74,11 @@ func WrapHandler(handlerFunc http.HandlerFunc, auth bool) http.HandlerFunc { |
73 | 74 | return |
74 | 75 | } |
75 | 76 | |
76 | - if auth { | |
77 | + if authorizedRoles != nil { | |
77 | 78 | token := req.Header.Get("Authorization") |
78 | - if _, err := ParseAPIToken(token); err != nil { | |
79 | + claims, err := ParseAPIToken(token); | |
80 | + if err != nil || !roleAuthorized(authorizedRoles, claims) { | |
81 | + fmt.Printf("not authorized %s %s...\n", claims.Username, claims.Role) | |
79 | 82 | UnauthorizedResponse(w, req) |
80 | 83 | return |
81 | 84 | } | ... | ... |
json_utility.go
... | ... | @@ -171,11 +171,16 @@ func fetchJSON(db *ora.Ses, project string) ([]byte, error) { |
171 | 171 | bytes := make([]byte, 0) |
172 | 172 | if rset.Next() { |
173 | 173 | lob := rset.Row[0].(io.Reader) |
174 | - bytes, err = ioutil.ReadAll(lob) | |
175 | - if err != nil { | |
176 | - // Ignore for now, it's some weird streaming read/write LOB error. | |
177 | - // TODO: Find a fix for this. | |
178 | - //return nil, err | |
174 | + if lob != nil { | |
175 | + bytes, err = ioutil.ReadAll(lob) | |
176 | + if err != nil { | |
177 | + // TODO: Find a fix for this. | |
178 | + // Some weird streaming read/write LOB error. | |
179 | + // Ignore for now. | |
180 | + //return nil, err | |
181 | + } | |
182 | + } else { | |
183 | + return nil, errors.New("json config is null") | |
179 | 184 | } |
180 | 185 | } |
181 | 186 | ... | ... |