Commit 33d137a67174c4877300a83d5741f58c3f160c65

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

Functional role checking for API endpoints.

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"
11
10 "github.com/dgrijalva/jwt-go" 12 "github.com/dgrijalva/jwt-go"
13 "fmt"
11 ) 14 )
12 15
13 const OneDay = time.Hour*24 16 const OneDay = time.Hour*24
14 const OneWeek = OneDay*7 17 const OneWeek = OneDay*7
15 const saltSize = 32 18 const saltSize = 32
16 const appName = "korisnicki-centar" 19 const appName = "korisnicki-centar"
17 const secret = "korisnicki-centar-api" 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 // TokenClaims are JWT token claims. 27 // TokenClaims are JWT token claims.
20 type TokenClaims struct { 28 type TokenClaims struct {
21 Username string `json:"username"` 29 Username string `json:"username"`
22 Role string `json:"role"` 30 Role string `json:"role"`
23 jwt.StandardClaims 31 jwt.StandardClaims
24 } 32 }
25 33
26 // CredentialsStruct is an instace of username/password values. 34 // CredentialsStruct is an instace of username/password values.
27 type CredentialsStruct struct { 35 type CredentialsStruct struct {
28 Username string `json:"username"` 36 Username string `json:"username"`
29 Password string `json:"password"` 37 Password string `json:"password"`
30 } 38 }
31 39
32 // generateSalt returns a string of random characters of 'saltSize' length. 40 // generateSalt returns a string of random characters of 'saltSize' length.
33 func generateSalt() (salt string, err error) { 41 func generateSalt() (salt string, err error) {
34 rawsalt := make([]byte, saltSize) 42 rawsalt := make([]byte, saltSize)
35 43
36 _, err = rand.Read(rawsalt) 44 _, err = rand.Read(rawsalt)
37 if err != nil { 45 if err != nil {
38 return "", err 46 return "", err
39 } 47 }
40 48
41 salt = hex.EncodeToString(rawsalt) 49 salt = hex.EncodeToString(rawsalt)
42 return salt, nil 50 return salt, nil
43 } 51 }
44 52
45 // HashString hashes input string with SHA256 algorithm. 53 // HashString hashes input string with SHA256 algorithm.
46 // If the presalt parameter is not provided HashString will generate new salt string. 54 // If the presalt parameter is not provided HashString will generate new salt string.
47 // Returns hash and salt string or an error if it fails. 55 // Returns hash and salt string or an error if it fails.
48 func HashString(str string, presalt string) (hash, salt string, err error) { 56 func HashString(str string, presalt string) (hash, salt string, err error) {
49 // chech if message is presalted 57 // chech if message is presalted
50 if presalt == "" { 58 if presalt == "" {
51 salt, err = generateSalt() 59 salt, err = generateSalt()
52 if err != nil { 60 if err != nil {
53 return "", "", err 61 return "", "", err
54 } 62 }
55 } else { 63 } else {
56 salt = presalt 64 salt = presalt
57 } 65 }
58 66
59 // convert strings to raw byte slices 67 // convert strings to raw byte slices
60 rawstr := []byte(str) 68 rawstr := []byte(str)
61 rawsalt, err := hex.DecodeString(salt) 69 rawsalt, err := hex.DecodeString(salt)
62 if err != nil { 70 if err != nil {
63 return "", "", err 71 return "", "", err
64 } 72 }
65 73
66 rawdata := make([]byte, len(rawstr) + len(rawsalt)) 74 rawdata := make([]byte, len(rawstr) + len(rawsalt))
67 rawdata = append(rawdata, rawstr...) 75 rawdata = append(rawdata, rawstr...)
68 rawdata = append(rawdata, rawsalt...) 76 rawdata = append(rawdata, rawsalt...)
69 77
70 // hash message + salt 78 // hash message + salt
71 hasher := sha256.New() 79 hasher := sha256.New()
72 hasher.Write(rawdata) 80 hasher.Write(rawdata)
73 rawhash := hasher.Sum(nil) 81 rawhash := hasher.Sum(nil)
74 82
75 hash = hex.EncodeToString(rawhash) 83 hash = hex.EncodeToString(rawhash)
76 return hash, salt, nil 84 return hash, salt, nil
77 } 85 }
78 86
79 // CreateAPIToken returns JWT token with encoded username, role, expiration date and issuer claims. 87 // CreateAPIToken returns JWT token with encoded username, role, expiration date and issuer claims.
80 // It returns an error if it fails. 88 // It returns an error if it fails.
81 func CreateAPIToken(username, role string) (string, error) { 89 func CreateAPIToken(username, role string) (string, error) {
82 var apiToken string 90 var apiToken string
83 var err error 91 var err error
84 92
85 if err != nil { 93 if err != nil {
86 return "", err 94 return "", err
87 } 95 }
88 96
89 claims := TokenClaims{ 97 claims := TokenClaims{
90 username, 98 username,
91 role, 99 role,
92 jwt.StandardClaims{ 100 jwt.StandardClaims{
93 ExpiresAt: (time.Now().Add(OneWeek)).Unix(), 101 ExpiresAt: (time.Now().Add(OneWeek)).Unix(),
94 Issuer: appName, 102 Issuer: appName,
95 }, 103 },
96 } 104 }
97 105
98 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 106 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
99 apiToken, err = jwtToken.SignedString([]byte(secret)) 107 apiToken, err = jwtToken.SignedString([]byte(secret))
100 if err != nil { 108 if err != nil {
101 return "", err 109 return "", err
102 } 110 }
103 return apiToken, nil 111 return apiToken, nil
104 } 112 }
105 113
106 // RefreshAPIToken prolongs JWT token's expiration date for one week. 114 // RefreshAPIToken prolongs JWT token's expiration date for one week.
107 // It returns new JWT token or an error if it fails. 115 // It returns new JWT token or an error if it fails.
108 func RefreshAPIToken(tokenString string) (string, error) { 116 func RefreshAPIToken(tokenString string) (string, error) {
109 var newToken string 117 var newToken string
110 tokenString = strings.TrimPrefix(tokenString, "Bearer ") 118 tokenString = strings.TrimPrefix(tokenString, "Bearer ")
111 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc) 119 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc)
112 if err != nil { 120 if err != nil {
113 return "", err 121 return "", err
114 } 122 }
115 123
116 // type assertion 124 // type assertion
117 claims, ok := token.Claims.(*TokenClaims) 125 claims, ok := token.Claims.(*TokenClaims)
118 if !ok || !token.Valid { 126 if !ok || !token.Valid {
119 return "", errors.New("token is not valid") 127 return "", errors.New("token is not valid")
120 } 128 }
121 129
122 claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix() 130 claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix()
123 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 131 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
124 132
125 newToken, err = jwtToken.SignedString([]byte(secret)) 133 newToken, err = jwtToken.SignedString([]byte(secret))
126 if err != nil { 134 if err != nil {
127 return "", err 135 return "", err
128 } 136 }
129 137
130 return newToken, nil 138 return newToken, nil
131 } 139 }
132 140
133 // ParseAPIToken parses JWT token claims. 141 // ParseAPIToken parses JWT token claims.
134 // It returns a pointer to TokenClaims struct or an error if it fails. 142 // It returns a pointer to TokenClaims struct or an error if it fails.
135 func ParseAPIToken(tokenString string) (*TokenClaims, error) { 143 func ParseAPIToken(tokenString string) (*TokenClaims, error) {
136 if ok := strings.HasPrefix(tokenString, "Bearer "); ok { 144 if ok := strings.HasPrefix(tokenString, "Bearer "); ok {
137 tokenString = strings.TrimPrefix(tokenString, "Bearer ") 145 tokenString = strings.TrimPrefix(tokenString, "Bearer ")
138 } else { 146 } else {
139 return &TokenClaims{}, errors.New("Authorization header is incomplete") 147 return &TokenClaims{}, errors.New("Authorization header is incomplete")
140 } 148 }
141 149
142 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc) 150 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc)
143 if err != nil { 151 if err != nil {
144 return &TokenClaims{}, err 152 return &TokenClaims{}, err
145 } 153 }
146 154
147 // type assertion 155 // type assertion
148 claims, ok := token.Claims.(*TokenClaims) 156 claims, ok := token.Claims.(*TokenClaims)
149 if !ok || !token.Valid { 157 if !ok || !token.Valid {
150 return &TokenClaims{}, errors.New("token is not valid") 158 return &TokenClaims{}, errors.New("token is not valid")
151 } 159 }
152 return claims, nil 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 // secretFunc returns byte slice of API secret keyword. 184 // secretFunc returns byte slice of API secret keyword.
156 func secretFunc(token *jwt.Token) (interface{}, error) { 185 func secretFunc(token *jwt.Token) (interface{}, error) {
157 return []byte(secret), nil 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 }
159 203
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "net/http" 4 "net/http"
5 "encoding/json" 5 "encoding/json"
6 "fmt"
6 ) 7 )
7 8
8 const templateHttpErr500_EN = "An internal server error has occurred." 9 const templateHttpErr500_EN = "An internal server error has occurred."
9 const templateHttpErr500_RS = "Došlo je do greške na serveru." 10 const templateHttpErr500_RS = "Došlo je do greške na serveru."
10 const templateHttpErr400_EN = "Bad request: invalid request body." 11 const templateHttpErr400_EN = "Bad request: invalid request body."
11 const templateHttpErr400_RS = "Neispravan zahtev." 12 const templateHttpErr400_RS = "Neispravan zahtev."
12 const templateHttpErr401_EN = "Unauthorized request." 13 const templateHttpErr401_EN = "Unauthorized request."
13 const templateHttpErr401_RS = "Neautorizovan zahtev." 14 const templateHttpErr401_RS = "Neautorizovan zahtev."
14 15
15 type httpError struct { 16 type httpError struct {
16 Error []HttpErrorDesc `json:"error"` 17 Error []HttpErrorDesc `json:"error"`
17 Request string `json:"request"` 18 Request string `json:"request"`
18 } 19 }
19 20
20 type HttpErrorDesc struct { 21 type HttpErrorDesc struct {
21 Lang string `json:"lang"` 22 Lang string `json:"lang"`
22 Desc string `json:"description"` 23 Desc string `json:"description"`
23 } 24 }
24 25
25 // ErrorResponse writes HTTP error to w. 26 // ErrorResponse writes HTTP error to w.
26 func ErrorResponse(w http.ResponseWriter, r *http.Request, code int, desc []HttpErrorDesc) { 27 func ErrorResponse(w http.ResponseWriter, r *http.Request, code int, desc []HttpErrorDesc) {
27 err := httpError{ desc, r.Method + " " + r.URL.Path } 28 err := httpError{ desc, r.Method + " " + r.URL.Path }
28 w.WriteHeader(code) 29 w.WriteHeader(code)
29 json.NewEncoder(w).Encode(err) 30 json.NewEncoder(w).Encode(err)
30 } 31 }
31 32
32 // BadRequestResponse writes HTTP error 400 to w. 33 // BadRequestResponse writes HTTP error 400 to w.
33 func BadRequestResponse(w http.ResponseWriter, req *http.Request) { 34 func BadRequestResponse(w http.ResponseWriter, req *http.Request) {
34 ErrorResponse(w, req, http.StatusBadRequest, []HttpErrorDesc{ 35 ErrorResponse(w, req, http.StatusBadRequest, []HttpErrorDesc{
35 { "en", templateHttpErr400_EN }, 36 { "en", templateHttpErr400_EN },
36 { "rs", templateHttpErr400_RS }, 37 { "rs", templateHttpErr400_RS },
37 }) 38 })
38 } 39 }
39 40
40 // InternalSeverErrorResponse writes HTTP error 500 to w. 41 // InternalSeverErrorResponse writes HTTP error 500 to w.
41 func InternalServerErrorResponse(w http.ResponseWriter, req *http.Request) { 42 func InternalServerErrorResponse(w http.ResponseWriter, req *http.Request) {
42 ErrorResponse(w, req, http.StatusInternalServerError, []HttpErrorDesc{ 43 ErrorResponse(w, req, http.StatusInternalServerError, []HttpErrorDesc{
43 { "en", templateHttpErr500_EN }, 44 { "en", templateHttpErr500_EN },
44 { "rs", templateHttpErr500_RS }, 45 { "rs", templateHttpErr500_RS },
45 }) 46 })
46 } 47 }
47 48
48 // UnauthorizedError writes HTTP error 401 to w. 49 // UnauthorizedError writes HTTP error 401 to w.
49 func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) { 50 func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) {
50 ErrorResponse(w, req, http.StatusUnauthorized, []HttpErrorDesc{ 51 ErrorResponse(w, req, http.StatusUnauthorized, []HttpErrorDesc{
51 { "en", templateHttpErr401_EN }, 52 { "en", templateHttpErr401_EN },
52 { "rs", templateHttpErr401_RS }, 53 { "rs", templateHttpErr401_RS },
53 }) 54 })
54 } 55 }
55 56
56 // TODO: Check for content type 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 // If authentication passes it calls the handlerFunc. 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 return func(w http.ResponseWriter, req *http.Request) { 61 return func(w http.ResponseWriter, req *http.Request) {
61 w.Header().Set("Access-Control-Allow-Origin", "*") 62 w.Header().Set("Access-Control-Allow-Origin", "*")
62 63
63 w.Header().Set("Access-Control-Allow-Methods", 64 w.Header().Set("Access-Control-Allow-Methods",
64 "POST, GET, PUT, DELETE, OPTIONS") 65 "POST, GET, PUT, DELETE, OPTIONS")
65 66
66 w.Header().Set("Access-Control-Allow-Headers", 67 w.Header().Set("Access-Control-Allow-Headers",
67 `Accept, Content-Type, Content-Length, 68 `Accept, Content-Type, Content-Length,
68 Accept-Encoding, X-CSRF-Token, Authorization`) 69 Accept-Encoding, X-CSRF-Token, Authorization`)
69 70
70 w.Header().Set("Content-Type", "application/json; charset=utf-8") 71 w.Header().Set("Content-Type", "application/json; charset=utf-8")
71 72
72 if req.Method == "OPTIONS" { 73 if req.Method == "OPTIONS" {
73 return 74 return
74 } 75 }
75 76
76 if auth { 77 if authorizedRoles != nil {
77 token := req.Header.Get("Authorization") 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 UnauthorizedResponse(w, req) 82 UnauthorizedResponse(w, req)
80 return 83 return
81 } 84 }
82 } 85 }
83 86
84 err := req.ParseForm() 87 err := req.ParseForm()
85 if err != nil { 88 if err != nil {
86 BadRequestResponse(w, req) 89 BadRequestResponse(w, req)
87 return 90 return
88 } 91 }
89 92
90 // execute HandlerFunc 93 // execute HandlerFunc
91 handlerFunc(w, req) 94 handlerFunc(w, req)
92 } 95 }
93 } 96 }
94 97
95 // NotFoundHandler writes HTTP error 404 to w. 98 // NotFoundHandler writes HTTP error 404 to w.
96 func NotFoundHandler(w http.ResponseWriter, req *http.Request) { 99 func NotFoundHandler(w http.ResponseWriter, req *http.Request) {
97 ErrorResponse(w, req, http.StatusNotFound, []HttpErrorDesc{ 100 ErrorResponse(w, req, http.StatusNotFound, []HttpErrorDesc{
98 { "en", "Not found." }, 101 { "en", "Not found." },
99 { "rs", "Traženi resurs ne postoji." }, 102 { "rs", "Traženi resurs ne postoji." },
100 }) 103 })
101 } 104 }
102 105
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "net/http" 4 "net/http"
5 "encoding/json" 5 "encoding/json"
6 "errors" 6 "errors"
7 "gopkg.in/rana/ora.v3" 7 "gopkg.in/rana/ora.v3"
8 "io" 8 "io"
9 "io/ioutil" 9 "io/ioutil"
10 "sync" 10 "sync"
11 ) 11 )
12 12
13 var mu = &sync.Mutex{} 13 var mu = &sync.Mutex{}
14 var payloads []payloadBuff 14 var payloads []payloadBuff
15 15
16 type LangMap map[string]map[string]string 16 type LangMap map[string]map[string]string
17 17
18 type Field struct { 18 type Field struct {
19 Parameter string `json:"param"` 19 Parameter string `json:"param"`
20 Type string `json:"type"` 20 Type string `json:"type"`
21 Visible bool `json:"visible"` 21 Visible bool `json:"visible"`
22 Editable bool `json:"editable"` 22 Editable bool `json:"editable"`
23 } 23 }
24 24
25 type CorrelationField struct { 25 type CorrelationField struct {
26 Result string `json:"result"` 26 Result string `json:"result"`
27 Elements []string `json:"elements"` 27 Elements []string `json:"elements"`
28 Type string `json:"type"` 28 Type string `json:"type"`
29 } 29 }
30 30
31 type Translation struct { 31 type Translation struct {
32 Language string `json:"language"` 32 Language string `json:"language"`
33 FieldsLabels map[string]string `json:"fieldsLabels"` 33 FieldsLabels map[string]string `json:"fieldsLabels"`
34 } 34 }
35 35
36 type payloadBuff struct { 36 type payloadBuff struct {
37 Type string `json:"tableType"` 37 Type string `json:"tableType"`
38 Method string `json:"method"` 38 Method string `json:"method"`
39 Params map[string]string `json:"params"` 39 Params map[string]string `json:"params"`
40 Lang []Translation `json:"lang"` 40 Lang []Translation `json:"lang"`
41 Fields []Field `json:"fields"` 41 Fields []Field `json:"fields"`
42 Correlations []CorrelationField `json:"correlationFields"` 42 Correlations []CorrelationField `json:"correlationFields"`
43 IdField string `json:"idField"` 43 IdField string `json:"idField"`
44 44
45 // Data can only hold slices of any type. It can't be used for itteration 45 // Data can only hold slices of any type. It can't be used for itteration
46 Data interface{} `json:"data"` 46 Data interface{} `json:"data"`
47 } 47 }
48 48
49 type Payload struct { 49 type Payload struct {
50 Method string `json:"method"` 50 Method string `json:"method"`
51 Params map[string]string `json:"params"` 51 Params map[string]string `json:"params"`
52 Lang []Translation `json:"lang"` 52 Lang []Translation `json:"lang"`
53 Fields []Field `json:"fields"` 53 Fields []Field `json:"fields"`
54 Correlations []CorrelationField `json:"correlationFields"` 54 Correlations []CorrelationField `json:"correlationFields"`
55 IdField string `json:"idField"` 55 IdField string `json:"idField"`
56 56
57 // Data can only hold slices of any type. It can't be used for itteration 57 // Data can only hold slices of any type. It can't be used for itteration
58 Data interface{} `json:"data"` 58 Data interface{} `json:"data"`
59 } 59 }
60 60
61 // NewPayload returs a payload sceleton for provided table. 61 // NewPayload returs a payload sceleton for provided table.
62 func NewPayload(r *http.Request, table string) Payload { 62 func NewPayload(r *http.Request, table string) Payload {
63 var pload Payload 63 var pload Payload
64 64
65 pload.Method = r.Method + " " + r.URL.Path 65 pload.Method = r.Method + " " + r.URL.Path
66 if table != "" { 66 if table != "" {
67 pload.Params = make(map[string]string, 0) 67 pload.Params = make(map[string]string, 0)
68 pload.Lang = translations(table) 68 pload.Lang = translations(table)
69 pload.Fields = fields(table) 69 pload.Fields = fields(table)
70 pload.IdField = id(table) 70 pload.IdField = id(table)
71 pload.Correlations = correlations(table) 71 pload.Correlations = correlations(table)
72 } 72 }
73 return pload 73 return pload
74 } 74 }
75 75
76 // DeliverPayload encodes payload to w. 76 // DeliverPayload encodes payload to w.
77 func DeliverPayload(w http.ResponseWriter, payload Payload) { 77 func DeliverPayload(w http.ResponseWriter, payload Payload) {
78 json.NewEncoder(w).Encode(payload) 78 json.NewEncoder(w).Encode(payload)
79 payload.Data = nil 79 payload.Data = nil
80 } 80 }
81 81
82 // translations returns a slice of translations for a payload/table of ptype type. 82 // translations returns a slice of translations for a payload/table of ptype type.
83 func translations(ptype string) []Translation { 83 func translations(ptype string) []Translation {
84 var translations []Translation 84 var translations []Translation
85 85
86 for _, pload := range payloads { 86 for _, pload := range payloads {
87 if pload.Type == ptype { 87 if pload.Type == ptype {
88 for _, t := range pload.Lang { 88 for _, t := range pload.Lang {
89 translations = append(translations, Translation{ 89 translations = append(translations, Translation{
90 Language: t.Language, 90 Language: t.Language,
91 FieldsLabels: t.FieldsLabels, 91 FieldsLabels: t.FieldsLabels,
92 }) 92 })
93 } 93 }
94 } 94 }
95 } 95 }
96 96
97 return translations 97 return translations
98 } 98 }
99 99
100 // fields returns a slice of fields for a payload/table of ptype type. 100 // fields returns a slice of fields for a payload/table of ptype type.
101 func fields(ptype string) []Field { 101 func fields(ptype string) []Field {
102 var fields []Field 102 var fields []Field
103 103
104 for _, pload := range payloads { 104 for _, pload := range payloads {
105 if pload.Type == ptype { 105 if pload.Type == ptype {
106 for _, f := range pload.Fields { 106 for _, f := range pload.Fields {
107 fields = append(fields, f) 107 fields = append(fields, f)
108 } 108 }
109 } 109 }
110 } 110 }
111 111
112 return fields 112 return fields
113 } 113 }
114 114
115 // id returns the name of ID field of a payload/table of ptype type. 115 // id returns the name of ID field of a payload/table of ptype type.
116 func id(ptype string) string { 116 func id(ptype string) string {
117 for _, pload := range payloads { 117 for _, pload := range payloads {
118 if pload.Type == ptype { 118 if pload.Type == ptype {
119 return pload.IdField 119 return pload.IdField
120 } 120 }
121 } 121 }
122 return "" 122 return ""
123 } 123 }
124 124
125 // correlations returns a slice of correlation fields for a payload/table of ptype type. 125 // correlations returns a slice of correlation fields for a payload/table of ptype type.
126 func correlations(ptype string) []CorrelationField { 126 func correlations(ptype string) []CorrelationField {
127 var corr []CorrelationField 127 var corr []CorrelationField
128 128
129 for _, pload := range payloads { 129 for _, pload := range payloads {
130 if pload.Type == ptype { 130 if pload.Type == ptype {
131 for _, c := range pload.Correlations { 131 for _, c := range pload.Correlations {
132 corr = append(corr, c) 132 corr = append(corr, c)
133 } 133 }
134 } 134 }
135 } 135 }
136 136
137 return corr 137 return corr
138 } 138 }
139 139
140 // InitTables loads all payloads in the payloads variable. 140 // InitTables loads all payloads in the payloads variable.
141 // Returns an error if it fails. 141 // Returns an error if it fails.
142 func InitTables(db *ora.Ses, project string) error { 142 func InitTables(db *ora.Ses, project string) error {
143 jsonbuf, _ := fetchJSON(db, EqualQuotes(project)) 143 jsonbuf, _ := fetchJSON(db, EqualQuotes(project))
144 mu.Lock() 144 mu.Lock()
145 defer mu.Unlock() 145 defer mu.Unlock()
146 json.Unmarshal(jsonbuf, &payloads) 146 json.Unmarshal(jsonbuf, &payloads)
147 if len(payloads) == 0 { 147 if len(payloads) == 0 {
148 return errors.New("tables config is corrupt") 148 return errors.New("tables config is corrupt")
149 } 149 }
150 return nil 150 return nil
151 } 151 }
152 152
153 // fetchJSON returns a byte slice of JSON configuration file from TABLES_CONFIG table. 153 // fetchJSON returns a byte slice of JSON configuration file from TABLES_CONFIG table.
154 // Returns an error if it fails. 154 // Returns an error if it fails.
155 func fetchJSON(db *ora.Ses, project string) ([]byte, error) { 155 func fetchJSON(db *ora.Ses, project string) ([]byte, error) {
156 stmt, err := db.Prep(`SELECT 156 stmt, err := db.Prep(`SELECT
157 JSON_CLOB 157 JSON_CLOB
158 FROM TABLES_CONFIG 158 FROM TABLES_CONFIG
159 WHERE PROJEKAT` + project, ora.S) 159 WHERE PROJEKAT` + project, ora.S)
160 defer stmt.Close() 160 defer stmt.Close()
161 161
162 if err != nil { 162 if err != nil {
163 return nil, err 163 return nil, err
164 } 164 }
165 165
166 rset, err := stmt.Qry() 166 rset, err := stmt.Qry()
167 if err != nil { 167 if err != nil {
168 return nil, err 168 return nil, err
169 } 169 }
170 170
171 bytes := make([]byte, 0) 171 bytes := make([]byte, 0)
172 if rset.Next() { 172 if rset.Next() {
173 lob := rset.Row[0].(io.Reader) 173 lob := rset.Row[0].(io.Reader)
174 bytes, err = ioutil.ReadAll(lob) 174 if lob != nil {
175 if err != nil { 175 bytes, err = ioutil.ReadAll(lob)
176 // Ignore for now, it's some weird streaming read/write LOB error. 176 if err != nil {
177 // TODO: Find a fix for this. 177 // TODO: Find a fix for this.
178 //return nil, err 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
182 return bytes, nil 187 return bytes, nil
183 } 188 }
184 189
185 // DecodeJSON decodes JSON data from r to v. 190 // DecodeJSON decodes JSON data from r to v.
186 // Returns an error if it fails. 191 // Returns an error if it fails.
187 func DecodeJSON(r io.Reader, v interface{}) error { 192 func DecodeJSON(r io.Reader, v interface{}) error {
188 return json.NewDecoder(r).Decode(v) 193 return json.NewDecoder(r).Decode(v)
189 } 194 }
190 195