Commit 4b3627bba5221a97582397b8b4ccfb0edd51d2d1
1 parent
67337ffa8f
Exists in
master
and in
1 other branch
added role ID to the credentials struct
Showing
2 changed files
with
10 additions
and
6 deletions
Show diff stats
auth_utility.go
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 |
json_utility.go
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 | } |