Commit 4b3627bba5221a97582397b8b4ccfb0edd51d2d1

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

added role ID to the credentials struct

Showing 2 changed files with 10 additions and 6 deletions   Show diff stats
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "crypto/rand" 4 "crypto/rand"
5 "crypto/sha256" 5 "crypto/sha256"
6 "encoding/hex" 6 "encoding/hex"
7 "errors" 7 "errors"
8 "net/http" 8 "net/http"
9 "strings" 9 "strings"
10 "time" 10 "time"
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 type Role struct { 21 type Role struct {
22 Name string `json:"name"` 22 Name string `json:"name"`
23 ID uint32 `json:"id"` 23 ID uint32 `json:"id"`
24 } 24 }
25 25
26 // TokenClaims are JWT token claims. 26 // TokenClaims are JWT token claims.
27 type TokenClaims struct { 27 type TokenClaims struct {
28 Token string `json:"access_token"` 28 Token string `json:"access_token"`
29 TokenType string `json:"token_type"` 29 TokenType string `json:"token_type"`
30 Username string `json:"username"` 30 Username string `json:"username"`
31 Role string `json:"role"` 31 Role string `json:"role"`
32 RoleID uint32 `json:"role_id"` 32 RoleID uint32 `json:"role_id"`
33 ExpiresIn int64 `json:"expires_in"` 33 ExpiresIn int64 `json:"expires_in"`
34 34
35 // extending a struct 35 // extending a struct
36 jwt.StandardClaims 36 jwt.StandardClaims
37 } 37 }
38 38
39 // CredentialsStruct is an instace of username/password values. 39 // CredentialsStruct is an instace of username/password values.
40 type CredentialsStruct struct { 40 type CredentialsStruct struct {
41 Username string `json:"username"` 41 Username string `json:"username"`
42 Password string `json:"password"` 42 Password string `json:"password"`
43 RoleID uint32 `json:"roleID"`
43 } 44 }
44 45
45 // ValidateCredentials hashes pass and salt and returns comparison result with resultHash 46 // ValidateCredentials hashes pass and salt and returns comparison result with resultHash
46 func ValidateCredentials(pass, salt, resultHash string) bool { 47 func ValidateCredentials(pass, salt, resultHash string) bool {
47 hash, _, err := CreateHash(pass, salt) 48 hash, _, err := CreateHash(pass, salt)
48 if err != nil { 49 if err != nil {
49 return false 50 return false
50 } 51 }
51 return hash == resultHash 52 return hash == resultHash
52 } 53 }
53 54
54 // CreateHash hashes str using SHA256. 55 // CreateHash hashes str using SHA256.
55 // If the presalt parameter is not provided CreateHash will generate new salt string. 56 // If the presalt parameter is not provided CreateHash will generate new salt string.
56 // Returns hash and salt strings or an error if it fails. 57 // Returns hash and salt strings or an error if it fails.
57 func CreateHash(str, presalt string) (hash, salt string, err error) { 58 func CreateHash(str, presalt string) (hash, salt string, err error) {
58 // chech if message is presalted 59 // chech if message is presalted
59 if presalt == "" { 60 if presalt == "" {
60 salt, err = randomSalt() 61 salt, err = randomSalt()
61 if err != nil { 62 if err != nil {
62 return "", "", err 63 return "", "", err
63 } 64 }
64 } else { 65 } else {
65 salt = presalt 66 salt = presalt
66 } 67 }
67 68
68 // convert strings to raw byte slices 69 // convert strings to raw byte slices
69 rawstr := []byte(str) 70 rawstr := []byte(str)
70 rawsalt, err := hex.DecodeString(salt) 71 rawsalt, err := hex.DecodeString(salt)
71 if err != nil { 72 if err != nil {
72 return "", "", err 73 return "", "", err
73 } 74 }
74 75
75 rawdata := make([]byte, len(rawstr)+len(rawsalt)) 76 rawdata := make([]byte, len(rawstr)+len(rawsalt))
76 rawdata = append(rawdata, rawstr...) 77 rawdata = append(rawdata, rawstr...)
77 rawdata = append(rawdata, rawsalt...) 78 rawdata = append(rawdata, rawsalt...)
78 79
79 // hash message + salt 80 // hash message + salt
80 hasher := sha256.New() 81 hasher := sha256.New()
81 hasher.Write(rawdata) 82 hasher.Write(rawdata)
82 rawhash := hasher.Sum(nil) 83 rawhash := hasher.Sum(nil)
83 84
84 hash = hex.EncodeToString(rawhash) 85 hash = hex.EncodeToString(rawhash)
85 return hash, salt, nil 86 return hash, salt, nil
86 } 87 }
87 88
88 // CreateAuthToken returns JWT token with encoded username, role, expiration date and issuer claims. 89 // CreateAuthToken returns JWT token with encoded username, role, expiration date and issuer claims.
89 // It returns an error if it fails. 90 // It returns an error if it fails.
90 func CreateAuthToken(username string, role Role) (TokenClaims, error) { 91 func CreateAuthToken(username string, role Role) (TokenClaims, error) {
91 t0 := (time.Now()).Unix() 92 t0 := (time.Now()).Unix()
92 t1 := (time.Now().Add(OneWeek)).Unix() 93 t1 := (time.Now().Add(OneWeek)).Unix()
93 claims := TokenClaims{ 94 claims := TokenClaims{
94 TokenType: "Bearer", 95 TokenType: "Bearer",
95 Username: username, 96 Username: username,
96 Role: role.Name, 97 Role: role.Name,
97 RoleID: role.ID, 98 RoleID: role.ID,
98 ExpiresIn: t1 - t0, 99 ExpiresIn: t1 - t0,
99 } 100 }
100 // initialize jwt.StandardClaims fields (anonymous struct) 101 // initialize jwt.StandardClaims fields (anonymous struct)
101 claims.IssuedAt = t0 102 claims.IssuedAt = t0
102 claims.ExpiresAt = t1 103 claims.ExpiresAt = t1
103 claims.Issuer = appName 104 claims.Issuer = appName
104 105
105 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 106 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
106 token, err := jwtToken.SignedString([]byte(secret)) 107 token, err := jwtToken.SignedString([]byte(secret))
107 if err != nil { 108 if err != nil {
108 return TokenClaims{}, err 109 return TokenClaims{}, err
109 } 110 }
110 claims.Token = token 111 claims.Token = token
111 return claims, nil 112 return claims, nil
112 } 113 }
113 114
114 // RefreshAuthToken prolongs JWT token's expiration date for one week. 115 // RefreshAuthToken prolongs JWT token's expiration date for one week.
115 // It returns new JWT token or an error if it fails. 116 // It returns new JWT token or an error if it fails.
116 func RefreshAuthToken(req *http.Request) (TokenClaims, error) { 117 func RefreshAuthToken(req *http.Request) (TokenClaims, error) {
117 authHead := req.Header.Get("Authorization") 118 authHead := req.Header.Get("Authorization")
118 tokenstr := strings.TrimPrefix(authHead, "Bearer ") 119 tokenstr := strings.TrimPrefix(authHead, "Bearer ")
119 token, err := jwt.ParseWithClaims(tokenstr, &TokenClaims{}, secretFunc) 120 token, err := jwt.ParseWithClaims(tokenstr, &TokenClaims{}, secretFunc)
120 if err != nil { 121 if err != nil {
121 return TokenClaims{}, err 122 return TokenClaims{}, err
122 } 123 }
123 124
124 // type assertion 125 // type assertion
125 claims, ok := token.Claims.(*TokenClaims) 126 claims, ok := token.Claims.(*TokenClaims)
126 if !ok || !token.Valid { 127 if !ok || !token.Valid {
127 return TokenClaims{}, errors.New("token is not valid") 128 return TokenClaims{}, errors.New("token is not valid")
128 } 129 }
129 130
130 // extend token expiration date 131 // extend token expiration date
131 return CreateAuthToken(claims.Username, Role{claims.Role, claims.RoleID}) 132 return CreateAuthToken(claims.Username, Role{claims.Role, claims.RoleID})
132 } 133 }
133 134
134 // RbacCheck returns true if role that made HTTP request is authorized to 135 // RbacCheck returns true if role that made HTTP request is authorized to
135 // access the resource it is targeting. 136 // access the resource it is targeting.
136 // It exctracts user's role from the JWT token located in Authorization header of 137 // It exctracts user's role from the JWT token located in Authorization header of
137 // http.Request and then compares it with the list of supplied roles and returns 138 // http.Request and then compares it with the list of supplied roles and returns
138 // true if there's a match, if "*" is provided or if the authRoles is nil. 139 // true if there's a match, if "*" is provided or if the authRoles is nil.
139 // Otherwise it returns false. 140 // Otherwise it returns false.
140 func RbacCheck(req *http.Request, authRoles []string) bool { 141 func RbacCheck(req *http.Request, authRoles []string) bool {
141 if authRoles == nil { 142 if authRoles == nil {
142 return true 143 return true
143 } 144 }
144 145
145 // validate token and check expiration date 146 // validate token and check expiration date
146 claims, err := GetTokenClaims(req) 147 claims, err := GetTokenClaims(req)
147 if err != nil { 148 if err != nil {
148 return false 149 return false
149 } 150 }
150 // check if token has expired 151 // check if token has expired
151 if claims.ExpiresAt < (time.Now()).Unix() { 152 if claims.ExpiresAt < (time.Now()).Unix() {
152 return false 153 return false
153 } 154 }
154 155
155 // check if role extracted from token matches 156 // check if role extracted from token matches
156 // any of the provided (allowed) ones 157 // any of the provided (allowed) ones
157 for _, r := range authRoles { 158 for _, r := range authRoles {
158 if claims.Role == r || r == "*" { 159 if claims.Role == r || r == "*" {
159 return true 160 return true
160 } 161 }
161 } 162 }
162 163
163 return false 164 return false
164 } 165 }
165 166
166 // TODO 167 // TODO
167 func AuthCheck(req *http.Request, authRoles []string) (*TokenClaims, error) { 168 func AuthCheck(req *http.Request, authRoles []string) (*TokenClaims, error) {
168 if authRoles == nil { 169 if authRoles == nil {
169 return &TokenClaims{}, nil 170 return &TokenClaims{}, nil
170 } 171 }
171 172
172 // validate token and check expiration date 173 // validate token and check expiration date
173 claims, err := GetTokenClaims(req) 174 claims, err := GetTokenClaims(req)
174 if err != nil { 175 if err != nil {
175 return &TokenClaims{}, err 176 return &TokenClaims{}, err
176 } 177 }
177 // check if token has expired 178 // check if token has expired
178 if claims.ExpiresAt < (time.Now()).Unix() { 179 if claims.ExpiresAt < (time.Now()).Unix() {
179 return &TokenClaims{}, errors.New("token has expired") 180 return &TokenClaims{}, errors.New("token has expired")
180 } 181 }
181 182
182 // check if role extracted from token matches 183 // check if role extracted from token matches
183 // any of the provided (allowed) ones 184 // any of the provided (allowed) ones
184 for _, r := range authRoles { 185 for _, r := range authRoles {
185 if claims.Role == r || r == "*" { 186 if claims.Role == r || r == "*" {
186 return claims, nil 187 return claims, nil
187 } 188 }
188 } 189 }
189 190
190 return claims, errors.New("role is not authorized") 191 return claims, errors.New("role is not authorized")
191 } 192 }
192 193
193 // GetTokenClaims extracts JWT claims from Authorization header of the request. 194 // GetTokenClaims extracts JWT claims from Authorization header of the request.
194 // Returns token claims or an error. 195 // Returns token claims or an error.
195 func GetTokenClaims(req *http.Request) (*TokenClaims, error) { 196 func GetTokenClaims(req *http.Request) (*TokenClaims, error) {
196 // check for and strip 'Bearer' prefix 197 // check for and strip 'Bearer' prefix
197 var tokstr string 198 var tokstr string
198 authHead := req.Header.Get("Authorization") 199 authHead := req.Header.Get("Authorization")
199 if ok := strings.HasPrefix(authHead, "Bearer "); ok { 200 if ok := strings.HasPrefix(authHead, "Bearer "); ok {
200 tokstr = strings.TrimPrefix(authHead, "Bearer ") 201 tokstr = strings.TrimPrefix(authHead, "Bearer ")
201 } else { 202 } else {
202 return &TokenClaims{}, errors.New("authorization header in incomplete") 203 return &TokenClaims{}, errors.New("authorization header in incomplete")
203 } 204 }
204 205
205 token, err := jwt.ParseWithClaims(tokstr, &TokenClaims{}, secretFunc) 206 token, err := jwt.ParseWithClaims(tokstr, &TokenClaims{}, secretFunc)
206 if err != nil { 207 if err != nil {
207 return &TokenClaims{}, err 208 return &TokenClaims{}, err
208 } 209 }
209 210
210 // type assertion 211 // type assertion
211 claims, ok := token.Claims.(*TokenClaims) 212 claims, ok := token.Claims.(*TokenClaims)
212 if !ok || !token.Valid { 213 if !ok || !token.Valid {
213 return &TokenClaims{}, errors.New("token is not valid") 214 return &TokenClaims{}, errors.New("token is not valid")
214 } 215 }
215 216
216 return claims, nil 217 return claims, nil
217 } 218 }
218 219
219 // randomSalt returns a string of random characters of 'saltSize' length. 220 // randomSalt returns a string of random characters of 'saltSize' length.
220 func randomSalt() (s string, err error) { 221 func randomSalt() (s string, err error) {
221 rawsalt := make([]byte, saltSize) 222 rawsalt := make([]byte, saltSize)
222 223
223 _, err = rand.Read(rawsalt) 224 _, err = rand.Read(rawsalt)
224 if err != nil { 225 if err != nil {
225 return "", err 226 return "", err
226 } 227 }
227 228
228 s = hex.EncodeToString(rawsalt) 229 s = hex.EncodeToString(rawsalt)
229 return s, nil 230 return s, nil
230 } 231 }
231 232
232 // secretFunc returns byte slice of API secret keyword. 233 // secretFunc returns byte slice of API secret keyword.
233 func secretFunc(token *jwt.Token) (interface{}, error) { 234 func secretFunc(token *jwt.Token) (interface{}, error) {
234 return []byte(secret), nil 235 return []byte(secret), nil
235 } 236 }
236 237
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "encoding/json" 4 "encoding/json"
5 "errors" 5 "errors"
6 "fmt" 6 "fmt"
7 "io" 7 "io"
8 "net/http" 8 "net/http"
9 "os" 9 "os"
10 "sync" 10 "sync"
11 "time" 11 "time"
12 12
13 "gopkg.in/rana/ora.v4" 13 "gopkg.in/rana/ora.v4"
14 ) 14 )
15 15
16 var ( 16 var (
17 mu = &sync.Mutex{} 17 mu = &sync.Mutex{}
18 metadata = make(map[string]Payload) 18 metadata = make(map[string]Payload)
19 updateQue = make(map[string][]byte) 19 updateQue = make(map[string][]byte)
20 20
21 metadataDB *ora.Ses 21 metadataDB *ora.Ses
22 activeProject string 22 activeProject string
23 23
24 inited bool 24 inited bool
25 ) 25 )
26 26
27 type LangMap map[string]map[string]string 27 type LangMap map[string]map[string]string
28 28
29 type Field struct { 29 type Field struct {
30 Parameter string `json:"param"` 30 Parameter string `json:"param"`
31 Type string `json:"type"` 31 Type string `json:"type"`
32 Visible bool `json:"visible"` 32 Visible bool `json:"visible"`
33 Editable bool `json:"editable"` 33 Editable bool `json:"editable"`
34 } 34 }
35 35
36 type CorrelationField struct { 36 type CorrelationField struct {
37 Result string `json:"result"` 37 Result string `json:"result"`
38 Elements []string `json:"elements"` 38 Elements []string `json:"elements"`
39 Type string `json:"type"` 39 Type string `json:"type"`
40 } 40 }
41 41
42 type Translation struct { 42 type Translation struct {
43 Language string `json:"language"` 43 Language string `json:"language"`
44 FieldsLabels map[string]string `json:"fieldsLabels"` 44 FieldsLabels map[string]string `json:"fieldsLabels"`
45 } 45 }
46 46
47 type Payload struct { 47 type Payload struct {
48 Method string `json:"method"` 48 Method string `json:"method"`
49 Params map[string]string `json:"params"` 49 Params map[string]string `json:"params"`
50 Lang []Translation `json:"lang"` 50 Lang []Translation `json:"lang"`
51 Fields []Field `json:"fields"` 51 Fields []Field `json:"fields"`
52 Correlations []CorrelationField `json:"correlationFields"` 52 Correlations []CorrelationField `json:"correlationFields"`
53 IdField string `json:"idField"` 53 IdField string `json:"idField"`
54 54
55 // Data holds JSON payload. It can't be used for itteration. 55 // Data holds JSON payload. It can't be used for itteration.
56 Data interface{} `json:"data"` 56 Data interface{} `json:"data"`
57 } 57 }
58 58
59 // LoadPayloadsdetaData loads all payloads' information into 'metadata' variable. 59 // LoadPayloadsdetaData loads all payloads' information into 'metadata' variable.
60 func LoadPayloadsMetadata(db *ora.Ses, project string, hotloading bool, hlPeriod int) error { 60 func LoadPayloadsMetadata(db *ora.Ses, project string, hotloading bool, hlPeriod int) error {
61 metadataDB = db 61 metadataDB = db
62 activeProject = project 62 activeProject = project
63 63
64 mu.Lock() 64 mu.Lock()
65 defer mu.Unlock() 65 defer mu.Unlock()
66 err := initMetadata(project) 66 err := initMetadata(project)
67 if err != nil { 67 if err != nil {
68 return err 68 return err
69 } 69 }
70 if hotloading { 70 if hotloading {
71 go hotload(hlPeriod) 71 go hotload(hlPeriod)
72 } 72 }
73 inited = true 73 inited = true
74 74
75 return nil 75 return nil
76 } 76 }
77 77
78 func GetMetadataForAllEntities() map[string]Payload { 78 func GetMetadataForAllEntities() map[string]Payload {
79 return metadata 79 return metadata
80 } 80 }
81 81
82 func GetMetadataForEntity(t string) (Payload, bool) { 82 func GetMetadataForEntity(t string) (Payload, bool) {
83 p, ok := metadata[t] 83 p, ok := metadata[t]
84 return p, ok 84 return p, ok
85 } 85 }
86 86
87 func QueEntityModelUpdate(entityType string, v interface{}) { 87 func QueEntityModelUpdate(entityType string, v interface{}) {
88 updateQue[entityType], _ = json.Marshal(v) 88 updateQue[entityType], _ = json.Marshal(v)
89 } 89 }
90 90
91 func UpdateEntityModels(forceUpdate bool) (total, upd, add int, err error) { 91 func UpdateEntityModels(command string) (total, upd, add int, err error) {
92 if command != "force" && command != "missing" {
93 return total, 0, 0, errors.New("uknown command: " + command)
94 }
95
92 if !inited { 96 if !inited {
93 return 0, 0, 0, errors.New("webutil: metadata not initialized but update was tried.") 97 return 0, 0, 0, errors.New("webutil: metadata not initialized but update was tried.")
94 } 98 }
95 99
96 total = len(updateQue) 100 total = len(updateQue)
97 101
98 forUpdate := make([]string, 0) 102 forUpdate := make([]string, 0)
99 forAdd := make([]string, 0) 103 forAdd := make([]string, 0)
100 104
101 for k, _ := range updateQue { 105 for k, _ := range updateQue {
102 if _, exists := metadata[k]; exists { 106 if _, exists := metadata[k]; exists {
103 if forceUpdate { 107 if command == "force" {
104 forUpdate = append(forUpdate, k) 108 forUpdate = append(forUpdate, k)
105 } 109 }
106 } else { 110 } else {
107 forAdd = append(forAdd, k) 111 forAdd = append(forAdd, k)
108 } 112 }
109 } 113 }
110 114
111 for _, k := range forUpdate { 115 for _, k := range forUpdate {
112 fmt.Printf("for update: %s\n", k)
113
114 _, err := metadataDB.PrepAndExe(`update entities set 116 _, err := metadataDB.PrepAndExe(`update entities set
115 entity_model = :1 117 entity_model = :1
116 where entity_type = :2`, 118 where entity_type = :2`,
117 string(updateQue[k]), 119 string(updateQue[k]),
118 k) 120 k)
119 121
120 if err != nil { 122 if err != nil {
121 fmt.Printf("webutility: update metadata: prep and exe: %v\n", err) 123 fmt.Printf("webutility: update metadata: prep and exe: %v\n", err)
122 continue 124 continue
123 } 125 }
124 upd++ 126 upd++
127 fmt.Printf("webutility: updated %s payload model\n", k)
125 } 128 }
126 129
127 blankPayload, _ := json.Marshal(Payload{}) 130 blankPayload, _ := json.Marshal(Payload{})
128 for _, k := range forAdd { 131 for _, k := range forAdd {
129 fmt.Printf("for add: %s\n", k)
130
131 _, err := metadataDB.PrepAndExe(`insert into entities 132 _, err := metadataDB.PrepAndExe(`insert into entities
132 (projekat, metadata, entity_type, entity_model) 133 (projekat, metadata, entity_type, entity_model)
133 values(:1, :2, :3, :4)`, 134 values(:1, :2, :3, :4)`,
134 activeProject, 135 activeProject,
135 string(blankPayload), 136 string(blankPayload),
136 k, 137 k,
137 string(updateQue[k])) 138 string(updateQue[k]))
138 139
139 if err != nil { 140 if err != nil {
140 fmt.Printf("webutility: add metadata: prep and exe: %v\n", err) 141 fmt.Printf("webutility: add metadata: prep and exe: %v\n", err)
141 continue 142 continue
142 } 143 }
143 metadata[k] = Payload{} 144 metadata[k] = Payload{}
144 add++ 145 add++
146 fmt.Printf("webutility: added %s to the payload models\n", k)
147
145 } 148 }
146 149
147 return total, upd, add, nil 150 return total, upd, add, nil
148 } 151 }
149 152
150 func ModifyMetadataForEntity(entityType string, p *Payload) error { 153 func ModifyMetadataForEntity(entityType string, p *Payload) error {
151 md, err := json.Marshal(*p) 154 md, err := json.Marshal(*p)
152 if err != nil { 155 if err != nil {
153 return err 156 return err
154 } 157 }
155 158
156 mu.Lock() 159 mu.Lock()
157 defer mu.Unlock() 160 defer mu.Unlock()
158 _, err = metadataDB.PrepAndExe(`update entities set 161 _, err = metadataDB.PrepAndExe(`update entities set
159 metadata = :1 162 metadata = :1
160 where projekat = :2 163 where projekat = :2
161 and entity_type = :3`, 164 and entity_type = :3`,
162 string(md), 165 string(md),
163 activeProject, 166 activeProject,
164 entityType) 167 entityType)
165 if err != nil { 168 if err != nil {
166 return err 169 return err
167 } 170 }
168 return nil 171 return nil
169 } 172 }
170 173
171 func DeleteEntityModel(entityType string) error { 174 func DeleteEntityModel(entityType string) error {
172 _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType) 175 _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType)
173 if err == nil { 176 if err == nil {
174 mu.Lock() 177 mu.Lock()
175 delete(metadata, entityType) 178 delete(metadata, entityType)
176 mu.Unlock() 179 mu.Unlock()
177 } 180 }
178 return err 181 return err
179 } 182 }
180 183
181 // NewPayload returs a payload sceleton for entity described with etype. 184 // NewPayload returs a payload sceleton for entity described with etype.
182 func NewPayload(r *http.Request, etype string) Payload { 185 func NewPayload(r *http.Request, etype string) Payload {
183 pload := metadata[etype] 186 pload := metadata[etype]
184 pload.Method = r.Method + " " + r.RequestURI 187 pload.Method = r.Method + " " + r.RequestURI
185 return pload 188 return pload
186 } 189 }
187 190
188 // DecodeJSON decodes JSON data from r to v. 191 // DecodeJSON decodes JSON data from r to v.
189 // Returns an error if it fails. 192 // Returns an error if it fails.
190 func DecodeJSON(r io.Reader, v interface{}) error { 193 func DecodeJSON(r io.Reader, v interface{}) error {
191 return json.NewDecoder(r).Decode(v) 194 return json.NewDecoder(r).Decode(v)
192 } 195 }
193 196
194 func initMetadata(project string) error { 197 func initMetadata(project string) error {
195 metadataDB.SetCfg(metadataDB.Cfg().SetClob(ora.S)) 198 metadataDB.SetCfg(metadataDB.Cfg().SetClob(ora.S))
196 stmt, err := metadataDB.Prep(`select 199 stmt, err := metadataDB.Prep(`select
197 entity_type, 200 entity_type,
198 metadata 201 metadata
199 from entities 202 from entities
200 where projekat = `+fmt.Sprintf("'%s'", project), 203 where projekat = `+fmt.Sprintf("'%s'", project),
201 ora.S, 204 ora.S,
202 ora.S) 205 ora.S)
203 206
204 defer stmt.Close() 207 defer stmt.Close()
205 if err != nil { 208 if err != nil {
206 return err 209 return err
207 } 210 }
208 211
209 rset, err := stmt.Qry() 212 rset, err := stmt.Qry()
210 if err != nil { 213 if err != nil {
211 return err 214 return err
212 } 215 }
213 216
214 count := 0 217 count := 0
215 success := 0 218 success := 0
216 if len(metadata) > 0 { 219 if len(metadata) > 0 {
217 metadata = nil 220 metadata = nil
218 } 221 }
219 metadata = make(map[string]Payload) 222 metadata = make(map[string]Payload)
220 for rset.Next() { 223 for rset.Next() {
221 name := rset.Row[0].(string) 224 name := rset.Row[0].(string)
222 load := []byte(rset.Row[1].(string)) 225 load := []byte(rset.Row[1].(string))
223 226
224 p := Payload{} 227 p := Payload{}
225 err := json.Unmarshal(load, &p) 228 err := json.Unmarshal(load, &p)
226 if err != nil { 229 if err != nil {
227 fmt.Printf("couldn't init: '%s' metadata\n", name) 230 fmt.Printf("couldn't init: '%s' metadata\n", name)
228 } else { 231 } else {
229 success++ 232 success++
230 metadata[name] = p 233 metadata[name] = p
231 } 234 }
232 count++ 235 count++
233 } 236 }
234 fmt.Printf("webutility: successfully loaded %d/%d (%.1f%%) entities\n", 237 fmt.Printf("webutility: successfully loaded %d/%d (%.1f%%) entities\n",
235 success, count, float32(success)/float32(count)*100.0) 238 success, count, float32(success)/float32(count)*100.0)
236 239
237 return nil 240 return nil
238 } 241 }
239 242
240 func hotload(n int) { 243 func hotload(n int) {
241 entityScan := make(map[string]int64) 244 entityScan := make(map[string]int64)
242 firstCheck := true 245 firstCheck := true
243 for { 246 for {
244 time.Sleep(time.Duration(n) * time.Second) 247 time.Sleep(time.Duration(n) * time.Second)
245 stmt, err := metadataDB.Prep(`select 248 stmt, err := metadataDB.Prep(`select
246 ora_rowscn, 249 ora_rowscn,
247 entity_type 250 entity_type
248 from entities where projekat = `+fmt.Sprintf("'%s'", activeProject), 251 from entities where projekat = `+fmt.Sprintf("'%s'", activeProject),
249 ora.I64, 252 ora.I64,
250 ora.S) 253 ora.S)
251 if err != nil { 254 if err != nil {
252 fmt.Fprintf(os.Stderr, "hotload failed: %v\n", err) 255 fmt.Fprintf(os.Stderr, "hotload failed: %v\n", err)
253 time.Sleep(time.Duration(n) * time.Second) 256 time.Sleep(time.Duration(n) * time.Second)
254 continue 257 continue
255 } 258 }
256 259
257 rset, err := stmt.Qry() 260 rset, err := stmt.Qry()
258 if err != nil { 261 if err != nil {
259 fmt.Fprintf(os.Stderr, "hotload failed: %v\n", err) 262 fmt.Fprintf(os.Stderr, "hotload failed: %v\n", err)
260 time.Sleep(time.Duration(n) * time.Second) 263 time.Sleep(time.Duration(n) * time.Second)
261 continue 264 continue
262 } 265 }
263 266
264 var toRefresh []string 267 var toRefresh []string
265 for rset.Next() { 268 for rset.Next() {
266 scanID := rset.Row[0].(int64) 269 scanID := rset.Row[0].(int64)
267 entity := rset.Row[1].(string) 270 entity := rset.Row[1].(string)
268 oldID, ok := entityScan[entity] 271 oldID, ok := entityScan[entity]
269 if !ok || oldID != scanID { 272 if !ok || oldID != scanID {
270 entityScan[entity] = scanID 273 entityScan[entity] = scanID
271 toRefresh = append(toRefresh, entity) 274 toRefresh = append(toRefresh, entity)
272 } 275 }
273 } 276 }
274 stmt.Close() 277 stmt.Close()
275 278
276 if rset.Err() != nil { 279 if rset.Err() != nil {
277 fmt.Fprintf(os.Stderr, "hotload rset error: %v\n", rset.Err()) 280 fmt.Fprintf(os.Stderr, "hotload rset error: %v\n", rset.Err())
278 time.Sleep(time.Duration(n) * time.Second) 281 time.Sleep(time.Duration(n) * time.Second)
279 continue 282 continue
280 } 283 }
281 284
282 if len(toRefresh) > 0 && !firstCheck { 285 if len(toRefresh) > 0 && !firstCheck {
283 mu.Lock() 286 mu.Lock()
284 refreshMetadata(toRefresh) 287 refreshMetadata(toRefresh)
285 mu.Unlock() 288 mu.Unlock()
286 } 289 }
287 if firstCheck { 290 if firstCheck {
288 firstCheck = false 291 firstCheck = false
289 } 292 }
290 } 293 }
291 } 294 }
292 295
293 func refreshMetadata(entities []string) { 296 func refreshMetadata(entities []string) {
294 for _, e := range entities { 297 for _, e := range entities {
295 fmt.Printf("refreshing %s\n", e) 298 fmt.Printf("refreshing %s\n", e)
296 stmt, err := metadataDB.Prep(`select 299 stmt, err := metadataDB.Prep(`select
297 metadata 300 metadata
298 from entities 301 from entities
299 where projekat = `+fmt.Sprintf("'%s'", activeProject)+ 302 where projekat = `+fmt.Sprintf("'%s'", activeProject)+
300 ` and entity_type = `+fmt.Sprintf("'%s'", e), 303 ` and entity_type = `+fmt.Sprintf("'%s'", e),
301 ora.S) 304 ora.S)
302 305
303 if err != nil { 306 if err != nil {
304 fmt.Printf("webutility: refresh: prep: %v\n", err) 307 fmt.Printf("webutility: refresh: prep: %v\n", err)
305 stmt.Close() 308 stmt.Close()
306 continue 309 continue
307 } 310 }
308 311
309 rset, err := stmt.Qry() 312 rset, err := stmt.Qry()
310 if err != nil { 313 if err != nil {
311 fmt.Printf("webutility: refresh: query: %v\n", err) 314 fmt.Printf("webutility: refresh: query: %v\n", err)
312 stmt.Close() 315 stmt.Close()
313 continue 316 continue
314 } 317 }
315 318
316 for rset.Next() { 319 for rset.Next() {
317 load := []byte(rset.Row[0].(string)) 320 load := []byte(rset.Row[0].(string))
318 p := Payload{} 321 p := Payload{}
319 err := json.Unmarshal(load, &p) 322 err := json.Unmarshal(load, &p)
320 if err != nil { 323 if err != nil {
321 fmt.Printf("couldn't refresh: '%s' metadata\n", e) 324 fmt.Printf("couldn't refresh: '%s' metadata\n", e)
322 } else { 325 } else {
323 metadata[e] = p 326 metadata[e] = p
324 } 327 }
325 } 328 }