forked from Sifr-Labs-2020-Interns/IOE-electricity-cost
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
374 lines (276 loc) · 10.8 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
package main
import (
"database/sql"
"encoding/json"
"fmt"
"math/rand"
"os"
"time"
"unicode"
"unsafe"
"github.com/Sifr-Labs-2020-Interns/IOE-electricity-cost/connection"
"golang.org/x/crypto/bcrypt"
"github.com/go-macaron/binding"
macaron "gopkg.in/macaron.v1"
)
//Connection object
var conn *sql.DB
/* Taken from https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go
Accessed 17/06/2020. Line 16 - 24 and function getRandomString has been taken from the source specified above.
*/
var src = rand.NewSource(time.Now().UnixNano())
// This is the character set we will be using to build our key.
const charSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
const (
charIndexBits = 6 // 6 bits to represent a character index (since we have 62 characters)
charIndexMask = 1<<charIndexBits - 1 // All 1-bits, as many as charIndexBits
charIndexMax = 63 / charIndexBits // Number of character indices fitting in 63 bits
)
/*----------------------------------------- Struct for Post request ------------------------------------------- */
//Post request parameters for route add user
//NewUser is a type that models a user to be added to the database
type NewUser struct {
Name string `form: "name" binding:"Required"`
Username string `form: "username" binding:"Required"`
Password string `form: "password" binding:"Required"`
EmailID string `form:"email_id" binding:"Required"`
AdminKey string `form: "admin_key" binding:"Required"`
}
//Post request parameters for route to remove user
//RemoveUser is models a user to be removed from the database
type RemoveUser struct {
Username string `form: "username" binding:"Required"`
EmailID string `form:"email_id" binding:"Required"`
AdminKey string `form: "admin_key" binding:"Required"`
}
//Post request parameters for route to Add transaction
//AddTransaction models a transaction that is added to the database
type AddTransaction struct {
UserKey string `form: "admin_key" binding:"Required"`
Watts string `form: "Watts" binding:"Required"`
Type string `form: "Type" binding:"Required"`
}
/*-----------------------------------------------------------------------------------------------------------------*/
/* ----------------------------------------------------------------------------------------
------------------------------------ MAIN FUNCTION ---------------------------------------
------------------------------------------------------------------------------------------*/
func main() {
argsWithoutProg := os.Args[1:]
// Getting database information from arguments
db_username := argsWithoutProg[0]
db_password := argsWithoutProg[1]
db := argsWithoutProg[2]
port_no := argsWithoutProg[3]
// database connection
conn = connection.ConnectToDB(db_username, db_password, db, port_no)
if conn == nil {
panic("Database Connection Failed")
}
m := macaron.Classic()
// Public files
m.Use(macaron.Static("public"))
// All routes
m.Post("/adduser", binding.Bind(NewUser{}), adduser)
m.Post("/removeuser", binding.Bind(RemoveUser{}), removeuser)
m.Post("/addtransaction", binding.Bind(AddTransaction{}), addtransaction)
m.Run()
}
/*-----------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------------------------
------------------------------------- HELPER FUNCTIONS ----------------------------------------
---------------------------------------------------------------------------------------------*/
/* A function that converts a map[string]string into a JSON string */
func convertToJSON(data map[string]string) string {
result, _ := json.Marshal(data)
return (string(result))
}
/* A function that checks if the provided admin key is valid or not.
An admin key is valid only if it is alpha numeric (no special characters) and atleast
500 characters long.
Used https://stackoverflow.com/questions/38554353/how-to-check-if-a-string-only-contains-alphabetic-characters-in-go (17/06/2020)
for reference
NOT USED : To be used later
*/
func isValidKey(key string) bool {
if len(key) < 500 {
return false
}
// Flags to check if the string that's read contains alphabets, letters and no special characters.
alphaFlag := false
numFlag := false
specialCharFlag := false
for _, c := range key {
if c >= 48 && c <= 57 {
numFlag = true
} else if (c >= 65 && c <= 90) || (c >= 97 && c <= 122) {
alphaFlag = true
} else {
specialCharFlag = true
}
if specialCharFlag {
return false
}
}
if !numFlag || !alphaFlag {
return false
}
return true
}
/* Checks if the value of a field exists in the database based on the query string */
func isValid(value string, query string) bool {
result, err := conn.Query(query, value)
if err != nil {
panic(err.Error())
}
if result.Next() {
var count int
// for each row, scan the result into our tag composite object
err = result.Scan(&count)
if err != nil {
panic(err.Error()) // proper error handling instead of panic in your app
}
if count == 1 {
return true
}
}
return false
}
// A function that return a random string containing alpha numeric characters
func getRandomString(n int) string {
numFlag := false // A flag to keep track if the string we're creating has a number in it.
b := make([]byte, n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := n-1, src.Int63(), charIndexMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), charIndexMax
}
if index := int(cache & charIndexMask); index < len(charSet) {
b[i] = charSet[index]
} else {
b[i] = charSet[index-2] // In case index gets a value of 63 or 62, decrement it by 2
}
if !numFlag { // If numFlag is false
// If the rune of this byte is a digit, set numFlag to true
if unicode.IsDigit(rune(b[i])) {
numFlag = true
}
}
i--
cache >>= charIndexBits
remain--
}
// In the event that the string we created doesn't have any number in it
if numFlag == false {
b[5] = charSet[1] // set the 10th character in the string to the number 1
}
return *(*string)(unsafe.Pointer(&b))
}
/*-----------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------------------------
------------------------------- FUNCTION TO HANDLE ROUTES ------------------------------------
---------------------------------------------------------------------------------------------*/
// TODO: add a new user
func adduser(newuser NewUser) string {
// Get user from post request
name := newuser.Name
username := newuser.Username
password := newuser.Password
emailID := newuser.EmailID
adminKey := newuser.AdminKey
//Hashing the password
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
if err != nil {
panic(err.Error())
}
password = string(bytes)
userKey := "null" // the auto generated key
result := "null"
/* Check if the admin key is valid
|___ is valid
|___ Generate key for user and insert data to the database and return JSON success with
user key
|___ not valid
|___ Returns in json error admin key not valid
*/
if isValid(adminKey, "select count(admin_key) as admin from admins where admin_key=?") {
userKey = getRandomString(500)
// Checking if the user exists
if isValid(username, "select count(username) as users from users where username=?") {
result = convertToJSON(map[string]string{"Error": "Username " + username + " is already taken"})
return (string(result))
}
result = convertToJSON(map[string]string{"Generated Key": userKey, "Status": "Success"})
// Taken from https://www.golangprograms.com/example-of-golang-crud-using-mysql-from-scratch.html (Accessed 19/06/2020)
query, err := conn.Prepare("INSERT INTO users (`NAME`,`EMAIL_ID`, `USERNAME`, `PASSWORD`,`USER_KEY`) VALUES(?,?,?,?,?)")
if err != nil {
panic(err.Error())
}
query.Exec(name, emailID, username, password, userKey)
} else {
result = convertToJSON(map[string]string{"Error": "Admin key not valid"})
}
return result
}
// TODO: remove user
func removeuser(removeuser RemoveUser) string {
// Get user information from post request - UNCOMMENT WHEN USING THE VARIABLES
Username := removeuser.Username
AdminKey := removeuser.AdminKey
result := "null"
/* Check if the admin key is valid
|___ is valid
|___ Remove user from database and return success
|___ not valid
|___ Returns in json error admin key not valid
*/
if isValid(AdminKey, "select count(admin_key) as admin from admins where admin_key=?") {
// Taken from https://www.golangprograms.com/example-of-golang-crud-using-mysql-from-scratch.html (Accessed 19/06/2020)
query, err := conn.Prepare("DELETE FROM users WHERE username = ?")
if err != nil {
panic(err.Error())
}
query.Exec(Username)
result = convertToJSON(map[string]string{"Status": "Successfully removed " + Username})
} else {
result = convertToJSON(map[string]string{"Error": "Admin key not valid"})
}
return result
}
// TODO: Add transaction
func addtransaction(ctx *macaron.Context, addtransaction AddTransaction) string {
// Get information to add transaaction
UserKey := addtransaction.UserKey
Watts := addtransaction.Watts
Type := addtransaction.Type
mResponse := map[string]string{}
jResponse := []byte{}
/* Check if the user key is valid
|___ is valid
|___ Add the transaction information in the database and return JSON success
|___ not valid
|___ Returns in json error user key not valid
*/
if isValid(UserKey, "select count(user_key) as users from users where user_key=?") {
query, err := conn.Prepare("INSERT INTO transactions (`USER_ID`, `WATT/SECOND`,`TYPE`) SELECT user_id,?,? FROM users WHERE user_key=?")
if err != nil {
panic(err.Error())
}
// query.Exec(User_key, Watts, Type)
query.Exec(Watts, Type, UserKey)
fmt.Println("Transaction done")
if err != nil {
panic(err.Error())
}
} else {
mResponse = map[string]string{"Error": "User key does not exist"}
jResponse, _ = json.Marshal(mResponse)
}
return string(jResponse)
}
/*-----------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------*/