Files
databunker/src/agreements_api.go
2024-08-18 22:39:38 +03:00

492 lines
13 KiB
Go

package main
import (
"fmt"
"log"
"net/http"
"reflect"
"github.com/julienschmidt/httprouter"
//"go.mongodb.org/mongo-driver/bson"
)
func (e mainEnv) agreementAccept(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
identity := ps.ByName("identity")
brief := ps.ByName("brief")
mode := ps.ByName("mode")
event := audit("accept agreement by "+brief, identity, mode, identity)
defer func() { event.submit(e.db, e.conf) }()
if validateMode(mode) == false {
returnError(w, r, "bad mode", 405, nil, event)
return
}
brief = normalizeBrief(brief)
if isValidBrief(brief) == false {
returnError(w, r, "bad brief format", 405, nil, event)
return
}
exists, err := e.db.checkLegalBasis(brief)
if err != nil {
returnError(w, r, "internal error", 405, err, event)
return
}
if exists == false {
returnError(w, r, "not found", 404, nil, event)
return
}
userTOKEN := ""
if mode == "token" {
if enforceUUID(w, identity, event) == false {
return
}
userBson, err := e.db.lookupUserRecord(identity)
if err != nil || userBson == nil {
returnError(w, r, "internal error", 405, err, event)
return
}
if e.enforceAuth(w, r, event) == "" {
return
}
userTOKEN = identity
} else {
userBson, err := e.db.lookupUserRecordByIndex(mode, identity, e.conf)
if err != nil {
returnError(w, r, "internal error", 405, err, event)
return
}
if userBson != nil {
userTOKEN = userBson["token"].(string)
event.Record = userTOKEN
} else {
if mode == "login" {
returnError(w, r, "internal error", 405, nil, event)
return
}
// else user not found - we allow to save consent for unlinked users!
}
}
records, err := getJSONPostMap(r)
if err != nil {
returnError(w, r, "failed to decode request body", 405, err, event)
return
}
starttime := int32(0)
expiration := int32(0)
referencecode := getStringValue(records["referencecode"])
lastmodifiedby := getStringValue(records["lastmodifiedby"])
agreementmethod := getStringValue(records["agreementmethod"])
status := getStringValue(records["status"])
if len(status) == 0 {
status = "yes"
} else {
status = normalizeConsentStatus(status)
}
if value, ok := records["expiration"]; ok {
switch records["expiration"].(type) {
case string:
expiration, _ = parseExpiration(value.(string))
case float64:
expiration = int32(value.(float64))
}
}
if value, ok := records["starttime"]; ok {
switch records["starttime"].(type) {
case string:
starttime, _ = parseExpiration(value.(string))
case float64:
starttime = int32(value.(float64))
}
}
switch mode {
case "email":
identity = normalizeEmail(identity)
case "phone":
identity = normalizePhone(identity, e.conf.Sms.DefaultCountry)
}
log.Printf("Processing agreement, status: %s\n", status)
e.db.acceptAgreement(userTOKEN, mode, identity, brief, status, agreementmethod,
referencecode, lastmodifiedby, starttime, expiration)
/*
notifyURL := e.conf.Notification.NotificationURL
if newStatus == true && len(notifyURL) > 0 {
// change notificate on new record or if status change
if len(userTOKEN) > 0 {
notifyConsentChange(notifyURL, brief, status, "token", userTOKEN)
} else {
notifyConsentChange(notifyURL, brief, status, mode, identity)
}
}
*/
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
w.Write([]byte(`{"status":"ok"}`))
}
func (e mainEnv) agreementWithdraw(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
identity := ps.ByName("identity")
brief := ps.ByName("brief")
mode := ps.ByName("mode")
event := audit("withdraw agreement by "+brief, identity, mode, identity)
defer func() { event.submit(e.db, e.conf) }()
if validateMode(mode) == false {
returnError(w, r, "bad mode", 405, nil, event)
return
}
brief = normalizeBrief(brief)
if isValidBrief(brief) == false {
returnError(w, r, "bad brief format", 405, nil, event)
return
}
lbasis, err := e.db.getLegalBasis(brief)
if err != nil {
returnError(w, r, "internal error", 405, err, event)
return
}
if lbasis == nil {
returnError(w, r, "not found", 405, nil, event)
return
}
userTOKEN := ""
authResult := ""
if mode == "token" {
if enforceUUID(w, identity, event) == false {
return
}
userBson, _ := e.db.lookupUserRecord(identity)
if userBson == nil {
returnError(w, r, "internal error", 405, nil, event)
return
}
authResult = e.enforceAuth(w, r, event)
if authResult == "" {
return
}
userTOKEN = identity
} else {
// TODO: decode url in code!
userBson, _ := e.db.lookupUserRecordByIndex(mode, identity, e.conf)
if userBson != nil {
userTOKEN = userBson["token"].(string)
event.Record = userTOKEN
} else {
if mode == "login" {
returnError(w, r, "internal error", 405, nil, event)
return
}
// else user not found - we allow to save consent for unlinked users!
}
}
records, err := getJSONPostMap(r)
if err != nil {
returnError(w, r, "failed to decode request body", 405, err, event)
return
}
lastmodifiedby := getStringValue(records["lastmodifiedby"])
selfService := false
if value, ok := lbasis["usercontrol"]; ok {
if reflect.TypeOf(value).Kind() == reflect.Bool {
selfService = value.(bool)
} else {
num := value.(int32)
if num > 0 {
selfService = true
}
}
}
if selfService == false {
// user can change consent only for briefs defined in self-service
if len(authResult) == 0 {
if e.enforceAdmin(w, r) == "" {
return
}
}
}
if authResult == "login" && selfService == false {
rtoken, rstatus, err := e.db.saveUserRequest("agreement-withdraw", userTOKEN, "", brief, nil, e.conf)
if err != nil {
returnError(w, r, "internal error", 405, err, event)
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
fmt.Fprintf(w, `{"status":"ok","result":"%s","rtoken":"%s"}`, rstatus, rtoken)
return
}
switch mode {
case "email":
identity = normalizeEmail(identity)
case "phone":
identity = normalizePhone(identity, e.conf.Sms.DefaultCountry)
}
e.db.withdrawAgreement(userTOKEN, brief, mode, identity, lastmodifiedby)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
w.Write([]byte(`{"status":"ok"}`))
notifyURL := e.conf.Notification.NotificationURL
if len(userTOKEN) > 0 {
notifyConsentChange(notifyURL, brief, "no", "token", userTOKEN)
} else {
notifyConsentChange(notifyURL, brief, "no", mode, identity)
}
}
func (e mainEnv) agreementRevokeAll(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
brief := ps.ByName("brief")
authResult := e.enforceAdmin(w, r)
if authResult == "" {
return
}
if e.enforceAdmin(w, r) == "" {
return
}
brief = normalizeBrief(brief)
if isValidBrief(brief) == false {
returnError(w, r, "bad brief format", 405, nil, nil)
return
}
exists, err := e.db.checkLegalBasis(brief)
if err != nil {
returnError(w, r, "internal error", 405, nil, nil)
return
}
if exists == false {
returnError(w, r, "not found", 405, nil, nil)
return
}
e.db.revokeLegalBasis(brief)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
w.Write([]byte(`{"status":"ok"}`))
}
func (e mainEnv) getUserAgreements(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
identity := ps.ByName("identity")
mode := ps.ByName("mode")
event := audit("privacy agreements for "+mode, identity, mode, identity)
defer func() { event.submit(e.db, e.conf) }()
if validateMode(mode) == false {
returnError(w, r, "bad mode", 405, nil, event)
return
}
userTOKEN := ""
if mode == "token" {
if enforceUUID(w, identity, event) == false {
return
}
userBson, _ := e.db.lookupUserRecord(identity)
if userBson == nil {
returnError(w, r, "internal error", 405, nil, event)
return
}
if e.enforceAuth(w, r, event) == "" {
return
}
userTOKEN = identity
} else {
// TODO: decode url in code!
userBson, _ := e.db.lookupUserRecordByIndex(mode, identity, e.conf)
if userBson != nil {
userTOKEN = userBson["token"].(string)
event.Record = userTOKEN
if e.enforceAuth(w, r, event) == "" {
return
}
} else {
if mode == "login" {
returnError(w, r, "internal error", 405, nil, event)
return
}
// else user not found - we allow to save consent for unlinked users!
}
}
// make sure that user is logged in here, unless he wants to cancel emails
if e.enforceAuth(w, r, event) == "" {
return
}
var resultJSON []byte
var numRecords int
var err error
if len(userTOKEN) > 0 {
resultJSON, numRecords, err = e.db.listAgreementRecords(userTOKEN)
} else {
resultJSON, numRecords, err = e.db.listAgreementRecordsByIdentity(identity)
}
if err != nil {
returnError(w, r, "internal error", 405, err, event)
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
str := fmt.Sprintf(`{"status":"ok","total":%d,"rows":%s}`, numRecords, resultJSON)
w.Write([]byte(str))
}
func (e mainEnv) getUserAgreement(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
identity := ps.ByName("identity")
brief := ps.ByName("brief")
mode := ps.ByName("mode")
event := audit("privacy agreements for "+mode, identity, mode, identity)
defer func() { event.submit(e.db, e.conf) }()
if validateMode(mode) == false {
returnError(w, r, "bad mode", 405, nil, event)
return
}
brief = normalizeBrief(brief)
if isValidBrief(brief) == false {
returnError(w, r, "bad brief format", 405, nil, event)
return
}
exists, err := e.db.checkLegalBasis(brief)
if err != nil {
returnError(w, r, "internal error", 405, err, event)
return
}
if exists == false {
returnError(w, r, "not found", 404, nil, event)
}
userTOKEN := ""
if mode == "token" {
if enforceUUID(w, identity, event) == false {
return
}
userBson, _ := e.db.lookupUserRecord(identity)
if userBson == nil {
returnError(w, r, "internal error", 405, nil, event)
return
}
if e.enforceAuth(w, r, event) == "" {
return
}
userTOKEN = identity
} else {
// TODO: decode url in code!
userBson, _ := e.db.lookupUserRecordByIndex(mode, identity, e.conf)
if userBson != nil {
userTOKEN = userBson["token"].(string)
event.Record = userTOKEN
if e.enforceAuth(w, r, event) == "" {
return
}
} else {
if mode == "login" {
returnError(w, r, "internal error", 405, nil, event)
return
}
// else user not found - we allow to save consent for unlinked users!
}
}
// make sure that user is logged in here, unless he wants to cancel emails
if e.enforceAuth(w, r, event) == "" {
return
}
var resultJSON []byte
resultJSON, err = e.db.viewAgreementRecord(userTOKEN, brief)
if err != nil {
returnError(w, r, "internal error", 405, err, event)
return
}
if resultJSON == nil {
returnError(w, r, "not found", 405, err, event)
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
str := fmt.Sprintf(`{"status":"ok","data":%s}`, resultJSON)
w.Write([]byte(str))
}
/*
func (e mainEnv) consentUserRecord(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
identity := ps.ByName("identity")
brief := ps.ByName("brief")
mode := ps.ByName("mode")
event := audit("consent record for "+brief, identity, mode, identity)
defer func() { event.submit(e.db, e.conf) }()
if validateMode(mode) == false {
returnError(w, r, "bad mode", 405, nil, event)
return
}
brief = normalizeBrief(brief)
if isValidBrief(brief) == false {
returnError(w, r, "bad brief format", 405, nil, event)
return
}
userTOKEN := identity
var userBson bson.M
if mode == "token" {
if enforceUUID(w, identity, event) == false {
return
}
userBson, _ = e.db.lookupUserRecord(identity)
} else {
userBson, _ = e.db.lookupUserRecordByIndex(mode, identity, e.conf)
if userBson != nil {
userTOKEN = userBson["token"].(string)
event.Record = userTOKEN
}
}
if userBson == nil {
returnError(w, r, "internal error", 405, nil, event)
return
}
// make sure that user is logged in here, unless he wants to cancel emails
if e.enforceAuth(w, r, event) == "" {
return
}
resultJSON, err := e.db.viewConsentRecord(userTOKEN, brief)
if err != nil {
returnError(w, r, "internal error", 405, err, event)
return
}
if resultJSON == nil {
returnError(w, r, "not found", 405, nil, event)
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
str := fmt.Sprintf(`{"status":"ok","data":%s}`, resultJSON)
w.Write([]byte(str))
}
*/
/*
func (e mainEnv) consentFilterRecords(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
brief := ps.ByName("brief")
event := audit("consent get all for "+brief, brief, "brief", brief)
defer func() { event.submit(e.db, e.conf) }()
if e.enforceAuth(w, r, event) == "" {
return
}
var offset int32
var limit int32 = 10
args := r.URL.Query()
if value, ok := args["offset"]; ok {
offset = atoi(value[0])
}
if value, ok := args["limit"]; ok {
limit = atoi(value[0])
}
resultJSON, numRecords, err := e.db.filterConsentRecords(brief, offset, limit)
if err != nil {
returnError(w, r, "internal error", 405, err, event)
return
}
log.Printf("Total count of rows: %d\n", numRecords)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(200)
str := fmt.Sprintf(`{"status":"ok","total":%d,"rows":%s}`, numRecords, resultJSON)
w.Write([]byte(str))
}
*/