Commit 33d137a67174c4877300a83d5741f58c3f160c65

Authored by Marko Tikvić
1 parent 3a5383589b
Exists in master and in 1 other branch v2

Functional role checking for API endpoints.

... ... @@ -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 +}
... ...
... ... @@ -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 }
... ...
... ... @@ -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  
... ...