-
Notifications
You must be signed in to change notification settings - Fork 0
/
participant.go
295 lines (260 loc) · 9.85 KB
/
participant.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
package participant
import (
"fmt"
"log"
"net/http"
"strconv"
"github.com/cliffordlab/amoss_services/capacity"
"github.com/cliffordlab/amoss_services/database"
"github.com/cliffordlab/amoss_services/log_writer"
"github.com/cliffordlab/amoss_services/mathb"
"github.com/cliffordlab/amoss_services/support"
)
const (
duplicateUserErr = `{"error":"cannot create a duplicate participant"}`
noSuchUserErr = `{"error":"invalid participant id or password"}`
getEmail = `SELECT encrypted_phone FROM participants WHERE participant_id=$1`
getIV = `SELECT phone_iv FROM participants WHERE participant_id=$1`
insertAdmin = `INSERT INTO participants (participant_id, password_hash, password_salt, capacity_id)
VALUES ($1, $2, $3, (SELECT capacity_id FROM participant_capacity WHERE capacity_id=$4))`
insertParticipant = `INSERT INTO participants (participant_id, password_hash, password_salt, capacity_id, study_id)
VALUES ($1, $2, $3, (SELECT capacity_id FROM participant_capacity WHERE capacity_id=$4),
(SELECT study_id FROM studies WHERE study_id=$5))`
insertMoyoParticipant = `INSERT INTO participants (participant_id, password_hash, password_salt, capacity_id, study_id, email_hash, encryption_iv, encrypted_email, encrypted_phone, phone_iv, is_consented)
VALUES ($1, $2, $3, (SELECT capacity_id FROM participant_capacity WHERE capacity_id=$4),
(SELECT study_id FROM studies WHERE study_id=$5),
$6, $7, $8, $9, $10, $11)`
selectMoyoParticipant = `SELECT email_hash FROM participants where (email_hash) = ($1)`
alterParticipant = `UPDATE participants SET (password_hash, password_salt) = ($1, $2) WHERE participant_id = $3;`
insertAccessToken = `UPDATE participants SET access_token = $1 WHERE participant_id = $2;`
)
//Participant data type of users interacting with application
type Participant struct {
EmailEncoded []byte
Email string
Phone []byte
ID int64
Capacity string
Salt string
PasswordHash string
Study string
IV []byte
PhoneIV []byte
EmailHash string
Password string
HasConsented bool
}
//Salt gets the salt for the participant
func Salt(currentParticipant *Participant, ptID int64, w http.ResponseWriter) {
log.Println("Querying db for participant's salt... ")
w = log_writer.LogWriter{ResponseWriter: w}
query := "SELECT password_salt, capacity_id, study_id FROM participants WHERE participant_id = $1"
rows, err := database.ADB.Db.Query(query, ptID)
if err != nil {
log.Println("failed to execute get salt query statement")
log.Println(err)
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
w.Write([]byte(noSuchUserErr))
return
}
var salt string
var participantCapacity string
var study string
for rows.Next() {
err = rows.Scan(&salt, &participantCapacity, &study)
if err != nil {
log.Println("failed to scan row for salt")
log.Println(err)
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
w.Write([]byte(noSuchUserErr))
return
}
}
rows.Close()
currentParticipant.Salt = salt
currentParticipant.Capacity = participantCapacity
currentParticipant.Study = study
if salt == "" {
log.Println("Salt is nil in database for participant")
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
w.Write([]byte(noSuchUserErr))
return
}
}
//Password get current participants password
func Password(ptID int64, currentParticipant *Participant) {
log.Println("Querying for participants password...")
query := "SELECT password_hash FROM participants WHERE participant_id = $1"
rows, err := database.ADB.Db.Query(query, ptID)
if err != nil {
log.Println("failed to execute get password query")
log.Println(err)
return
}
var passwordHash string
for rows.Next() {
err = rows.Scan(&passwordHash)
if err != nil {
log.Println("failed to scan row for password hash")
log.Println(err)
}
}
rows.Close()
currentParticipant.PasswordHash = passwordHash
}
//LoginParticipant check if participant creds match what
//is in the database
func LoginParticipant(currentParticipant *Participant, w http.ResponseWriter) {
w = log_writer.LogWriter{ResponseWriter: w}
token := capacity.CreateAccessToken(currentParticipant.Capacity, currentParticipant.Study, currentParticipant.ID)
saveTokenToDb(currentParticipant, token, w)
tokenResponse := fmt.Sprintf(`{"token":"%s", "capacity":"%s", "participantID":"%v", "study":"%s", "isConsented":"%v"}`, token, currentParticipant.Capacity, currentParticipant.ID, currentParticipant.Study, currentParticipant.HasConsented)
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
w.Write([]byte(tokenResponse))
}
func saveTokenToDb(currentParticipant *Participant, token string, w http.ResponseWriter) {
w = log_writer.LogWriter{ResponseWriter: w}
log.Println("Ready to insert token")
stmt, err := database.ADB.Db.Prepare(insertAccessToken)
if err != nil {
log.Println("failed to prepare insert access token statement")
log.Fatalln(err)
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
w.Write([]byte(noSuchUserErr))
}
defer stmt.Close()
ptidString := strconv.Itoa(int(currentParticipant.ID))
rows, err := stmt.Query(token, ptidString)
if err != nil {
log.Println("failed to execute query statement. participant id error.")
log.Println(err)
http.Error(w, "Invalid email address.", http.StatusUnauthorized)
return
}
rows.Close()
log.Println("Access token inserted to db successfully")
}
//CreateAdmin insert participant into database
func CreateAdmin(admin Participant, w http.ResponseWriter) {
w = log_writer.LogWriter{ResponseWriter: w}
stmt, err := database.ADB.Db.Prepare(insertAdmin)
if err != nil {
log.Println("failed to prepare create admin statement")
log.Fatalln(err)
}
defer stmt.Close()
rows, err := stmt.Query(admin.ID, admin.PasswordHash, admin.Salt, admin.Capacity)
if err != nil {
log.Println("failed to execute query statement")
log.Println(err)
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
w.Write([]byte(duplicateUserErr))
return
}
rows.Close()
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
w.Write([]byte("{\"success\":\"admin participant created\"}"))
}
//CreateNonAdmin insert participant with coordinator privileges into database
func CreateNonAdmin(pt Participant, w http.ResponseWriter) {
w = log_writer.LogWriter{ResponseWriter: w}
stmt, err := database.ADB.Db.Prepare(insertParticipant)
if err != nil {
log.Println("failed to prepare create participant statement")
log.Fatalln(err)
}
defer stmt.Close()
rows, err := stmt.Query(pt.ID, pt.PasswordHash, pt.Salt, pt.Capacity, pt.Study)
if err != nil {
log.Println("failed to execute query statement")
log.Println(err)
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
w.WriteHeader(http.StatusOK)
w.Write([]byte(duplicateUserErr))
return
}
rows.Close()
response := fmt.Sprintf("{\"success\":\"%s participant created\"}", pt.Capacity)
log.Println("participant created successfully")
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
w.WriteHeader(http.StatusCreated)
w.Write([]byte(response))
}
var counter int
//CreateNonAdmin insert participant with coordinator privileges into database
func CreateMoyoNonAdmin(pt Participant, w http.ResponseWriter) (int int64, email string, err error) {
log.Printf("Creating Moyo non admin....")
//Find unused participant ID in database
//Generate random 10 digit numerical number
//rand.NewSource(time.Now().UnixNano())
pid := mathb.RandInt(1000000000, 9999999999)
log.Println(pid)
//Attempt to insert into DB
pt.ID = pid
log.Println("-----------------")
log.Println("this is the encrypted email before db insert: ")
fmt.Println(pt.EmailEncoded)
fmt.Println(string(pt.EmailEncoded))
fmt.Printf("%x", pt.EmailEncoded)
log.Println("-----------------")
stmt, err := database.ADB.Db.Prepare(insertMoyoParticipant)
if err != nil {
log.Println("failed to prepare create participant statement")
log.Fatalln(err)
}
defer stmt.Close()
rows, err := stmt.Query(pt.ID, pt.PasswordHash, pt.Salt, pt.Capacity, pt.Study, pt.EmailHash, pt.IV, pt.EmailEncoded, pt.Phone, pt.PhoneIV, true)
if err != nil {
log.Println("failed to execute query statement")
log.Println(err)
return 0, "failed", err
}
rows.Close()
log.Println("participant created successfully. Participant ID: " + string(pt.ID))
// send email with participant ID and password if everything is successful
emailStatus := support.EmailMoyoParticipant(pt.Email, pt.ID, pt.Password, w)
//decrypt(w, pt)
return pid, emailStatus, nil
}
func IsParticipantInDB(pt Participant) (isRegistered bool) {
log.Printf("Check if email is already in system...")
stmt, err := database.ADB.Db.Prepare(selectMoyoParticipant)
if err != nil {
log.Println("failed to prepare create participant statement")
log.Fatalln(err)
}
defer stmt.Close()
rows, err := stmt.Query(pt.EmailHash)
if err != nil {
log.Println("failed to execute query statement")
log.Fatalln(err)
}
var emailHash string
for rows.Next() {
err = rows.Scan(&emailHash)
if err != nil {
log.Println("failed to scan row for email hash")
log.Println(err)
return
}
}
rows.Close()
return pt.EmailHash == emailHash
}
//AlterSaltAndPasswordHash to protect against hacking
func AlterSaltAndPasswordHash(currentParticipant *Participant) {
log.Println("Ready to alter salt and pwHash")
stmt, err := database.ADB.Db.Prepare(alterParticipant)
if err != nil {
log.Println("failed to prepare alter participant statement")
log.Fatalln(err)
}
defer stmt.Close()
rows, err := stmt.Query(currentParticipant.PasswordHash, currentParticipant.Salt, currentParticipant.ID)
if err != nil {
log.Println("failed to execute query statement")
log.Println(err)
return
}
rows.Close()
}