Commit 33fd58161a627305c9c343cb31ea018b4b5d0ac0

Authored by markotikvic
1 parent 437859ae90
Exists in master and in 1 other branch v2

minor changes, should update dependant apps

1 package restutility 1 package restutility
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 "github.com/dgrijalva/jwt-go" 10 "github.com/dgrijalva/jwt-go"
11 ) 11 )
12 12
13 const OneDay = time.Hour*24 13 const OneDay = time.Hour*24
14 const OneWeek = OneDay*7 14 const OneWeek = OneDay*7
15 const saltSize = 32 15 const saltSize = 32
16 const appName = "korisnicki-centar" 16 const appName = "korisnicki-centar"
17 const secret = "korisnicki-centar-api" 17 const secret = "korisnicki-centar-api"
18 18
19 type TokenClaims struct { 19 type TokenClaims struct {
20 Username string `json:"username"` 20 Username string `json:"username"`
21 Role string `json:"role"` 21 Role string `json:"role"`
22 jwt.StandardClaims 22 jwt.StandardClaims
23 } 23 }
24 24
25 type CredentialsStruct struct { 25 type CredentialsStruct struct {
26 Username string `json:"username"` 26 Username string `json:"username"`
27 Password string `json:"password"` 27 Password string `json:"password"`
28 } 28 }
29 29
30 func GenerateSalt() (string, error) { 30 func generateSalt() (salt string, error) {
31 salt := ""
32 rawsalt := make([]byte, saltSize) 31 rawsalt := make([]byte, saltSize)
32
33 _, err := rand.Read(rawsalt) 33 _, err := rand.Read(rawsalt)
34 if err != nil { 34 if err != nil {
35 return "", err 35 return "", err
36 } 36 }
37
37 salt = hex.EncodeToString(rawsalt) 38 salt = hex.EncodeToString(rawsalt)
38 return salt, nil 39 return salt, nil
39 } 40 }
40 41
41 func HashMessage(message string, presalt string) (string, string, error) { 42 func HashString(str string, presalt string) (hash, salt string, err error) {
42 hash, salt := "", ""
43 var err error
44
45 // chech if message is presalted 43 // chech if message is presalted
46 if presalt == "" { 44 if presalt == "" {
47 salt, err = GenerateSalt() 45 salt, err = generateSalt()
48 if err != nil { 46 if err != nil {
49 return "", "", err 47 return "", "", err
50 } 48 }
51 } else { 49 } else {
52 salt = presalt 50 salt = presalt
53 } 51 }
54 52
55 // convert strings to raw byte slices 53 // convert strings to raw byte slices
56 rawmessage := []byte(message) 54 rawstr := []byte(str)
57 rawsalt, err := hex.DecodeString(salt) 55 rawsalt, err := hex.DecodeString(salt)
58 if err != nil { 56 if err != nil {
59 return "", "", err 57 return "", "", err
60 } 58 }
61 rawdata := make([]byte, len(rawmessage) + len(rawsalt)) 59
62 rawdata = append(rawdata, rawmessage...) 60 rawdata := make([]byte, len(rawstr) + len(rawsalt))
61 rawdata = append(rawdata, rawstr...)
63 rawdata = append(rawdata, rawsalt...) 62 rawdata = append(rawdata, rawsalt...)
64 63
65 // hash message + salt 64 // hash message + salt
66 hasher := sha256.New() 65 hasher := sha256.New()
67 hasher.Write(rawdata) 66 hasher.Write(rawdata)
68 rawhash := hasher.Sum(nil) 67 rawhash := hasher.Sum(nil)
68
69 hash = hex.EncodeToString(rawhash) 69 hash = hex.EncodeToString(rawhash)
70 return hash, salt, nil 70 return hash, salt, nil
71 } 71 }
72 72
73 func IssueAPIToken(username, role string) (string, error) { 73 func CreateAPIToken(username, role string) (string, error) {
74 var apiToken string 74 var apiToken string
75 var err error 75 var err error
76 76
77 if err != nil { 77 if err != nil {
78 return "", err 78 return "", err
79 } 79 }
80 80
81 claims := TokenClaims{ 81 claims := TokenClaims{
82 username, 82 username,
83 role, 83 role,
84 jwt.StandardClaims{ 84 jwt.StandardClaims{
85 ExpiresAt: (time.Now().Add(OneWeek)).Unix(), 85 ExpiresAt: (time.Now().Add(OneWeek)).Unix(),
86 Issuer: appName, 86 Issuer: appName,
87 }, 87 },
88 } 88 }
89 89
90 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 90 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
91 apiToken, err = jwtToken.SignedString([]byte(secret)) 91 apiToken, err = jwtToken.SignedString([]byte(secret))
92 if err != nil { 92 if err != nil {
93 return "", err 93 return "", err
94 } 94 }
95 return apiToken, nil 95 return apiToken, nil
96 } 96 }
97 97
98 func RefreshAPIToken(tokenString string) (string, error) { 98 func RefreshAPIToken(tokenString string) (string, error) {
99 var newToken string 99 var newToken string
100 tokenString = strings.TrimPrefix(tokenString, "Bearer ") 100 tokenString = strings.TrimPrefix(tokenString, "Bearer ")
101 token, err := parseTokenFunc(tokenString) 101 token, err := parseTokenFunc(tokenString)
102 if err != nil { 102 if err != nil {
103 return "", err 103 return "", err
104 } 104 }
105 105
106 // type assertion 106 // type assertion
107 claims, ok := token.Claims.(*TokenClaims) 107 claims, ok := token.Claims.(*TokenClaims)
108 if !ok || !token.Valid { 108 if !ok || !token.Valid {
109 return "", errors.New("token is not valid") 109 return "", errors.New("token is not valid")
110 } 110 }
111 111
112 claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix() 112 claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix()
113 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 113 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
114 114
115 newToken, err = jwtToken.SignedString([]byte(secret)) 115 newToken, err = jwtToken.SignedString([]byte(secret))
116 if err != nil { 116 if err != nil {
117 return "", err 117 return "", err
118 } 118 }
119 119
120 return newToken, nil 120 return newToken, nil
121 } 121 }
122 122
123 func ParseAPIToken(tokenString string) (*TokenClaims, error) { 123 func ParseAPIToken(tokenString string) (*TokenClaims, error) {
124 if ok := strings.HasPrefix(tokenString, "Bearer"); ok { 124 if ok := strings.HasPrefix(tokenString, "Bearer"); ok {
125 tokenString = strings.TrimPrefix(tokenString, "Bearer ") 125 tokenString = strings.TrimPrefix(tokenString, "Bearer ")
126 } else { 126 } else {
127 return &TokenClaims{}, errors.New("Authorization header is incomplete") 127 return &TokenClaims{}, errors.New("Authorization header is incomplete")
128 } 128 }
129 129
130 token, err := parseTokenFunc(tokenString) 130 token, err := parseTokenFunc(tokenString)
131 if err != nil { 131 if err != nil {
132 return &TokenClaims{}, err 132 return &TokenClaims{}, err
133 } 133 }
134 134
135 // type assertion 135 // type assertion
136 claims, ok := token.Claims.(*TokenClaims) 136 claims, ok := token.Claims.(*TokenClaims)
137 if !ok || !token.Valid { 137 if !ok || !token.Valid {
138 return &TokenClaims{}, errors.New("token is not valid") 138 return &TokenClaims{}, errors.New("token is not valid")
139 } 139 }
140 return claims, nil 140 return claims, nil
141 } 141 }
142 142
143 func parseTokenFunc(tokenString string) (*jwt.Token, error) { 143 func parseTokenFunc(tokenString string) (*jwt.Token, error) {
144 token, err := jwt.ParseWithClaims(tokenString, 144 token, err := jwt.ParseWithClaims(tokenString,
145 &TokenClaims{}, 145 &TokenClaims{},
146 func(token *jwt.Token) (interface{}, error) { 146 func(token *jwt.Token) (interface{}, error) {
147 return []byte(secret), nil 147 return []byte(secret), nil
148 }, 148 },
format_utility.go
1 package restutility 1 package restutility
2 2
3 import ( 3 import (
4 "time" 4 "time"
5 ) 5 )
6 6
7 func UnixToDate(input int64) time.Time { 7 func UnixToDate(unix int64) time.Time {
8 return time.Unix(input, 0) 8 return time.Unix(unix, 0)
9 } 9 }
10 10
11 func DateToUnix(input interface{}) int64 { 11 func DateToUnix(date interface{}) int64 {
12 if input != nil { 12 if date != nil {
13 t := input.(time.Time) 13 t := date.(time.Time)
14 return t.Unix() 14 return t.Unix()
15 15
16 } 16 }
17 return 0 17 return 0
18 } 18 }
19 19
20 func EqualQuotes(input string) string { 20 func EqualQuotes(stmt string) string {
21 if input != "" { 21 if stmt != "" {
22 return " = '" + input + "'" 22 stmt = " = '" + stmt + "'"
23 } 23 }
24 return "" 24 return stmt
25 } 25 }
26 26
27 func LikeQuotes(input string) string { 27 func LikeQuotes(stmt string) string {
28 if input != "" { 28 if stmt != "" {
29 return " LIKE UPPER('%" + input + "%')" 29 stmt " LIKE UPPER('%" + stmt + "%')"
30 } 30 }
31 return "" 31 return stmt
32 } 32 }
33 33
34 34
1 package restutility 1 package restutility
2 2
3 import ( 3 import (
4 "net/http" 4 "net/http"
5 "encoding/json" 5 "encoding/json"
6 ) 6 )
7 7
8 var _apiVersion = "/api/v1"
9 var _authEndPoint = "/token"
10
11 func SetApiVersion(ver string) string {
12 _apiVersion = ver
13 return _apiVersion
14 }
15
16 func SetAuthEndpoint(ep string) {
17 _authEndPoint = ep
18 }
19
20 const templateHttpErr500_EN = "An internal server error has occurred." 8 const templateHttpErr500_EN = "An internal server error has occurred."
21 const templateHttpErr500_RS = "Došlo je do greške na serveru." 9 const templateHttpErr500_RS = "Došlo je do greške na serveru."
22 const templateHttpErr400_EN = "Bad request: invalid request body." 10 const templateHttpErr400_EN = "Bad request: invalid request body."
23 const templateHttpErr400_RS = "Neispravan zahtev." 11 const templateHttpErr400_RS = "Neispravan zahtev."
12 const templateHttpErr401_EN = "Unauthorized request."
13 const templateHttpErr401_RS = "Neautorizovan zahtev."
24 14
25 type HttpError struct { 15 type httpError struct {
26 Error []HttpErrorDesc `json:"error"` 16 Error []HttpErrorDesc `json:"error"`
27 Request string `json:"request"` 17 Request string `json:"request"`
28 } 18 }
29 19
30 type HttpErrorDesc struct { 20 type HttpErrorDesc struct {
31 Lang string `json:"lang"` 21 Lang string `json:"lang"`
32 Desc string `json:"description"` 22 Desc string `json:"description"`
33 } 23 }
34 24
35 func RespondWithHttpError(w http.ResponseWriter, 25 func ErrorResponse(w http.ResponseWriter, r *http.Request, code int, desc []HttpErrorDesc) {
36 req *http.Request, 26 err := httpError{ desc, r.Method + " " + r.URL.Path }
37 code int,
38 httpErr []HttpErrorDesc) {
39
40 err := HttpError{
41 Error: httpErr,
42 Request: req.Method + " " + req.URL.Path,
43 }
44 w.WriteHeader(code) 27 w.WriteHeader(code)
45 json.NewEncoder(w).Encode(err) 28 json.NewEncoder(w).Encode(err)
46 } 29 }
47 30
48 func RespondWithHttpError400(w http.ResponseWriter, req *http.Request) { 31 func BadRequestResponse(w http.ResponseWriter, req *http.Request) {
49 RespondWithHttpError(w, req, http.StatusBadRequest, []HttpErrorDesc{ 32 ErrorResponse(w, req, http.StatusBadRequest, []HttpErrorDesc{
50 {Lang: "en", Desc: templateHttpErr400_EN}, 33 { "en", templateHttpErr400_EN },
51 {Lang: "rs", Desc: templateHttpErr400_RS}, 34 { "rs", templateHttpErr400_RS },
52 }) 35 })
53 } 36 }
54 37
55 func RespondWithHttpError500(w http.ResponseWriter, req *http.Request) { 38 func InternalServerErrorResponse(w http.ResponseWriter, req *http.Request) {
56 RespondWithHttpError(w, req, http.StatusInternalServerError, []HttpErrorDesc{ 39 ErrorResponse(w, req, http.StatusInternalServerError, []HttpErrorDesc{
57 {Lang: "en", Desc: templateHttpErr500_EN}, 40 { "en", templateHttpErr500_EN },
58 {Lang: "rs", Desc: templateHttpErr500_RS}, 41 { "rs", templateHttpErr500_RS },
59 }) 42 })
60 } 43 }
61 44
62 //TODO: Add parameters to enable/disable roles authorization checks 45 func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) {
46 ErrorResponse(w, req, http.StatusUnauthorized, []HttpErrorDesc{
47 { "en", templateHttpErr500_EN },
48 { "rs", templateHttpErr500_RS },
49 })
50 }
51
52 // TODO: Add parameters to enable/disable roles authorization checks
53 // TODO: Check for content type
63 // Sets common headers and checks for token validity. 54 // Sets common headers and checks for token validity.
64 func HttpPreProc(handlerFunc http.HandlerFunc, authEnabled bool) http.HandlerFunc { 55 func WrapHandler(handlerFunc http.HandlerFunc, needauth bool) http.HandlerFunc {
65 return func(w http.ResponseWriter, req *http.Request) { 56 return func(w http.ResponseWriter, req *http.Request) {
66 // @TODO: check Content-type header (must be application/json)
67 // ctype := w.Header.Get("Content-Type")
68 // if req.Method != "GET" && ctype != "application/json" {
69 // replyWithHttpError(w, req, http.StatusBadRequest,
70 // "Not a supported content type: " + ctype)
71 // }
72
73 w.Header().Set("Access-Control-Allow-Origin", "*") 57 w.Header().Set("Access-Control-Allow-Origin", "*")
58
74 w.Header().Set("Access-Control-Allow-Methods", 59 w.Header().Set("Access-Control-Allow-Methods",
75 `POST, 60 "POST, GET, PUT, DELETE, OPTIONS")
76 GET, 61
77 PUT,
78 DELETE,
79 OPTIONS`)
80 w.Header().Set("Access-Control-Allow-Headers", 62 w.Header().Set("Access-Control-Allow-Headers",
81 `Accept, 63 "Accept, Content-Type, Content-Length, "
82 Content-Type, 64 "Accept-Encoding, X-CSRF-Token, Authorization")
83 Content-Length, 65
84 Accept-Encoding,
85 X-CSRF-Token,
86 Authorization`)
87 w.Header().Set("Content-Type", "application/json; charset=utf-8") 66 w.Header().Set("Content-Type", "application/json; charset=utf-8")
88 67
89 if req.Method == "OPTIONS" { 68 if req.Method == "OPTIONS" {
90 return 69 return
91 } 70 }
92 71
93 if authEnabled { 72 if needauth {
94 if req.URL.Path != _apiVersion + _authEndPoint { 73 token := req.Header.Get("Authorization")
95 token := req.Header.Get("Authorization") 74 if _, err := ParseAPIToken(token); err != nil {
96 if _, err := ParseAPIToken(token); err != nil { 75 UnathorizedResponse(w, req, http.StatusUnauthorized)
97 RespondWithHttpError(w, req, http.StatusUnauthorized, 76 return
98 []HttpErrorDesc{
99 {Lang: "en", Desc: "Unauthorized request."},
100 {Lang: "rs", Desc: "Neautorizovani zahtev."},
101 })
102 return
103 }
104 } 77 }
105 } 78 }
106 79
107 err := req.ParseForm() 80 err := req.ParseForm()
108 if err != nil { 81 if err != nil {
109 RespondWithHttpError400(w, req) 82 BadRequestResponse(w, req)
110 return 83 return
111 } 84 }
112 85
113 // execute HandlerFunc 86 // execute HandlerFunc
1 package restutility 1 package restutility
2 2
3 import ( 3 import (
4 "gopkg.in/rana/ora.v3" 4 "gopkg.in/rana/ora.v3"
5 ) 5 )
6 6
7 type SelectConfig struct { 7 type SelectConfig struct {
8 ListObjType string `json:"listObjectType"` 8 ListObjType string `json:"listObjectType"`
9 ObjType string `json:"objectType"` 9 ObjType string `json:"objectType"`
10 Type string `json:"type"` 10 Type string `json:"type"`
11 IdField string `json:"idField"` 11 IdField string `json:"idField"`
12 LabelField string `json:"labelField"` 12 LabelField string `json:"labelField"`
13 ValueField string `json:"valueField"` 13 ValueField string `json:"valueField"`
14 } 14 }
15 15
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 a.LIST_OBJECT_TYPE, a.OBJECT_TYPE, a.ID_FIELD,
21 a.LABEL_FIELD, a.TYPE, b.FIELD 21 a.LABEL_FIELD, a.TYPE, b.FIELD
22 FROM LIST_SELECT_CONFIG a, LIST_VALUE_FIELD b 22 FROM LIST_SELECT_CONFIG a, LIST_VALUE_FIELD b
23 WHERE a.LIST_OBJECT_TYPE` + otype + ` 23 WHERE a.LIST_OBJECT_TYPE` + otype + `
24 AND b.LIST_TYPE = a.LIST_OBJECT_TYPE 24 AND b.LIST_TYPE = a.LIST_OBJECT_TYPE
25 AND b.OBJECT_TYPE = a.OBJECT_TYPE` 25 AND b.OBJECT_TYPE = a.OBJECT_TYPE`
26 26
27 stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S, ora.S, 27 stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S, ora.S, ora.S)
28 ora.S)
29 defer stmt.Close() 28 defer stmt.Close()
30 if err != nil { 29 if err != nil {
31 return nil, err 30 return nil, err
32 } 31 }
33 32
34 rset, err := stmt.Qry() 33 rset, err := stmt.Qry()
35 if err != nil { 34 if err != nil {
36 return nil, err 35 return nil, err
37 } 36 }
38 for rset.Next() { 37 for rset.Next() {
39 resp = append(resp, SelectConfig{ 38 resp = append(resp, SelectConfig{
40 ListObjType: rset.Row[0].(string), 39 ListObjType: rset.Row[0].(string),
41 ObjType: rset.Row[1].(string), 40 ObjType: rset.Row[1].(string),
42 IdField: rset.Row[2].(string), 41 IdField: rset.Row[2].(string),
43 LabelField: rset.Row[3].(string), 42 LabelField: rset.Row[3].(string),
44 Type: rset.Row[4].(string), 43 Type: rset.Row[4].(string),
45 ValueField: rset.Row[5].(string), 44 ValueField: rset.Row[5].(string),
46 }) 45 })
47 } 46 }
48 if rset.Err != nil { 47 if rset.Err != nil {
49 return nil, rset.Err 48 return nil, rset.Err
50 } 49 }
51 50
52 return resp, nil 51 return resp, nil
53
54 } 52 }
55 53