Commit 6b16c88f25f9cb688e9f9f1fcf132e446fd3ab86

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

Working version with ora v4

Showing 2 changed files with 8 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 ) 13 )
14 14
15 const OneDay = time.Hour*24 15 const OneDay = time.Hour*24
16 const OneWeek = OneDay*7 16 const OneWeek = OneDay*7
17 const saltSize = 32 17 const saltSize = 32
18 const appName = "korisnicki-centar" 18 const appName = "korisnicki-centar"
19 const secret = "korisnicki-centar-api" 19 const secret = "korisnicki-centar-api"
20 20
21 const RoleAdmin string = "ADMINISTRATOR" 21 const RoleAdmin string = "ADMINISTRATOR"
22 const RoleManager string = "RUKOVODILAC" 22 const RoleManager string = "RUKOVODILAC"
23 const RoleReporter string = "REPORTER" 23 const RoleReporter string = "REPORTER"
24 const RoleOperator string = "OPERATER" 24 const RoleOperator string = "OPERATER"
25 const RoleAdminID uint32 = 1
26 const RoleManagerID uint32 = 2
27 const RoleReporterID uint32 = 3
28 const RoleOperatorID uint32 = 4
25 29
26 // TokenClaims are JWT token claims. 30 // TokenClaims are JWT token claims.
27 type TokenClaims struct { 31 type TokenClaims struct {
28 Username string `json:"username"` 32 Username string `json:"username"`
29 Role string `json:"role"` 33 Role string `json:"role"`
34 RoleID uint32 `json:"roleID"`
30 jwt.StandardClaims 35 jwt.StandardClaims
31 } 36 }
32 37
33 // CredentialsStruct is an instace of username/password values. 38 // CredentialsStruct is an instace of username/password values.
34 type CredentialsStruct struct { 39 type CredentialsStruct struct {
35 Username string `json:"username"` 40 Username string `json:"username"`
36 Password string `json:"password"` 41 Password string `json:"password"`
37 } 42 }
38 43
39 // generateSalt returns a string of random characters of 'saltSize' length. 44 // generateSalt returns a string of random characters of 'saltSize' length.
40 func generateSalt() (salt string, err error) { 45 func generateSalt() (salt string, err error) {
41 rawsalt := make([]byte, saltSize) 46 rawsalt := make([]byte, saltSize)
42 47
43 _, err = rand.Read(rawsalt) 48 _, err = rand.Read(rawsalt)
44 if err != nil { 49 if err != nil {
45 return "", err 50 return "", err
46 } 51 }
47 52
48 salt = hex.EncodeToString(rawsalt) 53 salt = hex.EncodeToString(rawsalt)
49 return salt, nil 54 return salt, nil
50 } 55 }
51 56
52 // HashString hashes input string with SHA256 algorithm. 57 // HashString hashes input string with SHA256 algorithm.
53 // If the presalt parameter is not provided HashString will generate new salt string. 58 // If the presalt parameter is not provided HashString will generate new salt string.
54 // Returns hash and salt string or an error if it fails. 59 // Returns hash and salt string or an error if it fails.
55 func HashString(str string, presalt string) (hash, salt string, err error) { 60 func HashString(str string, presalt string) (hash, salt string, err error) {
56 // chech if message is presalted 61 // chech if message is presalted
57 if presalt == "" { 62 if presalt == "" {
58 salt, err = generateSalt() 63 salt, err = generateSalt()
59 if err != nil { 64 if err != nil {
60 return "", "", err 65 return "", "", err
61 } 66 }
62 } else { 67 } else {
63 salt = presalt 68 salt = presalt
64 } 69 }
65 70
66 // convert strings to raw byte slices 71 // convert strings to raw byte slices
67 rawstr := []byte(str) 72 rawstr := []byte(str)
68 rawsalt, err := hex.DecodeString(salt) 73 rawsalt, err := hex.DecodeString(salt)
69 if err != nil { 74 if err != nil {
70 return "", "", err 75 return "", "", err
71 } 76 }
72 77
73 rawdata := make([]byte, len(rawstr) + len(rawsalt)) 78 rawdata := make([]byte, len(rawstr) + len(rawsalt))
74 rawdata = append(rawdata, rawstr...) 79 rawdata = append(rawdata, rawstr...)
75 rawdata = append(rawdata, rawsalt...) 80 rawdata = append(rawdata, rawsalt...)
76 81
77 // hash message + salt 82 // hash message + salt
78 hasher := sha256.New() 83 hasher := sha256.New()
79 hasher.Write(rawdata) 84 hasher.Write(rawdata)
80 rawhash := hasher.Sum(nil) 85 rawhash := hasher.Sum(nil)
81 86
82 hash = hex.EncodeToString(rawhash) 87 hash = hex.EncodeToString(rawhash)
83 return hash, salt, nil 88 return hash, salt, nil
84 } 89 }
85 90
86 // CreateAPIToken returns JWT token with encoded username, role, expiration date and issuer claims. 91 // CreateAPIToken returns JWT token with encoded username, role, expiration date and issuer claims.
87 // It returns an error if it fails. 92 // It returns an error if it fails.
88 func CreateAPIToken(username, role string) (string, error) { 93 func CreateAPIToken(username, role string, roleID uint32) (string, error) {
89 var apiToken string 94 var apiToken string
90 var err error 95 var err error
91 96
92 if err != nil { 97 if err != nil {
93 return "", err 98 return "", err
94 } 99 }
95 100
96 claims := TokenClaims{ 101 claims := TokenClaims{
97 username, 102 username,
98 role, 103 role,
104 roleID,
99 jwt.StandardClaims{ 105 jwt.StandardClaims{
100 ExpiresAt: (time.Now().Add(OneWeek)).Unix(), 106 ExpiresAt: (time.Now().Add(OneWeek)).Unix(),
101 Issuer: appName, 107 Issuer: appName,
102 }, 108 },
103 } 109 }
104 110
105 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 111 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
106 apiToken, err = jwtToken.SignedString([]byte(secret)) 112 apiToken, err = jwtToken.SignedString([]byte(secret))
107 if err != nil { 113 if err != nil {
108 return "", err 114 return "", err
109 } 115 }
110 return apiToken, nil 116 return apiToken, nil
111 } 117 }
112 118
113 // RefreshAPIToken prolongs JWT token's expiration date for one week. 119 // RefreshAPIToken prolongs JWT token's expiration date for one week.
114 // It returns new JWT token or an error if it fails. 120 // It returns new JWT token or an error if it fails.
115 func RefreshAPIToken(tokenString string) (string, error) { 121 func RefreshAPIToken(tokenString string) (string, error) {
116 var newToken string 122 var newToken string
117 tokenString = strings.TrimPrefix(tokenString, "Bearer ") 123 tokenString = strings.TrimPrefix(tokenString, "Bearer ")
118 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc) 124 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc)
119 if err != nil { 125 if err != nil {
120 return "", err 126 return "", err
121 } 127 }
122 128
123 // type assertion 129 // type assertion
124 claims, ok := token.Claims.(*TokenClaims) 130 claims, ok := token.Claims.(*TokenClaims)
125 if !ok || !token.Valid { 131 if !ok || !token.Valid {
126 return "", errors.New("token is not valid") 132 return "", errors.New("token is not valid")
127 } 133 }
128 134
129 claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix() 135 claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix()
130 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 136 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
131 137
132 newToken, err = jwtToken.SignedString([]byte(secret)) 138 newToken, err = jwtToken.SignedString([]byte(secret))
133 if err != nil { 139 if err != nil {
134 return "", err 140 return "", err
135 } 141 }
136 142
137 return newToken, nil 143 return newToken, nil
138 } 144 }
139 145
140 // ParseAPIToken parses JWT token claims. 146 // ParseAPIToken parses JWT token claims.
141 // It returns a pointer to TokenClaims struct or an error if it fails. 147 // It returns a pointer to TokenClaims struct or an error if it fails.
142 func ParseAPIToken(tokenString string) (*TokenClaims, error) { 148 func ParseAPIToken(tokenString string) (*TokenClaims, error) {
143 if ok := strings.HasPrefix(tokenString, "Bearer "); ok { 149 if ok := strings.HasPrefix(tokenString, "Bearer "); ok {
144 tokenString = strings.TrimPrefix(tokenString, "Bearer ") 150 tokenString = strings.TrimPrefix(tokenString, "Bearer ")
145 } else { 151 } else {
146 return &TokenClaims{}, errors.New("Authorization header is incomplete") 152 return &TokenClaims{}, errors.New("Authorization header is incomplete")
147 } 153 }
148 154
149 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc) 155 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc)
150 if err != nil { 156 if err != nil {
151 return &TokenClaims{}, err 157 return &TokenClaims{}, err
152 } 158 }
153 159
154 // type assertion 160 // type assertion
155 claims, ok := token.Claims.(*TokenClaims) 161 claims, ok := token.Claims.(*TokenClaims)
156 if !ok || !token.Valid { 162 if !ok || !token.Valid {
157 return &TokenClaims{}, errors.New("token is not valid") 163 return &TokenClaims{}, errors.New("token is not valid")
158 } 164 }
159 return claims, nil 165 return claims, nil
160 } 166 }
161 167
162 func GetTokenClaims(r *http.Request) (claims *TokenClaims, err error) { 168 func GetTokenClaims(r *http.Request) (claims *TokenClaims, err error) {
163 token := r.Header.Get("Authorization") 169 token := r.Header.Get("Authorization")
164 if ok := strings.HasPrefix(token, "Bearer "); ok { 170 if ok := strings.HasPrefix(token, "Bearer "); ok {
165 token = strings.TrimPrefix(token, "Bearer ") 171 token = strings.TrimPrefix(token, "Bearer ")
166 } else { 172 } else {
167 return &TokenClaims{}, errors.New("Authorization header is incomplete") 173 return &TokenClaims{}, errors.New("Authorization header is incomplete")
168 } 174 }
169 175
170 parsedToken, err := jwt.ParseWithClaims(token, &TokenClaims{}, secretFunc) 176 parsedToken, err := jwt.ParseWithClaims(token, &TokenClaims{}, secretFunc)
171 if err != nil { 177 if err != nil {
172 return &TokenClaims{}, err 178 return &TokenClaims{}, err
173 } 179 }
174 180
175 // type assertion 181 // type assertion
176 claims, ok := parsedToken.Claims.(*TokenClaims) 182 claims, ok := parsedToken.Claims.(*TokenClaims)
177 if !ok || !parsedToken.Valid { 183 if !ok || !parsedToken.Valid {
178 return &TokenClaims{}, errors.New("token is not valid") 184 return &TokenClaims{}, errors.New("token is not valid")
179 } 185 }
180 return claims, err 186 return claims, err
181 } 187 }
182 188
183 // secretFunc returns byte slice of API secret keyword. 189 // secretFunc returns byte slice of API secret keyword.
184 func secretFunc(token *jwt.Token) (interface{}, error) { 190 func secretFunc(token *jwt.Token) (interface{}, error) {
185 return []byte(secret), nil 191 return []byte(secret), nil
186 } 192 }
187 193
188 // roleAuthorized returns true if role from userClaims matches any of the authorizedRoles 194 // roleAuthorized returns true if role from userClaims matches any of the authorizedRoles
189 // or if authorizedRoles contains "*". 195 // or if authorizedRoles contains "*".
190 func roleAuthorized(authorizedRoles []string, userClaims *TokenClaims) bool { 196 func roleAuthorized(authorizedRoles []string, userClaims *TokenClaims) bool {
191 if userClaims == nil { 197 if userClaims == nil {
192 return false 198 return false
193 } 199 }
194 for _, r := range authorizedRoles { 200 for _, r := range authorizedRoles {
195 if userClaims.Role == r || r == "*" { 201 if userClaims.Role == r || r == "*" {
196 return true 202 return true
197 } 203 }
198 } 204 }
199 return false 205 return false
200 } 206 }
201 207
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 //"fmt" 4 //"fmt"
5 "net/http" 5 "net/http"
6 "encoding/json" 6 "encoding/json"
7 "errors" 7 "errors"
8 "io" 8 "io"
9 //"io/ioutil" 9 //"io/ioutil"
10 "sync" 10 "sync"
11 11
12 //"gopkg.in/rana/ora.v3" 12 //"gopkg.in/rana/ora.v3"
13 "gopkg.in/rana/ora.v4" 13 "gopkg.in/rana/ora.v4"
14 ) 14 )
15 15
16 var mu = &sync.Mutex{} 16 var mu = &sync.Mutex{}
17 var payloads []payloadBuff 17 var payloads []payloadBuff
18 18
19 type LangMap map[string]map[string]string 19 type LangMap map[string]map[string]string
20 20
21 type Field struct { 21 type Field struct {
22 Parameter string `json:"param"` 22 Parameter string `json:"param"`
23 Type string `json:"type"` 23 Type string `json:"type"`
24 Visible bool `json:"visible"` 24 Visible bool `json:"visible"`
25 Editable bool `json:"editable"` 25 Editable bool `json:"editable"`
26 } 26 }
27 27
28 type CorrelationField struct { 28 type CorrelationField struct {
29 Result string `json:"result"` 29 Result string `json:"result"`
30 Elements []string `json:"elements"` 30 Elements []string `json:"elements"`
31 Type string `json:"type"` 31 Type string `json:"type"`
32 } 32 }
33 33
34 type Translation struct { 34 type Translation struct {
35 Language string `json:"language"` 35 Language string `json:"language"`
36 FieldsLabels map[string]string `json:"fieldsLabels"` 36 FieldsLabels map[string]string `json:"fieldsLabels"`
37 } 37 }
38 38
39 type payloadBuff struct { 39 type payloadBuff struct {
40 Type string `json:"tableType"` 40 Type string `json:"tableType"`
41 Method string `json:"method"` 41 Method string `json:"method"`
42 Params map[string]string `json:"params"` 42 Params map[string]string `json:"params"`
43 Lang []Translation `json:"lang"` 43 Lang []Translation `json:"lang"`
44 Fields []Field `json:"fields"` 44 Fields []Field `json:"fields"`
45 Correlations []CorrelationField `json:"correlationFields"` 45 Correlations []CorrelationField `json:"correlationFields"`
46 IdField string `json:"idField"` 46 IdField string `json:"idField"`
47 47
48 // Data can only hold slices of any type. It can't be used for itteration 48 // Data can only hold slices of any type. It can't be used for itteration
49 Data interface{} `json:"data"` 49 Data interface{} `json:"data"`
50 } 50 }
51 51
52 type Payload struct { 52 type Payload struct {
53 Method string `json:"method"` 53 Method string `json:"method"`
54 Params map[string]string `json:"params"` 54 Params map[string]string `json:"params"`
55 Lang []Translation `json:"lang"` 55 Lang []Translation `json:"lang"`
56 Fields []Field `json:"fields"` 56 Fields []Field `json:"fields"`
57 Correlations []CorrelationField `json:"correlationFields"` 57 Correlations []CorrelationField `json:"correlationFields"`
58 IdField string `json:"idField"` 58 IdField string `json:"idField"`
59 59
60 // Data can only hold slices of any type. It can't be used for itteration 60 // Data can only hold slices of any type. It can't be used for itteration
61 Data interface{} `json:"data"` 61 Data interface{} `json:"data"`
62 } 62 }
63 63
64 // NewPayload returs a payload sceleton for provided table. 64 // NewPayload returs a payload sceleton for provided table.
65 func NewPayload(r *http.Request, table string) Payload { 65 func NewPayload(r *http.Request, table string) Payload {
66 var pload Payload 66 var pload Payload
67 67
68 pload.Method = r.Method + " " + r.URL.Path 68 pload.Method = r.Method + " " + r.URL.Path
69 if table != "" { 69 if table != "" {
70 pload.Params = make(map[string]string, 0) 70 pload.Params = make(map[string]string, 0)
71 pload.Lang = translations(table) 71 pload.Lang = translations(table)
72 pload.Fields = fields(table) 72 pload.Fields = fields(table)
73 pload.IdField = id(table) 73 pload.IdField = id(table)
74 pload.Correlations = correlations(table) 74 pload.Correlations = correlations(table)
75 } 75 }
76 return pload 76 return pload
77 } 77 }
78 78
79 // DeliverPayload encodes payload to w. 79 // DeliverPayload encodes payload to w.
80 func DeliverPayload(w http.ResponseWriter, payload Payload) { 80 func DeliverPayload(w http.ResponseWriter, payload Payload) {
81 json.NewEncoder(w).Encode(payload) 81 json.NewEncoder(w).Encode(payload)
82 payload.Data = nil 82 payload.Data = nil
83 } 83 }
84 84
85 // translations returns a slice of translations for a payload/table of ptype type. 85 // translations returns a slice of translations for a payload/table of ptype type.
86 func translations(ptype string) []Translation { 86 func translations(ptype string) []Translation {
87 var translations []Translation 87 var translations []Translation
88 88
89 for _, pload := range payloads { 89 for _, pload := range payloads {
90 if pload.Type == ptype { 90 if pload.Type == ptype {
91 for _, t := range pload.Lang { 91 for _, t := range pload.Lang {
92 translations = append(translations, Translation{ 92 translations = append(translations, Translation{
93 Language: t.Language, 93 Language: t.Language,
94 FieldsLabels: t.FieldsLabels, 94 FieldsLabels: t.FieldsLabels,
95 }) 95 })
96 } 96 }
97 } 97 }
98 } 98 }
99 99
100 return translations 100 return translations
101 } 101 }
102 102
103 // fields returns a slice of fields for a payload/table of ptype type. 103 // fields returns a slice of fields for a payload/table of ptype type.
104 func fields(ptype string) []Field { 104 func fields(ptype string) []Field {
105 var fields []Field 105 var fields []Field
106 106
107 for _, pload := range payloads { 107 for _, pload := range payloads {
108 if pload.Type == ptype { 108 if pload.Type == ptype {
109 for _, f := range pload.Fields { 109 for _, f := range pload.Fields {
110 fields = append(fields, f) 110 fields = append(fields, f)
111 } 111 }
112 } 112 }
113 } 113 }
114 114
115 return fields 115 return fields
116 } 116 }
117 117
118 // id returns the name of ID field of a payload/table of ptype type. 118 // id returns the name of ID field of a payload/table of ptype type.
119 func id(ptype string) string { 119 func id(ptype string) string {
120 for _, pload := range payloads { 120 for _, pload := range payloads {
121 if pload.Type == ptype { 121 if pload.Type == ptype {
122 return pload.IdField 122 return pload.IdField
123 } 123 }
124 } 124 }
125 return "" 125 return ""
126 } 126 }
127 127
128 // correlations returns a slice of correlation fields for a payload/table of ptype type. 128 // correlations returns a slice of correlation fields for a payload/table of ptype type.
129 func correlations(ptype string) []CorrelationField { 129 func correlations(ptype string) []CorrelationField {
130 var corr []CorrelationField 130 var corr []CorrelationField
131 131
132 for _, pload := range payloads { 132 for _, pload := range payloads {
133 if pload.Type == ptype { 133 if pload.Type == ptype {
134 for _, c := range pload.Correlations { 134 for _, c := range pload.Correlations {
135 corr = append(corr, c) 135 corr = append(corr, c)
136 } 136 }
137 } 137 }
138 } 138 }
139 139
140 return corr 140 return corr
141 } 141 }
142 142
143 // InitTables loads all payloads in the payloads variable. 143 // InitTables loads all payloads in the payloads variable.
144 // Returns an error if it fails. 144 // Returns an error if it fails.
145 func InitTables(db *ora.Ses, project string) error { 145 func InitTables(db *ora.Ses, project string) error {
146 jsonbuf, err := fetchJSON(db, project) 146 jsonbuf, err := fetchJSON(db, project)
147 if err != nil { 147 if err != nil {
148 return err 148 return err
149 } 149 }
150 150
151 mu.Lock() 151 mu.Lock()
152 defer mu.Unlock() 152 defer mu.Unlock()
153 json.Unmarshal(jsonbuf, &payloads) 153 json.Unmarshal(jsonbuf, &payloads)
154 if len(payloads) == 0 { 154 if len(payloads) == 0 {
155 return errors.New("tables config is corrupt") 155 return errors.New("tables config is corrupt")
156 } 156 }
157 return nil 157 return nil
158 } 158 }
159 159
160 // fetchJSON returns a byte slice of JSON configuration file from TABLES_CONFIG table. 160 // fetchJSON returns a byte slice of JSON configuration file from TABLES_CONFIG table.
161 // Returns an error if it fails. 161 // Returns an error if it fails.
162 func fetchJSON(db *ora.Ses, project string) ([]byte, error) { 162 func fetchJSON(db *ora.Ses, project string) ([]byte, error) {
163 db.SetCfg(db.Cfg().SetClob(ora.S)) 163 db.SetCfg(db.Cfg().SetClob(ora.S))
164 stmt, err := db.Prep(`SELECT JSON_CLOB FROM TABLES_CONFIG WHERE PROJEKAT` + EqualQuotes(project), ora.S) 164 stmt, err := db.Prep(`SELECT JSON_NCLOB FROM TABLES_CONFIG WHERE PROJEKAT` + EqualQuotes(project), ora.S)
165 defer stmt.Close() 165 defer stmt.Close()
166 if err != nil { 166 if err != nil {
167 return nil, err 167 return nil, err
168 } 168 }
169 169
170 rset, err := stmt.Qry() 170 rset, err := stmt.Qry()
171 if err != nil { 171 if err != nil {
172 return nil, err 172 return nil, err
173 } 173 }
174 174
175 var data string 175 var data string
176 if rset.Next() { 176 if rset.Next() {
177 data = rset.Row[0].(string) 177 data = rset.Row[0].(string)
178 } 178 }
179 179
180 //fmt.Println(data) 180 //fmt.Println(data)
181 return []byte(data), nil 181 return []byte(data), nil
182 } 182 }
183 183
184 // DecodeJSON decodes JSON data from r to v. 184 // DecodeJSON decodes JSON data from r to v.
185 // Returns an error if it fails. 185 // Returns an error if it fails.
186 func DecodeJSON(r io.Reader, v interface{}) error { 186 func DecodeJSON(r io.Reader, v interface{}) error {
187 return json.NewDecoder(r).Decode(v) 187 return json.NewDecoder(r).Decode(v)
188 } 188 }
189 189