Commit d2ddf82ef16c3ab5764f1b8136f1f387b117277b

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

started on new rbac

1 // TODO: Improve roles
1 package webutility 2 package webutility
2 3
3 import ( 4 import (
4 "errors"
5 "time"
6 "crypto/sha256"
7 "crypto/rand" 5 "crypto/rand"
6 "crypto/sha256"
8 "encoding/hex" 7 "encoding/hex"
9 "strings" 8 "errors"
10 "net/http" 9 "net/http"
10 "strings"
11 "time"
11 12
12 "github.com/dgrijalva/jwt-go" 13 "github.com/dgrijalva/jwt-go"
13 ) 14 )
14 15
15 const OneDay = time.Hour*24 16 const OneDay = time.Hour * 24
16 const OneWeek = OneDay*7 17 const OneWeek = OneDay * 7
17 const saltSize = 32 18 const saltSize = 32
18 const appName = "korisnicki-centar" 19 const appName = "korisnicki-centar"
19 const secret = "korisnicki-centar-api" 20 const secret = "korisnicki-centar-api"
20 21
21 const RoleAdmin string = "ADMINISTRATOR" 22 const RoleAdmin string = "ADMINISTRATOR"
22 const RoleManager string = "RUKOVODILAC" 23 const RoleManager string = "RUKOVODILAC"
23 const RoleReporter string = "REPORTER" 24 const RoleReporter string = "REPORTER"
24 const RoleOperator string = "OPERATER" 25 const RoleOperator string = "OPERATER"
25 const RoleAdminID uint32 = 1 26 const RoleAdminID uint32 = 1
26 const RoleManagerID uint32 = 2 27 const RoleManagerID uint32 = 2
27 const RoleReporterID uint32 = 3 28 const RoleReporterID uint32 = 3
28 const RoleOperatorID uint32 = 4 29 const RoleOperatorID uint32 = 4
29 30
31 type Role struct {
32 name string
33 id uint32
34 }
35
30 // TokenClaims are JWT token claims. 36 // TokenClaims are JWT token claims.
31 type TokenClaims struct { 37 type TokenClaims struct {
32 Username string `json:"username"` 38 Username string `json:"username"`
33 Role string `json:"role"` 39 Role string `json:"role"`
34 RoleID uint32 `json:"roleID"` 40 RoleID uint32 `json:"roleID"`
35 jwt.StandardClaims 41 jwt.StandardClaims
36 } 42 }
37 43
38 // CredentialsStruct is an instace of username/password values. 44 // CredentialsStruct is an instace of username/password values.
39 type CredentialsStruct struct { 45 type CredentialsStruct struct {
40 Username string `json:"username"` 46 Username string `json:"username"`
41 Password string `json:"password"` 47 Password string `json:"password"`
42 } 48 }
43 49
50 var admin Role = Role{RoleAdmin, RoleAdminID}
51 var manager Role = Role{RoleManager, RoleManagerID}
52 var reporter Role = Role{RoleReporter, RoleReporterID}
53 var operator Role = Role{RoleOperator, RoleOperatorID}
54
44 // generateSalt returns a string of random characters of 'saltSize' length. 55 // generateSalt returns a string of random characters of 'saltSize' length.
45 func generateSalt() (salt string, err error) { 56 func generateSalt() (salt string, err error) {
46 rawsalt := make([]byte, saltSize) 57 rawsalt := make([]byte, saltSize)
47 58
48 _, err = rand.Read(rawsalt) 59 _, err = rand.Read(rawsalt)
49 if err != nil { 60 if err != nil {
50 return "", err 61 return "", err
51 } 62 }
52 63
53 salt = hex.EncodeToString(rawsalt) 64 salt = hex.EncodeToString(rawsalt)
54 return salt, nil 65 return salt, nil
55 } 66 }
56 67
57 // HashString hashes input string with SHA256 algorithm. 68 // HashString hashes input string with SHA256 algorithm.
58 // If the presalt parameter is not provided HashString will generate new salt string. 69 // If the presalt parameter is not provided HashString will generate new salt string.
59 // Returns hash and salt string or an error if it fails. 70 // Returns hash and salt string or an error if it fails.
60 func HashString(str string, presalt string) (hash, salt string, err error) { 71 func HashString(str string, presalt string) (hash, salt string, err error) {
61 // chech if message is presalted 72 // chech if message is presalted
62 if presalt == "" { 73 if presalt == "" {
63 salt, err = generateSalt() 74 salt, err = generateSalt()
64 if err != nil { 75 if err != nil {
65 return "", "", err 76 return "", "", err
66 } 77 }
67 } else { 78 } else {
68 salt = presalt 79 salt = presalt
69 } 80 }
70 81
71 // convert strings to raw byte slices 82 // convert strings to raw byte slices
72 rawstr := []byte(str) 83 rawstr := []byte(str)
73 rawsalt, err := hex.DecodeString(salt) 84 rawsalt, err := hex.DecodeString(salt)
74 if err != nil { 85 if err != nil {
75 return "", "", err 86 return "", "", err
76 } 87 }
77 88
78 rawdata := make([]byte, len(rawstr) + len(rawsalt)) 89 rawdata := make([]byte, len(rawstr)+len(rawsalt))
79 rawdata = append(rawdata, rawstr...) 90 rawdata = append(rawdata, rawstr...)
80 rawdata = append(rawdata, rawsalt...) 91 rawdata = append(rawdata, rawsalt...)
81 92
82 // hash message + salt 93 // hash message + salt
83 hasher := sha256.New() 94 hasher := sha256.New()
84 hasher.Write(rawdata) 95 hasher.Write(rawdata)
85 rawhash := hasher.Sum(nil) 96 rawhash := hasher.Sum(nil)
86 97
87 hash = hex.EncodeToString(rawhash) 98 hash = hex.EncodeToString(rawhash)
88 return hash, salt, nil 99 return hash, salt, nil
89 } 100 }
90 101
91 // CreateAPIToken returns JWT token with encoded username, role, expiration date and issuer claims. 102 // CreateAPIToken returns JWT token with encoded username, role, expiration date and issuer claims.
92 // It returns an error if it fails. 103 // It returns an error if it fails.
93 func CreateAPIToken(username, role string, roleID uint32) (string, error) { 104 func CreateAPIToken(username, role string, roleID uint32) (string, error) {
94 var apiToken string 105 var apiToken string
95 var err error 106 var err error
96 107
97 if err != nil { 108 if err != nil {
98 return "", err 109 return "", err
99 } 110 }
100 111
101 claims := TokenClaims{ 112 claims := TokenClaims{
102 username, 113 username,
103 role, 114 role,
104 roleID, 115 roleID,
105 jwt.StandardClaims{ 116 jwt.StandardClaims{
106 ExpiresAt: (time.Now().Add(OneWeek)).Unix(), 117 ExpiresAt: (time.Now().Add(OneWeek)).Unix(),
107 Issuer: appName, 118 Issuer: appName,
108 }, 119 },
109 } 120 }
110 121
111 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 122 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
112 apiToken, err = jwtToken.SignedString([]byte(secret)) 123 apiToken, err = jwtToken.SignedString([]byte(secret))
113 if err != nil { 124 if err != nil {
114 return "", err 125 return "", err
115 } 126 }
116 return apiToken, nil 127 return apiToken, nil
117 } 128 }
118 129
119 // RefreshAPIToken prolongs JWT token's expiration date for one week. 130 // RefreshAPIToken prolongs JWT token's expiration date for one week.
120 // It returns new JWT token or an error if it fails. 131 // It returns new JWT token or an error if it fails.
121 func RefreshAPIToken(tokenString string) (string, error) { 132 func RefreshAPIToken(tokenString string) (string, error) {
122 var newToken string 133 var newToken string
123 tokenString = strings.TrimPrefix(tokenString, "Bearer ") 134 tokenString = strings.TrimPrefix(tokenString, "Bearer ")
124 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc) 135 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc)
125 if err != nil { 136 if err != nil {
126 return "", err 137 return "", err
127 } 138 }
128 139
129 // type assertion 140 // type assertion
130 claims, ok := token.Claims.(*TokenClaims) 141 claims, ok := token.Claims.(*TokenClaims)
131 if !ok || !token.Valid { 142 if !ok || !token.Valid {
132 return "", errors.New("token is not valid") 143 return "", errors.New("token is not valid")
133 } 144 }
134 145
135 claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix() 146 claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix()
136 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 147 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
137 148
138 newToken, err = jwtToken.SignedString([]byte(secret)) 149 newToken, err = jwtToken.SignedString([]byte(secret))
139 if err != nil { 150 if err != nil {
140 return "", err 151 return "", err
141 } 152 }
142 153
143 return newToken, nil 154 return newToken, nil
144 } 155 }
145 156
146 // ParseAPIToken parses JWT token claims. 157 // ParseAPIToken parses JWT token claims.
147 // It returns a pointer to TokenClaims struct or an error if it fails. 158 // It returns a pointer to TokenClaims struct or an error if it fails.
148 func ParseAPIToken(tokenString string) (*TokenClaims, error) { 159 func ParseAPIToken(tokenString string) (*TokenClaims, error) {
149 if ok := strings.HasPrefix(tokenString, "Bearer "); ok { 160 if ok := strings.HasPrefix(tokenString, "Bearer "); ok {
150 tokenString = strings.TrimPrefix(tokenString, "Bearer ") 161 tokenString = strings.TrimPrefix(tokenString, "Bearer ")
151 } else { 162 } else {
152 return &TokenClaims{}, errors.New("Authorization header is incomplete") 163 return &TokenClaims{}, errors.New("Authorization header is incomplete")
153 } 164 }
154 165
155 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc) 166 token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc)
156 if err != nil { 167 if err != nil {
157 return &TokenClaims{}, err 168 return &TokenClaims{}, err
158 } 169 }
159 170
160 // type assertion 171 // type assertion
161 claims, ok := token.Claims.(*TokenClaims) 172 claims, ok := token.Claims.(*TokenClaims)
162 if !ok || !token.Valid { 173 if !ok || !token.Valid {
163 return &TokenClaims{}, errors.New("token is not valid") 174 return &TokenClaims{}, errors.New("token is not valid")
164 } 175 }
165 return claims, nil 176 return claims, nil
166 } 177 }
167 178
168 func GetTokenClaims(r *http.Request) (claims *TokenClaims, err error) { 179 func GetTokenClaims(r *http.Request) (claims *TokenClaims, err error) {
169 token := r.Header.Get("Authorization") 180 token := r.Header.Get("Authorization")
170 if ok := strings.HasPrefix(token, "Bearer "); ok { 181 if ok := strings.HasPrefix(token, "Bearer "); ok {
171 token = strings.TrimPrefix(token, "Bearer ") 182 token = strings.TrimPrefix(token, "Bearer ")
172 } else { 183 } else {
173 return &TokenClaims{}, errors.New("Authorization header is incomplete") 184 return &TokenClaims{}, errors.New("Authorization header is incomplete")
174 } 185 }
175 186
176 parsedToken, err := jwt.ParseWithClaims(token, &TokenClaims{}, secretFunc) 187 parsedToken, err := jwt.ParseWithClaims(token, &TokenClaims{}, secretFunc)
177 if err != nil { 188 if err != nil {
178 return &TokenClaims{}, err 189 return &TokenClaims{}, err
179 } 190 }
180 191
181 // type assertion 192 // type assertion
182 claims, ok := parsedToken.Claims.(*TokenClaims) 193 claims, ok := parsedToken.Claims.(*TokenClaims)
183 if !ok || !parsedToken.Valid { 194 if !ok || !parsedToken.Valid {
184 return &TokenClaims{}, errors.New("token is not valid") 195 return &TokenClaims{}, errors.New("token is not valid")
185 } 196 }
186 return claims, err 197 return claims, err
187 } 198 }
188 199
189 // secretFunc returns byte slice of API secret keyword. 200 // secretFunc returns byte slice of API secret keyword.
190 func secretFunc(token *jwt.Token) (interface{}, error) { 201 func secretFunc(token *jwt.Token) (interface{}, error) {
191 return []byte(secret), nil 202 return []byte(secret), nil
192 } 203 }
193 204
194 // roleAuthorized returns true if role from userClaims matches any of the authorizedRoles 205 // rbacEnforce returns true if role that made HTTP request is authorized to
195 // or if authorizedRoles contains "*". 206 // access the resource it is targeting.
196 func roleAuthorized(authorizedRoles []string, userClaims *TokenClaims) bool { 207 // It exctracts user's role from the JWT token located in Authorization header of
197 if userClaims == nil { 208 // http.Request and then compares it with the list of supplied roles and returns
209 // true if there's a match, if "*" is provided or if the authRoles is nil.
210 // Otherwise it returns false.
211 func RbacCheck(req *http.Request, authRoles []string) bool {
212 if authRoles == nil {
213 return true
214 }
215
216 token := req.Header.Get("Authorization")
217 claims, err := ParseAPIToken(token)
218 if err != nil {
198 return false 219 return false
199 } 220 }
200 for _, r := range authorizedRoles { 221
201 if userClaims.Role == r || r == "*" { 222 for _, r := range authRoles {
223 if claims.Role == r || r == "*" {
202 return true 224 return true
203 } 225 }
204 } 226 }
227
205 return false 228 return false
206 } 229 }
230
231 // Rbac sets common headers and performs RBAC.
232 // If RBAC passes it calls the handlerFunc.
233 func Rbac(handlerFunc http.HandlerFunc, authRoles []string) http.HandlerFunc {
234 return func(w http.ResponseWriter, req *http.Request) {
235 w.Header().Set("Access-Control-Allow-Origin", "*")
236
237 w.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS")
238
239 w.Header().Set("Access-Control-Allow-Headers", `Accept, Content-Type,
240 Content-Length, Accept-Encoding, X-CSRF-Token, Authorization`)
241
242 w.Header().Set("Content-Type", "application/json; charset=utf-8")
243
244 // TODO: Check for content type
245
246 if req.Method == "OPTIONS" {
247 return
248 }
249
250 err := req.ParseForm()
251 if err != nil {
252 BadRequestResponse(w, req)
253 return
254 }
255
256 if !RbacCheck(req, authRoles) {
257 UnauthorizedResponse(w, req)
258 return
259 }
260
261 // execute HandlerFunc
262 handlerFunc(w, req)
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "net/http"
5 "encoding/json" 4 "encoding/json"
5 "net/http"
6 ) 6 )
7 7
8 const templateHttpErr500_EN = "An internal server error has occurred." 8 const templateHttpErr500_EN = "An internal server error has occurred."
9 const templateHttpErr500_RS = "Došlo je do greške na serveru." 9 const templateHttpErr500_RS = "Došlo je do greške na serveru."
10 const templateHttpErr400_EN = "Bad request: invalid request body." 10 const templateHttpErr400_EN = "Bad request: invalid request body."
11 const templateHttpErr400_RS = "Neispravan zahtev." 11 const templateHttpErr400_RS = "Neispravan zahtev."
12 const templateHttpErr401_EN = "Unauthorized request." 12 const templateHttpErr401_EN = "Unauthorized request."
13 const templateHttpErr401_RS = "Neautorizovan zahtev." 13 const templateHttpErr401_RS = "Neautorizovan zahtev."
14 14
15 type httpError struct { 15 type httpError struct {
16 Error []HttpErrorDesc `json:"error"` 16 Error []HttpErrorDesc `json:"error"`
17 Request string `json:"request"` 17 Request string `json:"request"`
18 } 18 }
19 19
20 type HttpErrorDesc struct { 20 type HttpErrorDesc struct {
21 Lang string `json:"lang"` 21 Lang string `json:"lang"`
22 Desc string `json:"description"` 22 Desc string `json:"description"`
23 } 23 }
24 24
25 // ErrorResponse writes HTTP error to w. 25 // ErrorResponse writes HTTP error to w.
26 func ErrorResponse(w http.ResponseWriter, r *http.Request, code int, desc []HttpErrorDesc) { 26 func ErrorResponse(w http.ResponseWriter, r *http.Request, code int, desc []HttpErrorDesc) {
27 err := httpError{ desc, r.Method + " " + r.URL.Path } 27 err := httpError{desc, r.Method + " " + r.URL.Path}
28 w.WriteHeader(code) 28 w.WriteHeader(code)
29 json.NewEncoder(w).Encode(err) 29 json.NewEncoder(w).Encode(err)
30 } 30 }
31 31
32 // BadRequestResponse writes HTTP error 400 to w. 32 // BadRequestResponse writes HTTP error 400 to w.
33 func BadRequestResponse(w http.ResponseWriter, req *http.Request) { 33 func BadRequestResponse(w http.ResponseWriter, req *http.Request) {
34 ErrorResponse(w, req, http.StatusBadRequest, []HttpErrorDesc{ 34 ErrorResponse(w, req, http.StatusBadRequest, []HttpErrorDesc{
35 { "en", templateHttpErr400_EN }, 35 {"en", templateHttpErr400_EN},
36 { "rs", templateHttpErr400_RS }, 36 {"rs", templateHttpErr400_RS},
37 }) 37 })
38 } 38 }
39 39
40 // InternalSeverErrorResponse writes HTTP error 500 to w. 40 // InternalSeverErrorResponse writes HTTP error 500 to w.
41 func InternalServerErrorResponse(w http.ResponseWriter, req *http.Request) { 41 func InternalServerErrorResponse(w http.ResponseWriter, req *http.Request) {
42 ErrorResponse(w, req, http.StatusInternalServerError, []HttpErrorDesc{ 42 ErrorResponse(w, req, http.StatusInternalServerError, []HttpErrorDesc{
43 { "en", templateHttpErr500_EN }, 43 {"en", templateHttpErr500_EN},
44 { "rs", templateHttpErr500_RS }, 44 {"rs", templateHttpErr500_RS},
45 }) 45 })
46 } 46 }
47 47
48 // UnauthorizedError writes HTTP error 401 to w. 48 // UnauthorizedError writes HTTP error 401 to w.
49 func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) { 49 func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) {
50 ErrorResponse(w, req, http.StatusUnauthorized, []HttpErrorDesc{ 50 ErrorResponse(w, req, http.StatusUnauthorized, []HttpErrorDesc{
51 { "en", templateHttpErr401_EN }, 51 {"en", templateHttpErr401_EN},
52 { "rs", templateHttpErr401_RS }, 52 {"rs", templateHttpErr401_RS},
53 }) 53 })
54 } 54 }
55 55
56 // TODO: Check for content type
57 // WrapHandler sets common headers, checks for token validity and performs access control checks.
58 // If authentication passes it calls the handlerFunc.
59 func WrapHandler(handlerFunc http.HandlerFunc, authorizedRoles []string) http.HandlerFunc {
60 return func(w http.ResponseWriter, req *http.Request) {
61 w.Header().Set("Access-Control-Allow-Origin", "*")
62
63 w.Header().Set("Access-Control-Allow-Methods",
64 "POST, GET, PUT, DELETE, OPTIONS")
65
66 w.Header().Set("Access-Control-Allow-Headers",
67 `Accept, Content-Type, Content-Length,
68 Accept-Encoding, X-CSRF-Token, Authorization`)
69
70 w.Header().Set("Content-Type", "application/json; charset=utf-8")
71
72 if req.Method == "OPTIONS" {
73 return
74 }
75
76 if authorizedRoles != nil {
77 token := req.Header.Get("Authorization")
78 claims, err := ParseAPIToken(token);
79 if err != nil || !roleAuthorized(authorizedRoles, claims) {
80 UnauthorizedResponse(w, req)
81 return
82 }
83 }
84
85 err := req.ParseForm()
86 if err != nil {
87 BadRequestResponse(w, req)
88 return
89 }
90
91 // execute HandlerFunc
92 handlerFunc(w, req)
93 }
94 }
95
96 // NotFoundHandler writes HTTP error 404 to w. 56 // NotFoundHandler writes HTTP error 404 to w.
97 func NotFoundHandler(w http.ResponseWriter, req *http.Request) { 57 func NotFoundHandler(w http.ResponseWriter, req *http.Request) {
58 SetDefaultHeaders(w)
59 if req.Method == "OPTIONS" {
60 return
61 }
98 ErrorResponse(w, req, http.StatusNotFound, []HttpErrorDesc{ 62 ErrorResponse(w, req, http.StatusNotFound, []HttpErrorDesc{
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 //"fmt" 4 //"fmt"
5 "net/http"
6 "encoding/json" 5 "encoding/json"
7 "errors" 6 "errors"
8 "io" 7 "io"
8 "net/http"
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_NCLOB 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 }
1 package webutility 1 package webutility
2 2
3 //import "gopkg.in/rana/ora.v3" 3 //import "gopkg.in/rana/ora.v3"
4 import "gopkg.in/rana/ora.v4" 4 import "gopkg.in/rana/ora.v4"
5 5
6 type SelectConfig struct { 6 type SelectConfig struct {
7 ListObjType string `json:"listObjectType"` 7 ListObjType string `json:"listObjectType"`
8 ObjType string `json:"objectType"` 8 ObjType string `json:"objectType"`
9 Type string `json:"type"` 9 Type string `json:"type"`
10 IdField string `json:"idField"` 10 IdField string `json:"idField"`
11 LabelField string `json:"labelField"` 11 LabelField string `json:"labelField"`
12 ValueField string `json:"valueField"` 12 ValueField string `json:"valueField"`
13 } 13 }
14 14
15 // GetSelectConfig returns select configuration slice for the given object type. 15 // GetSelectConfig returns select configuration slice for the given object type.
16 func GetSelectConfig(db *ora.Ses, otype string) ([]SelectConfig, error) { 16 func GetSelectConfig(db *ora.Ses, otype string) ([]SelectConfig, error) {
17 resp := make([]SelectConfig, 0) 17 resp := make([]SelectConfig, 0)
18 var err error 18 var err error
19 var stmt *ora.Stmt 19 var stmt *ora.Stmt
20 query := `SELECT a.LIST_OBJECT_TYPE, a.OBJECT_TYPE, a.ID_FIELD, 20 query := `SELECT
21 a.LABEL_FIELD, a.TYPE, b.FIELD 21 a.LIST_OBJECT_TYPE,
22 a.OBJECT_TYPE,
23 a.ID_FIELD,
24 a.LABEL_FIELD,
25 a.TYPE,
26 b.FIELD
22 FROM LIST_SELECT_CONFIG a, LIST_VALUE_FIELD b 27 FROM LIST_SELECT_CONFIG a, LIST_VALUE_FIELD b
23 WHERE a.LIST_OBJECT_TYPE` + otype + ` 28 WHERE a.LIST_OBJECT_TYPE` + otype + `
24 AND b.LIST_TYPE = a.LIST_OBJECT_TYPE 29 AND b.LIST_TYPE = a.LIST_OBJECT_TYPE
25 AND b.OBJECT_TYPE = a.OBJECT_TYPE` 30 AND b.OBJECT_TYPE = a.OBJECT_TYPE`
26 31
27 stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S, ora.S, ora.S) 32 stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S, ora.S, ora.S)
28 defer stmt.Close() 33 defer stmt.Close()
29 if err != nil { 34 if err != nil {
30 return nil, err 35 return nil, err
31 } 36 }
32 37
33 rset, err := stmt.Qry() 38 rset, err := stmt.Qry()
34 if err != nil { 39 if err != nil {
35 return nil, err 40 return nil, err
36 } 41 }
37 for rset.Next() { 42 for rset.Next() {
38 resp = append(resp, SelectConfig{ 43 resp = append(resp, SelectConfig{
39 ListObjType: rset.Row[0].(string), 44 ListObjType: rset.Row[0].(string),
40 ObjType: rset.Row[1].(string), 45 ObjType: rset.Row[1].(string),
41 IdField: rset.Row[2].(string), 46 IdField: rset.Row[2].(string),
42 LabelField: rset.Row[3].(string), 47 LabelField: rset.Row[3].(string),
43 Type: rset.Row[4].(string), 48 Type: rset.Row[4].(string),
44 ValueField: rset.Row[5].(string), 49 ValueField: rset.Row[5].(string),
45 }) 50 })
46 } 51 }
47 if rset.Err() != nil { 52 if rset.Err() != nil {
48 return nil, rset.Err() 53 return nil, rset.Err()
49 } 54 }
50 55
51 return resp, nil 56 return resp, nil
52 } 57 }
53 58