From e3617af9d8ad7d080d8cff54e64e4d2760063cda Mon Sep 17 00:00:00 2001 From: Albin Antony Date: Tue, 3 Oct 2023 15:17:15 +0530 Subject: [PATCH] Add #190 Align APIs to GovStack: Change URL paths for config endpoints --- src/config/constants.go | 10 + .../actionlog_handler.go | 2 +- src/{handler => handlerv1}/consent_handler.go | 2 +- .../consent_handler_helper.go | 2 +- .../consenthistory_handler.go | 2 +- .../datarequest_handler.go | 2 +- src/{handler => handlerv1}/iam_handler.go | 2 +- src/{handler => handlerv1}/mobile_handler.go | 2 +- .../notification_handler.go | 2 +- src/{handler => handlerv1}/orgType_handler.go | 2 +- .../organization_handler.go | 2 +- src/{handler => handlerv1}/user_handler.go | 2 +- .../webhooks_handler.go | 2 +- src/handlerv2/organization_handler.go | 760 ++++++++++++++++++ src/handlerv2/webhooks_handler.go | 745 +++++++++++++++++ src/{main => httppathsv1}/audit_paths.go | 2 +- src/{main => httppathsv1}/config_paths.go | 2 +- src/{main => httppathsv1}/onboard_paths.go | 2 +- src/{main => httppathsv1}/routes.go | 4 +- src/{main => httppathsv1}/service_paths.go | 2 +- src/httppathsv2/audit_paths.go | 1 + src/httppathsv2/config_paths.go | 36 + src/httppathsv2/onboard_paths.go | 1 + src/httppathsv2/routes.go | 45 ++ src/httppathsv2/service_paths.go | 1 + src/main/main.go | 7 +- src/middleware/middleware.go | 2 +- 27 files changed, 1623 insertions(+), 21 deletions(-) rename src/{handler => handlerv1}/actionlog_handler.go (98%) rename src/{handler => handlerv1}/consent_handler.go (99%) rename src/{handler => handlerv1}/consent_handler_helper.go (99%) rename src/{handler => handlerv1}/consenthistory_handler.go (99%) rename src/{handler => handlerv1}/datarequest_handler.go (99%) rename src/{handler => handlerv1}/iam_handler.go (99%) rename src/{handler => handlerv1}/mobile_handler.go (99%) rename src/{handler => handlerv1}/notification_handler.go (92%) rename src/{handler => handlerv1}/orgType_handler.go (99%) rename src/{handler => handlerv1}/organization_handler.go (99%) rename src/{handler => handlerv1}/user_handler.go (99%) rename src/{handler => handlerv1}/webhooks_handler.go (99%) create mode 100644 src/handlerv2/organization_handler.go create mode 100644 src/handlerv2/webhooks_handler.go rename src/{main => httppathsv1}/audit_paths.go (72%) rename src/{main => httppathsv1}/config_paths.go (99%) rename src/{main => httppathsv1}/onboard_paths.go (99%) rename src/{main => httppathsv1}/routes.go (99%) rename src/{main => httppathsv1}/service_paths.go (98%) create mode 100644 src/httppathsv2/audit_paths.go create mode 100644 src/httppathsv2/config_paths.go create mode 100644 src/httppathsv2/onboard_paths.go create mode 100644 src/httppathsv2/routes.go create mode 100644 src/httppathsv2/service_paths.go diff --git a/src/config/constants.go b/src/config/constants.go index d0910c4..d161a0d 100644 --- a/src/config/constants.go +++ b/src/config/constants.go @@ -13,3 +13,13 @@ const ( SingleTenat = "single-tenant" MultiTenant = "multi-tenant" ) + +// All http path url variables +const ( + OrganizationId = "OrganizationID" + DataAgreementId = "dataAgreementId" + DataAttributeId = "dataAttributeId" + WebhookId = "webhookId" + WebhookDeliveryId = "deliveryId" + PolicyId = "policyId" +) diff --git a/src/handler/actionlog_handler.go b/src/handlerv1/actionlog_handler.go similarity index 98% rename from src/handler/actionlog_handler.go rename to src/handlerv1/actionlog_handler.go index 74e8123..20c5488 100644 --- a/src/handler/actionlog_handler.go +++ b/src/handlerv1/actionlog_handler.go @@ -1,4 +1,4 @@ -package handler +package handlerv1 import ( "encoding/json" diff --git a/src/handler/consent_handler.go b/src/handlerv1/consent_handler.go similarity index 99% rename from src/handler/consent_handler.go rename to src/handlerv1/consent_handler.go index 0c73585..97101a6 100644 --- a/src/handler/consent_handler.go +++ b/src/handlerv1/consent_handler.go @@ -1,4 +1,4 @@ -package handler +package handlerv1 import ( "encoding/json" diff --git a/src/handler/consent_handler_helper.go b/src/handlerv1/consent_handler_helper.go similarity index 99% rename from src/handler/consent_handler_helper.go rename to src/handlerv1/consent_handler_helper.go index 38d25e7..9c6cc5f 100644 --- a/src/handler/consent_handler_helper.go +++ b/src/handlerv1/consent_handler_helper.go @@ -1,4 +1,4 @@ -package handler +package handlerv1 import ( "time" diff --git a/src/handler/consenthistory_handler.go b/src/handlerv1/consenthistory_handler.go similarity index 99% rename from src/handler/consenthistory_handler.go rename to src/handlerv1/consenthistory_handler.go index 7239c5d..0c0c90d 100644 --- a/src/handler/consenthistory_handler.go +++ b/src/handlerv1/consenthistory_handler.go @@ -1,4 +1,4 @@ -package handler +package handlerv1 import ( "encoding/json" diff --git a/src/handler/datarequest_handler.go b/src/handlerv1/datarequest_handler.go similarity index 99% rename from src/handler/datarequest_handler.go rename to src/handlerv1/datarequest_handler.go index e21eedd..0239c8b 100644 --- a/src/handler/datarequest_handler.go +++ b/src/handlerv1/datarequest_handler.go @@ -1,4 +1,4 @@ -package handler +package handlerv1 import ( "encoding/json" diff --git a/src/handler/iam_handler.go b/src/handlerv1/iam_handler.go similarity index 99% rename from src/handler/iam_handler.go rename to src/handlerv1/iam_handler.go index 4a0d1bf..f671295 100644 --- a/src/handler/iam_handler.go +++ b/src/handlerv1/iam_handler.go @@ -1,4 +1,4 @@ -package handler +package handlerv1 import ( "bytes" diff --git a/src/handler/mobile_handler.go b/src/handlerv1/mobile_handler.go similarity index 99% rename from src/handler/mobile_handler.go rename to src/handlerv1/mobile_handler.go index 3141114..2a86191 100644 --- a/src/handler/mobile_handler.go +++ b/src/handlerv1/mobile_handler.go @@ -1,4 +1,4 @@ -package handler +package handlerv1 import ( "encoding/json" diff --git a/src/handler/notification_handler.go b/src/handlerv1/notification_handler.go similarity index 92% rename from src/handler/notification_handler.go rename to src/handlerv1/notification_handler.go index 2f0355c..aca8fda 100644 --- a/src/handler/notification_handler.go +++ b/src/handlerv1/notification_handler.go @@ -1,4 +1,4 @@ -package handler +package handlerv1 import "github.com/bb-consent/api/src/org" diff --git a/src/handler/orgType_handler.go b/src/handlerv1/orgType_handler.go similarity index 99% rename from src/handler/orgType_handler.go rename to src/handlerv1/orgType_handler.go index 0d84285..ceab1bd 100644 --- a/src/handler/orgType_handler.go +++ b/src/handlerv1/orgType_handler.go @@ -1,4 +1,4 @@ -package handler +package handlerv1 import ( "bytes" diff --git a/src/handler/organization_handler.go b/src/handlerv1/organization_handler.go similarity index 99% rename from src/handler/organization_handler.go rename to src/handlerv1/organization_handler.go index 61de00d..22911e5 100644 --- a/src/handler/organization_handler.go +++ b/src/handlerv1/organization_handler.go @@ -1,4 +1,4 @@ -package handler +package handlerv1 import ( "bytes" diff --git a/src/handler/user_handler.go b/src/handlerv1/user_handler.go similarity index 99% rename from src/handler/user_handler.go rename to src/handlerv1/user_handler.go index d4712cc..e5eeb38 100644 --- a/src/handler/user_handler.go +++ b/src/handlerv1/user_handler.go @@ -1,4 +1,4 @@ -package handler +package handlerv1 import ( "encoding/json" diff --git a/src/handler/webhooks_handler.go b/src/handlerv1/webhooks_handler.go similarity index 99% rename from src/handler/webhooks_handler.go rename to src/handlerv1/webhooks_handler.go index ef7d938..203737b 100644 --- a/src/handler/webhooks_handler.go +++ b/src/handlerv1/webhooks_handler.go @@ -1,4 +1,4 @@ -package handler +package handlerv1 import ( "encoding/json" diff --git a/src/handlerv2/organization_handler.go b/src/handlerv2/organization_handler.go new file mode 100644 index 0000000..e94de90 --- /dev/null +++ b/src/handlerv2/organization_handler.go @@ -0,0 +1,760 @@ +package handlerv2 + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "strings" + + "github.com/asaskevich/govalidator" + "github.com/bb-consent/api/src/common" + "github.com/bb-consent/api/src/config" + "github.com/bb-consent/api/src/org" + "github.com/bb-consent/api/src/orgtype" + "github.com/gorilla/mux" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +type organization struct { + Organization org.Organization +} + +type globalPolicyConfigurationResp struct { + PolicyURL string + DataRetention org.DataRetention + Jurisdiction string + Disclosure string + Type orgtype.OrgType + Restriction string + Shared3PP bool +} + +// GetGlobalPolicyConfiguration Handler to get global policy configurations +func GetGlobalPolicyConfiguration(w http.ResponseWriter, r *http.Request) { + organizationID := r.Header.Get(config.OrganizationId) + + o, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to fetch organization: %v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + // Constructing the response + var resp globalPolicyConfigurationResp + + resp.PolicyURL = o.PolicyURL + resp.DataRetention = o.DataRetention + + if len(strings.TrimSpace(o.Jurisdiction)) == 0 { + resp.Jurisdiction = o.Location + o.Jurisdiction = o.Location + } else { + resp.Jurisdiction = o.Jurisdiction + } + + if len(strings.TrimSpace(o.Disclosure)) == 0 { + resp.Disclosure = "false" + o.Disclosure = "false" + } else { + resp.Disclosure = o.Disclosure + } + + resp.Type = o.Type + resp.Restriction = o.Restriction + resp.Shared3PP = o.Shared3PP + + // Updating global configuration policy with defaults + _, err = org.Update(o) + if err != nil { + m := fmt.Sprintf("Failed to update global configuration with defaults to organization: %v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + response, _ := json.Marshal(resp) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + w.Write(response) + +} + +type globalPolicyConfigurationReq struct { + PolicyURL string + RetentionPeriod int + Jurisdiction string + Disclosure string + TypeID string `valid:"required"` + Restriction string + Shared3PP bool +} + +// UpdateGlobalPolicyConfiguration Handler to update global policy configuration +func UpdateGlobalPolicyConfiguration(w http.ResponseWriter, r *http.Request) { + organizationID := r.Header.Get(config.OrganizationId) + + o, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to fetch organization: %v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + var policyReq globalPolicyConfigurationReq + b, _ := ioutil.ReadAll(r.Body) + defer r.Body.Close() + + json.Unmarshal(b, &policyReq) + + // Update global policy configuration for the org + o.PolicyURL = policyReq.PolicyURL + + if len(strings.TrimSpace(policyReq.Jurisdiction)) != 0 { + o.Jurisdiction = policyReq.Jurisdiction + } + + o.Restriction = policyReq.Restriction + o.Shared3PP = policyReq.Shared3PP + + if policyReq.Disclosure == "false" || policyReq.Disclosure == "true" { + o.Disclosure = policyReq.Disclosure + } + + // Check if type id is valid bson objectid hex + if !primitive.IsValidObjectID(policyReq.TypeID) { + m := fmt.Sprintf("Invalid organization type ID: %v", policyReq.TypeID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + orgType, err := orgtype.Get(policyReq.TypeID) + if err != nil { + m := fmt.Sprintf("Invalid organization type ID: %v", policyReq.TypeID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + o.Type = orgType + + if policyReq.RetentionPeriod > 0 { + o.DataRetention.RetentionPeriod = int64(policyReq.RetentionPeriod) + o.DataRetention.Enabled = true + } else { + o.DataRetention.RetentionPeriod = 0 + o.DataRetention.Enabled = false + } + + // Updating global configuration policy with defaults + o, err = org.Update(o) + if err != nil { + m := fmt.Sprintf("Failed to update global configuration to organization: %v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + // Constructing the response + var resp globalPolicyConfigurationResp + resp.PolicyURL = o.PolicyURL + resp.DataRetention = o.DataRetention + resp.Jurisdiction = o.Jurisdiction + resp.Disclosure = o.Disclosure + resp.Type = o.Type + resp.Restriction = o.Restriction + resp.Shared3PP = o.Shared3PP + + response, _ := json.Marshal(resp) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + w.Write(response) + +} + +// UpdateGlobalPolicyConfigurationById Handler to update global policy configuration +func UpdateGlobalPolicyConfigurationById(w http.ResponseWriter, r *http.Request) { + + // Constructing the response + var resp globalPolicyConfigurationResp + + response, _ := json.Marshal(resp) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + w.Write(response) + +} + +// OrgListPolicyRevisions Handler to list global policy revisions +func OrgListPolicyRevisions(w http.ResponseWriter, r *http.Request) { + + // Constructing the response + var resp globalPolicyConfigurationResp + + response, _ := json.Marshal(resp) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + w.Write(response) + +} + +// OrgDeletePolicy Handler to delete global policy revision +func OrgDeletePolicy(w http.ResponseWriter, r *http.Request) { + + // Constructing the response + var resp globalPolicyConfigurationResp + + response, _ := json.Marshal(resp) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + w.Write(response) + +} + +// OrgListPolicy Handler to list all global policies +func OrgListPolicy(w http.ResponseWriter, r *http.Request) { + + // Constructing the response + var resp globalPolicyConfigurationResp + + response, _ := json.Marshal(resp) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + w.Write(response) + +} + +// GetDataAgreementById Get a data agreement by ID +func GetDataAgreementById(w http.ResponseWriter, r *http.Request) { + orgID := r.Header.Get(config.OrganizationId) + purposeID := mux.Vars(r)[config.DataAgreementId] + + o, err := org.Get(orgID) + if err != nil { + m := fmt.Sprintf("Failed to get organization: %v", orgID) + common.HandleError(w, http.StatusNotFound, m, err) + return + } + + type purposeTemplates struct { + ID string + Consent string + } + + type purposeDetails struct { + Purpose org.Purpose + Templates []purposeTemplates + } + var pDetails purposeDetails + for _, p := range o.Purposes { + if p.ID == purposeID { + pDetails.Purpose = p + } + } + + for _, t := range o.Templates { + for _, pID := range t.PurposeIDs { + if pID == purposeID { + pDetails.Templates = append(pDetails.Templates, purposeTemplates{ID: t.ID, Consent: t.Consent}) + } + } + } + + response, _ := json.Marshal(pDetails) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + w.Write(response) +} + +type purpose struct { + Name string `valid:"required"` + Description string `valid:"required"` + LawfulBasisOfProcessing int + PolicyURL string `valid:"required"` + AttributeType int + Jurisdiction string + Disclosure string + IndustryScope string + DataRetention org.DataRetention + Restriction string + Shared3PP bool + SSIID string +} + +// Check if the lawful usage ID provided is valid +func isValidLawfulBasisOfProcessing(lawfulBasis int) bool { + isFound := false + for _, lawfulBasisOfProcessingMapping := range org.LawfulBasisOfProcessingMappings { + if lawfulBasisOfProcessingMapping.ID == lawfulBasis { + isFound = true + break + } + } + + return isFound +} + +// Fetch the lawful usage based on the lawful basis ID +func getLawfulUsageByLawfulBasis(lawfulBasis int) bool { + if lawfulBasis == org.ConsentBasis { + return false + } else { + return true + } +} + +func deletePurposeIDFromTemplate(purposeID string, orgID string, templates []org.Template) error { + for _, t := range templates { + for _, p := range t.PurposeIDs { + if p == purposeID { + var template org.Template + template.Consent = t.Consent + template.ID = t.ID + for _, p := range t.PurposeIDs { + if p != purposeID { + template.PurposeIDs = append(template.PurposeIDs, p) + } + } + _, err := org.DeleteTemplates(orgID, t) + if err != nil { + fmt.Printf("Failed to delete template: %v from organization: %v", t.ID, orgID) + return err + } + if len(template.PurposeIDs) == 0 { + continue + } + err = org.AddTemplates(orgID, template) + if err != nil { + fmt.Printf("Failed to add template: %v from organization: %v", t.ID, orgID) + return err + } + continue + } + } + } + return nil +} + +type addPurposeReq struct { + purpose +} + +// AddDataAgreement Adds a single data agreement to the organization +func AddDataAgreement(w http.ResponseWriter, r *http.Request) { + organizationID := r.Header.Get(config.OrganizationId) + + o, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to fetch organization: %v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + var pReq addPurposeReq + b, _ := ioutil.ReadAll(r.Body) + defer r.Body.Close() + + json.Unmarshal(b, &pReq) + + // validating request payload + valid, err := govalidator.ValidateStruct(pReq) + if !valid { + log.Printf("Missing mandatory fields for a adding consent purpose to org") + common.HandleError(w, http.StatusBadRequest, err.Error(), err) + return + } + + // Proceed if lawful basis of processing provided is valid + if !isValidLawfulBasisOfProcessing(pReq.LawfulBasisOfProcessing) { + m := fmt.Sprintf("Invalid lawful basis of processing provided") + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + tempLawfulUsage := getLawfulUsageByLawfulBasis(pReq.LawfulBasisOfProcessing) + + newPurpose := org.Purpose{ + ID: primitive.NewObjectID().Hex(), + Name: pReq.Name, + Description: pReq.Description, + LawfulUsage: tempLawfulUsage, + LawfulBasisOfProcessing: pReq.LawfulBasisOfProcessing, + PolicyURL: pReq.PolicyURL, + AttributeType: pReq.AttributeType, + Jurisdiction: pReq.Jurisdiction, + Disclosure: pReq.Disclosure, + IndustryScope: pReq.IndustryScope, + DataRetention: pReq.DataRetention, + Restriction: pReq.Restriction, + Shared3PP: pReq.Shared3PP, + SSIID: pReq.SSIID, + } + o.Purposes = append(o.Purposes, newPurpose) + + _, err = org.UpdatePurposes(o.ID.Hex(), o.Purposes) + if err != nil { + m := fmt.Sprintf("Failed to update purpose to organization: %v", o.Name) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + /* + u, err := user.Get(token.GetUserID(r)) + if err != nil { + //notifications.SendPurposeUpdateNotification(u, o.ID.Hex(), ) + } + */ + response, _ := json.Marshal(newPurpose) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + w.Write(response) +} + +// UpdateDataAgreementD Update the given data agreement by ID +func UpdateDataAgreement(w http.ResponseWriter, r *http.Request) { + organizationID := r.Header.Get(config.OrganizationId) + purposeID := mux.Vars(r)[config.DataAgreementId] + + var uReq purpose + b, _ := ioutil.ReadAll(r.Body) + defer r.Body.Close() + + json.Unmarshal(b, &uReq) + + o, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to fetch organization: %v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + // Proceed if lawful basis of processing provided is valid + if !isValidLawfulBasisOfProcessing(uReq.LawfulBasisOfProcessing) { + m := fmt.Sprintf("Invalid lawful basis of processing provided") + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + tempLawfulUsage := getLawfulUsageByLawfulBasis(uReq.LawfulBasisOfProcessing) + + var found = false + for i := range o.Purposes { + if o.Purposes[i].ID == purposeID { + found = true + o.Purposes[i].Name = strings.TrimSpace(uReq.Name) + o.Purposes[i].Description = strings.TrimSpace(uReq.Description) + o.Purposes[i].PolicyURL = strings.TrimSpace(uReq.PolicyURL) + o.Purposes[i].LawfulUsage = tempLawfulUsage + o.Purposes[i].LawfulBasisOfProcessing = uReq.LawfulBasisOfProcessing + o.Purposes[i].Jurisdiction = uReq.Jurisdiction + o.Purposes[i].Disclosure = uReq.Disclosure + o.Purposes[i].IndustryScope = uReq.IndustryScope + o.Purposes[i].DataRetention = uReq.DataRetention + o.Purposes[i].Restriction = uReq.Restriction + o.Purposes[i].Shared3PP = uReq.Shared3PP + if (o.Purposes[i].AttributeType != uReq.AttributeType) || + (o.Purposes[i].SSIID != uReq.SSIID) { + m := fmt.Sprintf("Can not modify attributeType or SSIID for purpose: %v organization: %v", + organizationID, purposeID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + } + } + + if !found { + m := fmt.Sprintf("Failed to find purpose with ID: %v in organization: %v", purposeID, o.Name) + common.HandleError(w, http.StatusNotFound, m, err) + return + } + + o, err = org.Update(o) + if err != nil { + m := fmt.Sprintf("Failed to update purpose: %v in organization: %v", purposeID, o.Name) + common.HandleError(w, http.StatusNotFound, m, err) + return + } + + response, _ := json.Marshal(organization{o}) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusCreated) + w.Write(response) +} + +// DeleteDataAgreement Deletes the given data agreement by ID +func DeleteDataAgreement(w http.ResponseWriter, r *http.Request) { + organizationID := r.Header.Get(config.OrganizationId) + purposeID := mux.Vars(r)[config.DataAgreementId] + + o, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to fetch organization: %v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + var purposeToDelete org.Purpose + for _, p := range o.Purposes { + if p.ID == purposeID { + purposeToDelete = p + } + } + + if purposeToDelete == (org.Purpose{}) { + m := fmt.Sprintf("Failed to find purpose with ID: %v in organization: %v", purposeID, o.Name) + common.HandleError(w, http.StatusNotFound, m, err) + return + } + + //TODO: Before we delete purpose, need to remove the purpose from the templates + err = deletePurposeIDFromTemplate(purposeID, o.ID.Hex(), o.Templates) + if err != nil { + m := fmt.Sprintf("Failed to update template for organization: %v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + orgResp, err := org.DeletePurposes(o.ID.Hex(), purposeToDelete) + if err != nil { + m := fmt.Sprintf("Failed to delete purpose: %v from organization: %v", purposeID, o.Name) + common.HandleError(w, http.StatusNotFound, m, err) + return + } + + response, _ := json.Marshal(organization{orgResp}) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusCreated) + w.Write(response) +} + +type getPurposesResp struct { + OrgID string + Purposes []org.Purpose +} + +// ListDataAgreements Gets an organization data agreements +func ListDataAgreements(w http.ResponseWriter, r *http.Request) { + organizationID := r.Header.Get(config.OrganizationId) + + o, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to fetch organization: %v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + response, _ := json.Marshal(getPurposesResp{OrgID: o.ID.Hex(), Purposes: o.Purposes}) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusCreated) + w.Write(response) +} + +// ListDataAgreementRevisions Gets an organization data agreements revisions +func ListDataAgreementRevisions(w http.ResponseWriter, r *http.Request) { + + response, _ := json.Marshal(getPurposesResp{}) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusCreated) + w.Write(response) +} + +// ReadDataAgreementRevision Gets an organization data agreements revision +func ReadDataAgreementRevision(w http.ResponseWriter, r *http.Request) { + + response, _ := json.Marshal(getPurposesResp{}) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusCreated) + w.Write(response) +} + +type getTemplatesResp struct { + OrgID string + Templates []org.Template +} + +// GetDataAttributes Gets an organization data attributes +func GetDataAttributes(w http.ResponseWriter, r *http.Request) { + organizationID := r.Header.Get(config.OrganizationId) + + o, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to fetch organization: %v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + response, _ := json.Marshal(getTemplatesResp{OrgID: o.ID.Hex(), Templates: o.Templates}) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusCreated) + w.Write(response) +} + +type template struct { + Consent string `valid:"required"` + PurposeIDs []string `valid:"required"` +} +type templateReq struct { + Templates []template +} + +// AddDataAttribute Adds an organization data attribute +func AddDataAttribute(w http.ResponseWriter, r *http.Request) { + organizationID := r.Header.Get(config.OrganizationId) + + o, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to fetch organization: %v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + var tReq templateReq + b, _ := ioutil.ReadAll(r.Body) + defer r.Body.Close() + + json.Unmarshal(b, &tReq) + + // validating request payload + valid, err := govalidator.ValidateStruct(tReq) + if !valid { + log.Printf("Missing mandatory fields for adding consent template to org: %v", organizationID) + common.HandleError(w, http.StatusBadRequest, err.Error(), err) + return + } + + // validating purposeIDs provided + for _, t := range tReq.Templates { + // checking if purposeID provided exist in the org + for _, p := range t.PurposeIDs { + _, err = org.GetPurpose(organizationID, p) + if err != nil { + m := fmt.Sprintf("Invalid purposeID:%v provided;Failed to update templates to organization: %v", p, o.Name) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + } + + // Appending the new template to existing org templates + o.Templates = append(o.Templates, org.Template{ + ID: primitive.NewObjectID().Hex(), + Consent: t.Consent, + PurposeIDs: t.PurposeIDs, + }) + } + + orgResp, err := org.UpdateTemplates(o.ID.Hex(), o.Templates) + if err != nil { + m := fmt.Sprintf("Failed to update templates to organization: %v", o.Name) + common.HandleError(w, http.StatusNotFound, m, err) + return + } + + response, _ := json.Marshal(organization{orgResp}) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusCreated) + w.Write(response) +} + +// UpdateDataAttributeById Updates an organization data attribute +func UpdateDataAttributeById(w http.ResponseWriter, r *http.Request) { + organizationID := r.Header.Get(config.OrganizationId) + templateID := mux.Vars(r)[config.DataAttributeId] + + var uReq template + b, _ := ioutil.ReadAll(r.Body) + defer r.Body.Close() + + json.Unmarshal(b, &uReq) + + o, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to fetch organization: %v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + // validating PurposeIDs provided + if uReq.PurposeIDs != nil { + + for _, p := range uReq.PurposeIDs { + _, err := org.GetPurpose(organizationID, p) + if err != nil { + m := fmt.Sprintf("Invalid purposeID:%v provided;Failed to update template to organization: %v", p, o.Name) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + } + } + + var found = false + + for i := range o.Templates { + if o.Templates[i].ID == templateID { + found = true + + // for partial updation + if strings.TrimSpace(uReq.Consent) != "" { + o.Templates[i].Consent = uReq.Consent + } + // only updating if any valid purpose id was given + if uReq.PurposeIDs != nil { + o.Templates[i].PurposeIDs = uReq.PurposeIDs + } + } + } + + if !found { + m := fmt.Sprintf("Failed to find template with ID: %v in organization: %v", templateID, o.Name) + common.HandleError(w, http.StatusNotFound, m, err) + return + } + + o, err = org.Update(o) + if err != nil { + m := fmt.Sprintf("Failed to update template: %v in organization: %v", templateID, o.Name) + common.HandleError(w, http.StatusNotFound, m, err) + return + } + + response, _ := json.Marshal(organization{o}) + + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusCreated) + w.Write(response) +} + +// DeleteDataAttributeById Deletes an organization data attribute +func DeleteDataAttributeById(w http.ResponseWriter, r *http.Request) { + organizationID := r.Header.Get(config.OrganizationId) + templateID := mux.Vars(r)[config.DataAttributeId] + + o, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to fetch organization: %v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + var templateToDelete org.Template + for _, t := range o.Templates { + if t.ID == templateID { + templateToDelete = t + } + } + + if templateToDelete.ID != templateID { + m := fmt.Sprintf("Failed to find template with ID: %v in organization: %v", templateID, o.Name) + common.HandleError(w, http.StatusNotFound, m, err) + return + } + + orgResp, err := org.DeleteTemplates(o.ID.Hex(), templateToDelete) + if err != nil { + m := fmt.Sprintf("Failed to delete template: %v from organization: %v", templateID, o.Name) + common.HandleError(w, http.StatusNotFound, m, err) + return + } + + response, _ := json.Marshal(organization{orgResp}) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusCreated) + w.Write(response) +} diff --git a/src/handlerv2/webhooks_handler.go b/src/handlerv2/webhooks_handler.go new file mode 100644 index 0000000..79af9f5 --- /dev/null +++ b/src/handlerv2/webhooks_handler.go @@ -0,0 +1,745 @@ +package handlerv2 + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "strconv" + "strings" + "time" + + "github.com/asaskevich/govalidator" + "github.com/bb-consent/api/src/actionlog" + "github.com/bb-consent/api/src/common" + "github.com/bb-consent/api/src/config" + "github.com/bb-consent/api/src/org" + "github.com/bb-consent/api/src/user" + wh "github.com/bb-consent/api/src/webhooks" + "github.com/gorilla/mux" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +// WebhookEventTypesResp Define response structure for webhook event types +type WebhookEventTypesResp struct { + EventTypes []string +} + +// GetWebhookEventTypes List available webhook event types +func GetWebhookEventTypes(w http.ResponseWriter, r *http.Request) { + var webhookEventTypesResp WebhookEventTypesResp + + for _, eventType := range wh.EventTypes { + webhookEventTypesResp.EventTypes = append(webhookEventTypesResp.EventTypes, eventType) + } + + response, _ := json.Marshal(webhookEventTypesResp) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + w.Write(response) + +} + +// WebhookPayloadContentTypesResp Defines response structure for webhook payload content types +type WebhookPayloadContentTypesResp struct { + ContentTypes []string +} + +// GetWebhookPayloadContentTypes List available webhook payload content types +func GetWebhookPayloadContentTypes(w http.ResponseWriter, r *http.Request) { + var webhookPayloadContentTypesResp WebhookPayloadContentTypesResp + + for _, payloadContentTypes := range wh.PayloadContentTypes { + webhookPayloadContentTypesResp.ContentTypes = append(webhookPayloadContentTypesResp.ContentTypes, payloadContentTypes) + } + + response, _ := json.Marshal(webhookPayloadContentTypesResp) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + w.Write(response) +} + +// WebhookWithLastDeliveryStatus Defines webhook structure along with last delivery status +type WebhookWithLastDeliveryStatus struct { + ID primitive.ObjectID `bson:"_id,omitempty"` // Webhook ID + PayloadURL string // Webhook payload URL + Disabled bool // Disabled or not + TimeStamp string // UTC timestamp + IsLastDeliverySuccess bool // Indicates whether last payload delivery to webhook was success or not +} + +// GetAllWebhooks Gets all webhooks for an organisation +func GetAllWebhooks(w http.ResponseWriter, r *http.Request) { + // Reading URL parameters + organizationID := r.Header.Get(config.OrganizationId) + + // Validating the given organisation ID + _, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to get organization: %v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + sanitizedOrgId := common.Sanitize(organizationID) + + // Fetching all the webhooks for an organisation + webhooks, err := wh.GetAllWebhooksByOrgID(sanitizedOrgId) + if err != nil { + m := fmt.Sprintf("Failed to fetch webhooks for organization: %v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + var updatedWebhooks []WebhookWithLastDeliveryStatus + + updatedWebhooks = make([]WebhookWithLastDeliveryStatus, 0) + + for _, webhook := range webhooks { + + // Fetching the last delivery to the webhook and retrieving the delivery status + isLastDeliverySuccess := false + lastDelivery, err := wh.GetLastWebhookDelivery(webhook.ID.Hex()) + if err != nil { + // There is no payload delivery yet ! + isLastDeliverySuccess = true + } else { + // if the last payload delivery is completed and response status code is within 2XX range + if lastDelivery.Status == wh.DeliveryStatus[wh.DeliveryStatusCompleted] { + if (lastDelivery.ResponseStatusCode >= 200 && lastDelivery.ResponseStatusCode <= 208) || lastDelivery.ResponseStatusCode == 226 { + isLastDeliverySuccess = true + } + } + + } + + updatedWebhook := WebhookWithLastDeliveryStatus{ + ID: webhook.ID, + PayloadURL: webhook.PayloadURL, + Disabled: webhook.Disabled, + TimeStamp: webhook.TimeStamp, + IsLastDeliverySuccess: isLastDeliverySuccess, + } + + updatedWebhooks = append(updatedWebhooks, updatedWebhook) + } + + response, _ := json.Marshal(updatedWebhooks) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + w.Write(response) +} + +// createWebhookReq Defines the request payload structure for creating a webhook for an organisation +type createWebhookReq struct { + PayloadURL string `valid:"required,url"` // Webhook endpoint URL + SubscribedEvents []string `valid:"required"` // Subscribed events + ContentType string `valid:"required"` // Data format for the webhook payload + Disabled bool // Disabled or not + SecretKey string // For calculating SHA256 HMAC to verify data integrity and authenticity + SkipSSLVerification bool // Skip SSL certificate verification or not (expiry is checked) +} + +// uniqueSlice Filter out all the duplicate strings and returns the unique slice +func uniqueSlice(inputSlice []string) []string { + keys := make(map[string]bool) + list := []string{} + for _, entry := range inputSlice { + if _, value := keys[entry]; !value { + keys[entry] = true + list = append(list, entry) + } + } + return list +} + +// CreateWebhook Creates a webhook endpoint for an organisation +func CreateWebhook(w http.ResponseWriter, r *http.Request) { + var requestPayload createWebhookReq + + // Reading request body as bytes and unmarshalling to a struct + b, _ := ioutil.ReadAll(r.Body) + defer r.Body.Close() + json.Unmarshal(b, &requestPayload) + + organizationID := r.Header.Get(config.OrganizationId) + + // Validating request payload struct + _, err := govalidator.ValidateStruct(requestPayload) + if err != nil { + log.Printf("Missing mandatory params; Failed to create webhook for organisation: %v", organizationID) + common.HandleError(w, http.StatusBadRequest, err.Error(), err) + return + } + + // Validating the given organisation ID + _, err = org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to get organization: %v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + // Check if the webhook endpoint contains http:// or https:// + if !(strings.HasPrefix(requestPayload.PayloadURL, "https://") || strings.HasPrefix(requestPayload.PayloadURL, "http://")) { + m := fmt.Sprintf("Please prefix the endpoint URL with https:// or http://; Failed to create webhook for organisation:%v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + sanitizedOrgId := common.Sanitize(organizationID) + sanitizedPayloadURL := common.Sanitize(requestPayload.PayloadURL) + + // Check if webhook with provided payload URL already exists + count, err := wh.GetWebhookCountByPayloadURL(sanitizedOrgId, sanitizedPayloadURL) + if err != nil { + m := fmt.Sprintf("Failed to create webhook for organisation:%v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + if count > 0 { + m := fmt.Sprintf("Webhook with provided payload URL already exists; Failed to create webhook for organisation:%v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + // Check if subscribed event type(s) array is empty + if len(requestPayload.SubscribedEvents) == 0 { + m := fmt.Sprintf("Provide atleast 1 event type; Failed to create webhook for organisation:%v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + // Check if subscribed event type(s) contains duplicates + requestPayload.SubscribedEvents = uniqueSlice(requestPayload.SubscribedEvents) + + // Check the subscribed event type ID(s) provided is valid + var isValidSubscribedEvents bool + for _, subscribedEventType := range requestPayload.SubscribedEvents { + isValidSubscribedEvents = false + for _, eventType := range wh.EventTypes { + if subscribedEventType == eventType { + isValidSubscribedEvents = true + break + } + } + + if !isValidSubscribedEvents { + break + } + } + + if !isValidSubscribedEvents { + m := fmt.Sprintf("Please provide a valid event type; Failed to create webhook for organisation:%v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + // Check if the content type ID provided is valid + isValidContentType := false + for _, payloadContentType := range wh.PayloadContentTypes { + if requestPayload.ContentType == payloadContentType { + isValidContentType = true + } + } + + if !isValidContentType { + m := fmt.Sprintf("Please provide a valid content type; Failed to create webhook for organisation:%v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + // Constructing webhook structure required for adding it to database + webhook := wh.Webhook{ + OrgID: organizationID, + PayloadURL: strings.TrimSpace(requestPayload.PayloadURL), + ContentType: requestPayload.ContentType, + SubscribedEvents: requestPayload.SubscribedEvents, + Disabled: requestPayload.Disabled, + SecretKey: strings.TrimSpace(requestPayload.SecretKey), + SkipSSLVerification: requestPayload.SkipSSLVerification, + TimeStamp: strconv.FormatInt(time.Now().UTC().Unix(), 10), + } + + // Creating webhook + webhook, err = wh.CreateWebhook(webhook) + if err != nil { + m := fmt.Sprintf("Failed to create webhook for organisation:%v", organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + response, _ := json.Marshal(webhook) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusCreated) + w.Write(response) + +} + +// updateWebhookReq Defines the request payload structure for updating a webhook for an organisation +type updateWebhookReq struct { + PayloadURL string `valid:"required,url"` // Webhook endpoint URL + SubscribedEvents []string `valid:"required"` // Events subscribed for e.g. user.data.delete + ContentType string `valid:"required"` // Data format for the webhook payload + Disabled bool // Disabled or not + SecretKey string // For calculating SHA256 HMAC to verify data integrity and authenticity + SkipSSLVerification bool // Skip SSL certificate verification or not (expiry is checked) +} + +// UpdateWebhook Updates a webhook for an organisation by ID +func UpdateWebhook(w http.ResponseWriter, r *http.Request) { + // Reading the URL parameters + organizationID := r.Header.Get(config.OrganizationId) + webhookID := mux.Vars(r)[config.WebhookId] + + fmt.Printf("Mux vars : %v\n", mux.Vars(r)) + + // Validating the given organisation ID + _, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to get organization: %v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + sanitizedOrgId := common.Sanitize(organizationID) + sanitizedWebhookId := common.Sanitize(webhookID) + + // Validating the given webhook ID for an organisation + webhook, err := wh.GetByOrgID(sanitizedWebhookId, sanitizedOrgId) + if err != nil { + m := fmt.Sprintf("Failed to get webhook:%v for organisation: %v", webhookID, organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + var requestPayload updateWebhookReq + + // Reading request body as bytes and unmarshalling to a struct + b, _ := ioutil.ReadAll(r.Body) + defer r.Body.Close() + json.Unmarshal(b, &requestPayload) + + // Validating request payload struct + valid, err := govalidator.ValidateStruct(requestPayload) + if valid != true { + log.Printf("Missing mandatory params; Failed to update webhook for organisation: %v", organizationID) + common.HandleError(w, http.StatusBadRequest, err.Error(), err) + return + } + + // Check if the webhook endpoint contains http:// or https:// + if !(strings.HasPrefix(requestPayload.PayloadURL, "https://") || strings.HasPrefix(requestPayload.PayloadURL, "http://")) { + m := fmt.Sprintf("Please prefix the endpoint URL with https:// or http://; Failed to update webhook for organisation:%v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + sanitizedPayloadURL := common.Sanitize(requestPayload.PayloadURL) + + // Check if webhook with provided payload URL already exists + tempWebhook, err := wh.GetWebhookByPayloadURL(sanitizedOrgId, sanitizedPayloadURL) + if err == nil { + if tempWebhook.ID.Hex() != webhookID { + m := fmt.Sprintf("Webhook with provided payload URL already exists; Failed to update webhook for organisation:%v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + } + + // Check if subscribed events array is empty + if len(requestPayload.SubscribedEvents) == 0 { + m := fmt.Sprintf("Provide atleast 1 event type in subscribed events; Failed to update webhook for organisation:%v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + // Check if subscribed event type(s) contains duplicates + requestPayload.SubscribedEvents = uniqueSlice(requestPayload.SubscribedEvents) + + // Check the subscribed event type ID(s) provided is valid + var isValidSubscribedEvents bool + for _, subscribedEventType := range requestPayload.SubscribedEvents { + isValidSubscribedEvents = false + for _, eventType := range wh.EventTypes { + if subscribedEventType == eventType { + isValidSubscribedEvents = true + break + } + } + + if !isValidSubscribedEvents { + break + } + } + + if !isValidSubscribedEvents { + m := fmt.Sprintf("Invalid event type provided in subscribed events; Failed to update webhook for organisation:%v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + // Check if the content type ID provided is valid + isValidContentType := false + for _, payloadContentType := range wh.PayloadContentTypes { + if requestPayload.ContentType == payloadContentType { + isValidContentType = true + } + } + + if !isValidContentType { + m := fmt.Sprintf("Invalid content type provided; Failed to update webhook for organisation:%v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + // Updating webhook + webhook.PayloadURL = strings.TrimSpace(requestPayload.PayloadURL) + webhook.ContentType = requestPayload.ContentType + webhook.SubscribedEvents = requestPayload.SubscribedEvents + webhook.Disabled = requestPayload.Disabled + webhook.SecretKey = requestPayload.SecretKey + webhook.SkipSSLVerification = requestPayload.SkipSSLVerification + + webhook, err = wh.UpdateWebhook(webhook) + if err != nil { + m := fmt.Sprintf("Failed to update webhook:%v for organisation: %v", webhookID, organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + response, _ := json.Marshal(webhook) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + w.Write(response) + +} + +// DeleteWebhook Deletes a webhook for an organisation +func DeleteWebhook(w http.ResponseWriter, r *http.Request) { + // Reading URL parameters + organizationID := r.Header.Get(config.OrganizationId) + webhookID := mux.Vars(r)[config.WebhookId] + + // Validating the given organisation ID + _, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to get organization: %v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + sanitizedOrgId := common.Sanitize(organizationID) + sanitizedWebhookId := common.Sanitize(webhookID) + + // Validating the given webhook ID for an organisation + _, err = wh.GetByOrgID(sanitizedWebhookId, sanitizedOrgId) + if err != nil { + m := fmt.Sprintf("Failed to get webhook:%v for organisation: %v", webhookID, organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + // Deleting webhook + err = wh.DeleteWebhook(webhookID) + if err != nil { + m := fmt.Sprintf("Failed to delete webhook:%v for organisation: %v", webhookID, organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusNoContent) + +} + +// PingWebhookResp Defines the response structure for webhook status check using ping +type PingWebhookResp struct { + ResponseStatusCode int // HTTP response status code + ResponseStatusStr string // HTTP response status string + ExecutionStartTimeStamp string // UTC timestamp when webhook execution started + ExecutionEndTimeStamp string // UTC timestamp when webhook execution ended + Status string // Status of webhook delivery for e.g. failed or completed + StatusDescription string // Describe the status for e.g. Reason for failure +} + +// PingWebhook Pings webhook payload URL to check the response status code is 200 OK or not +func PingWebhook(w http.ResponseWriter, r *http.Request) { + + // Reading the URL parameters + organizationID := r.Header.Get(config.OrganizationId) + webhookID := mux.Vars(r)[config.WebhookId] + + // Validating the given organisation ID + _, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to get organization: %v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + sanitizedOrgId := common.Sanitize(organizationID) + sanitizedWebhookId := common.Sanitize(webhookID) + + // Validating the given webhook ID for an organisation + webhook, err := wh.GetByOrgID(sanitizedWebhookId, sanitizedOrgId) + if err != nil { + m := fmt.Sprintf("Failed to get webhook:%v for organisation: %v", webhookID, organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + // Pinging webhook payload URL + _, resp, executionStartTimeStamp, executionEndTimeStamp, err := wh.PingWebhook(webhook) + + if err != nil { + + log.Printf("Error: %v; Failed to ping webhook:%v for organisation: %v", err, webhookID, organizationID) + + // Constructing webhook ping response + pingWebhookResp := PingWebhookResp{ + ExecutionStartTimeStamp: executionStartTimeStamp, + ExecutionEndTimeStamp: executionEndTimeStamp, + Status: wh.DeliveryStatus[wh.DeliveryStatusFailed], + StatusDescription: err.Error(), + } + + response, _ := json.Marshal(pingWebhookResp) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + w.Write(response) + return + } + + defer resp.Body.Close() + + // Constructing webhook ping response + pingWebhookResp := PingWebhookResp{ + ResponseStatusCode: resp.StatusCode, + ResponseStatusStr: resp.Status, + ExecutionStartTimeStamp: executionStartTimeStamp, + ExecutionEndTimeStamp: executionEndTimeStamp, + Status: wh.DeliveryStatus[wh.DeliveryStatusCompleted], + } + + response, _ := json.Marshal(pingWebhookResp) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + w.Write(response) + +} + +// recentWebhookDelivery Defines the structure for recent webhook delivery +type recentWebhookDelivery struct { + ID primitive.ObjectID `bson:"_id,omitempty"` // Webhook delivery ID + WebhookID string // Webhook ID + ResponseStatusCode int // HTTP response status code + ResponseStatusStr string // HTTP response status string + TimeStamp string // UTC timestamp when webhook execution started + Status string // Status of webhook delivery for e.g. failed or completed + StatusDescription string // Describe the status for e.g. Reason for failure +} + +type recentWebhookDeliveryResp struct { + WebhookDeliveries []recentWebhookDelivery + Links common.PaginationLinks +} + +// GetRecentWebhookDeliveries Gets the recent webhook deliveries limited by `x` records +func GetRecentWebhookDeliveries(w http.ResponseWriter, r *http.Request) { + // Reading the URL parameters + organizationID := r.Header.Get(config.OrganizationId) + webhookID := mux.Vars(r)[config.WebhookId] + + startID, limit := common.ParsePaginationQueryParameters(r) + if limit == 0 { + limit = 50 + } + + // Validating the given organisation ID + _, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to get organization: %v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + sanitizedOrgId := common.Sanitize(organizationID) + sanitizedWebhookId := common.Sanitize(webhookID) + + // Validating the given webhook ID for an organisation + webhook, err := wh.GetByOrgID(sanitizedWebhookId, sanitizedOrgId) + if err != nil { + m := fmt.Sprintf("Failed to get webhook:%v for organisation: %v", webhookID, organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + // Get all the recent webhook deliveries + recentWebhookDeliveries, lastID, err := wh.GetAllDeliveryByWebhookID(webhook.ID.Hex(), startID, limit) + if err != nil { + m := fmt.Sprintf("Failed to fetch recent payload deliveries for webhook:%v for organisation: %v", webhookID, organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + // Constructing the response + var resp recentWebhookDeliveryResp + + resp.WebhookDeliveries = make([]recentWebhookDelivery, 0) + + for _, wd := range recentWebhookDeliveries { + + tempRecentWebhookDelivery := recentWebhookDelivery{ + ID: wd.ID, + WebhookID: wd.WebhookID, + ResponseStatusCode: wd.ResponseStatusCode, + ResponseStatusStr: wd.ResponseStatusStr, + TimeStamp: wd.ExecutionStartTimeStamp, + Status: wd.Status, + StatusDescription: wd.StatusDescription, + } + + resp.WebhookDeliveries = append(resp.WebhookDeliveries, tempRecentWebhookDelivery) + } + + resp.Links = common.CreatePaginationLinks(r, startID, lastID, limit) + + response, _ := json.Marshal(resp) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + w.Write(response) + +} + +type webhookDeliveryResp struct { + ID primitive.ObjectID `bson:"_id,omitempty"` + RequestHeaders map[string][]string // HTTP headers posted to webhook endpoint + RequestPayload interface{} // JSON payload posted to webhook endpoint + ResponseHeaders map[string][]string // HTTP response headers received from webhook endpoint + ResponseBody string // HTTP response body received from webhook endpoint in bytes + ResponseStatusCode int // HTTP response status code + ResponseStatusStr string // HTTP response status string + ExecutionStartTimeStamp string // UTC timestamp when webhook execution started + ExecutionEndTimeStamp string // UTC timestamp when webhook execution ended + Status string // Status of webhook delivery for e.g. failed or completed + StatusDescription string // Describe the status for e.g. Reason for failure +} + +// GetRecentWebhookDeliveryById Gets the payload delivery details for a webhook by ID +func GetRecentWebhookDeliveryById(w http.ResponseWriter, r *http.Request) { + // Reading the URL parameters + organizationID := r.Header.Get(config.OrganizationId) + webhookID := mux.Vars(r)[config.WebhookId] + deliveryID := mux.Vars(r)[config.WebhookDeliveryId] + + // Validating the given organisation ID + _, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to get organization: %v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + sanitizedOrgId := common.Sanitize(organizationID) + sanitizedWebhookId := common.Sanitize(webhookID) + sanitizedDeliveryId := common.Sanitize(deliveryID) + + // Validating the given webhook ID for an organisation + webhook, err := wh.GetByOrgID(sanitizedWebhookId, sanitizedOrgId) + if err != nil { + m := fmt.Sprintf("Failed to get webhook:%v for organisation: %v", webhookID, organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + // Get the webhook delivery by ID + webhookDelivery, err := wh.GetWebhookDeliveryByID(webhook.ID.Hex(), sanitizedDeliveryId) + if err != nil { + m := fmt.Sprintf("Failed to get delivery details by ID:%v for webhook:%v for organisation: %v", deliveryID, webhookID, organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + resp := webhookDeliveryResp{ + ID: webhookDelivery.ID, + RequestHeaders: webhookDelivery.RequestHeaders, + RequestPayload: webhookDelivery.RequestPayload, + ResponseHeaders: webhookDelivery.ResponseHeaders, + ResponseBody: webhookDelivery.ResponseBody, + ResponseStatusCode: webhookDelivery.ResponseStatusCode, + ResponseStatusStr: webhookDelivery.ResponseStatusStr, + ExecutionStartTimeStamp: webhookDelivery.ExecutionStartTimeStamp, + ExecutionEndTimeStamp: webhookDelivery.ExecutionEndTimeStamp, + Status: webhookDelivery.Status, + StatusDescription: webhookDelivery.StatusDescription, + } + + response, _ := json.Marshal(resp) + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + w.Write(response) +} + +// RedeliverWebhookPayloadByDeliveryID Redo payload delivery to the webhook +func RedeliverWebhookPayloadByDeliveryID(w http.ResponseWriter, r *http.Request) { + // Reading the URL parameters + organizationID := r.Header.Get(config.OrganizationId) + webhookID := mux.Vars(r)[config.WebhookId] + deliveryID := mux.Vars(r)[config.WebhookDeliveryId] + + // Validating the given organisation ID + _, err := org.Get(organizationID) + if err != nil { + m := fmt.Sprintf("Failed to get organization: %v", organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + sanitizedOrgId := common.Sanitize(organizationID) + sanitizedWebhookId := common.Sanitize(webhookID) + sanitizedDeliveryId := common.Sanitize(deliveryID) + + // Validating the given webhook ID for an organisation + webhook, err := wh.GetByOrgID(sanitizedWebhookId, sanitizedOrgId) + if err != nil { + m := fmt.Sprintf("Failed to get webhook:%v for organisation: %v", webhookID, organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + // Validating the given delivery ID for a webhook + webhookDelivery, err := wh.GetWebhookDeliveryByID(webhook.ID.Hex(), sanitizedDeliveryId) + if err != nil { + m := fmt.Sprintf("Failed to get delivery details by ID:%v for webhook:%v for organisation: %v", deliveryID, webhookID, organizationID) + common.HandleError(w, http.StatusBadRequest, m, err) + return + } + + // Converting the webhook payload to bytes + webhookPayloadBytes, err := json.Marshal(webhookDelivery.RequestPayload) + if err != nil { + m := fmt.Sprintf("Failed to convert webhook event data to bytes, error:%v; Failed to redeliver payload for webhook for event:<%s>, user:<%s>, org:<%s>", err.Error(), webhookDelivery.WebhookEventType, webhookDelivery.UserID, organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + // Get the details of who triggered webhook + u, err := user.Get(webhookDelivery.UserID) + if err != nil { + m := fmt.Sprintf("Failed to get user, error:%v; Failed to redeliver payload for webhook for event:<%s>, user:<%s>, org:<%s>", err.Error(), webhookDelivery.WebhookEventType, webhookDelivery.UserID, organizationID) + common.HandleError(w, http.StatusInternalServerError, m, err) + return + } + + go wh.PushWebhookEventToKafkaTopic(webhookDelivery.WebhookEventType, webhookPayloadBytes, wh.WebhooksConfiguration.KafkaConfig.Topic) + + // Log webhook calls in webhooks category + aLog := fmt.Sprintf("Organization webhook: %v triggered by user: %v by event: %v", webhook.PayloadURL, u.Email, webhookDelivery.WebhookEventType) + actionlog.LogOrgWebhookCalls(u.ID.Hex(), u.Email, organizationID, aLog) + + w.Header().Set(config.ContentTypeHeader, config.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + +} diff --git a/src/main/audit_paths.go b/src/httppathsv1/audit_paths.go similarity index 72% rename from src/main/audit_paths.go rename to src/httppathsv1/audit_paths.go index 71511cc..29969fe 100644 --- a/src/main/audit_paths.go +++ b/src/httppathsv1/audit_paths.go @@ -1,3 +1,3 @@ -package main +package httppathsv1 const GetOrgLogs = "/v1/organizations/{orgID}/logs" diff --git a/src/main/config_paths.go b/src/httppathsv1/config_paths.go similarity index 99% rename from src/main/config_paths.go rename to src/httppathsv1/config_paths.go index 62758d3..c90ca55 100644 --- a/src/main/config_paths.go +++ b/src/httppathsv1/config_paths.go @@ -1,4 +1,4 @@ -package main +package httppathsv1 // Global policy configuration const GetGlobalPolicyConfiguration = "/v1/organizations/{organizationID}/global-policy-configuration" diff --git a/src/main/onboard_paths.go b/src/httppathsv1/onboard_paths.go similarity index 99% rename from src/main/onboard_paths.go rename to src/httppathsv1/onboard_paths.go index 2b9efb6..f3779e0 100644 --- a/src/main/onboard_paths.go +++ b/src/httppathsv1/onboard_paths.go @@ -1,4 +1,4 @@ -package main +package httppathsv1 const AddOrganization = "/v1/organizations" const GetOrganizationRoles = "/v1/organizations/roles" diff --git a/src/main/routes.go b/src/httppathsv1/routes.go similarity index 99% rename from src/main/routes.go rename to src/httppathsv1/routes.go index 8b15797..4898dde 100644 --- a/src/main/routes.go +++ b/src/httppathsv1/routes.go @@ -1,9 +1,9 @@ -package main +package httppathsv1 import ( "net/http" - "github.com/bb-consent/api/src/handler" + handler "github.com/bb-consent/api/src/handlerv1" m "github.com/bb-consent/api/src/middleware" "github.com/casbin/casbin/v2" "github.com/gorilla/mux" diff --git a/src/main/service_paths.go b/src/httppathsv1/service_paths.go similarity index 98% rename from src/main/service_paths.go rename to src/httppathsv1/service_paths.go index be9977f..8a008f3 100644 --- a/src/main/service_paths.go +++ b/src/httppathsv1/service_paths.go @@ -1,4 +1,4 @@ -package main +package httppathsv1 const GetUserConsentHistory = "/v1/users/{userID}/consenthistory" const GetConsentPurposeByID = "/v1/organizations/{orgID}/users/{userID}/consents/{consentID}/purposes/{purposeID}" diff --git a/src/httppathsv2/audit_paths.go b/src/httppathsv2/audit_paths.go new file mode 100644 index 0000000..4d9bf1a --- /dev/null +++ b/src/httppathsv2/audit_paths.go @@ -0,0 +1 @@ +package httppathsv2 diff --git a/src/httppathsv2/config_paths.go b/src/httppathsv2/config_paths.go new file mode 100644 index 0000000..3a5993d --- /dev/null +++ b/src/httppathsv2/config_paths.go @@ -0,0 +1,36 @@ +package httppathsv2 + +// Global policy configuration +const UpdateGlobalPolicyConfigurations = "/v2/config/policy" +const GetGlobalPolicyConfigurations = "/v2/config/policy" +const UpdateGlobalPolicyConfigurationById = "/v2/config/policy/{policyId}/" +const OrgListPolicyRevisions = "/v2/config/policy/{policyId}/revisions/" +const OrgDeletePolicy = "/v2/config/policy/{policyId}/" +const OrgListPolicy = "/v2/config/policies/" + +// Data agreements +const GetDataAgreementById = "/v2/config/data-agreement/{dataAgreementId}" +const AddDataAgreement = "/v2/config/data-agreement/" +const UpdateDataAgreement = "/v2/config/data-agreement/{dataAgreementId}" +const DeleteDataAgreement = "/v2/config/data-agreement/{dataAgreementId}" +const ListDataAgreements = "/v2/config/data-agreements" +const ListDataAgreementRevisions = "/v2/config/data-agreement/{dataAgreementId}/revisions" +const ReadDataAgreementRevision = "/v2/config/data-agreement/{dataAgreementId}/revision/{revisionId}" + +// Data attributes +const GetDataAttributes = "/v2/config/data-agreements/data-attributes" +const AddDataAttribute = "/v2/config/data-agreements/data-attribute" +const UpdateDataAttributeById = "/v2/config/data-agreements/data-attribute/{dataAttributeId}" +const DeleteDataAttributeById = "/v2/config/data-agreements/data-attribute/{dataAttributeId}" + +// Webhooks +const GetWebhookEventTypes = "/v2/config/webhooks/event-types" +const GetWebhookPayloadContentTypes = "/v2/config/webhooks/payload/content-types" +const GetAllWebhooks = "/v2/config/webhooks" +const CreateWebhook = "/v2/config/webhook" +const UpdateWebhook = " /v2/config/webhook/{webhookId}" +const DeleteWebhook = " /v2/config/webhook/{webhookId}" +const PingWebhook = " /v2/config/webhook/{webhookId}/ping" +const GetRecentWebhookDeliveries = " /v2/config/webhooks/{webhookId}/delivery" +const GetRecentWebhookDeliveryById = " /v2/config/webhooks/{webhookId}/delivery/{deliveryId}" +const RedeliverWebhookPayloadByDeliveryID = " /v2/config/webhooks/{webhookId}/delivery/{deliveryId}/redeliver" diff --git a/src/httppathsv2/onboard_paths.go b/src/httppathsv2/onboard_paths.go new file mode 100644 index 0000000..4d9bf1a --- /dev/null +++ b/src/httppathsv2/onboard_paths.go @@ -0,0 +1 @@ +package httppathsv2 diff --git a/src/httppathsv2/routes.go b/src/httppathsv2/routes.go new file mode 100644 index 0000000..018e583 --- /dev/null +++ b/src/httppathsv2/routes.go @@ -0,0 +1,45 @@ +package httppathsv2 + +import ( + handler "github.com/bb-consent/api/src/handlerv2" + m "github.com/bb-consent/api/src/middleware" + "github.com/casbin/casbin/v2" + "github.com/gorilla/mux" +) + +// SetRoutes sets the routes that the back end server serves +func SetRoutes(r *mux.Router, e *casbin.Enforcer) { + // Organization global policy configuration + r.Handle(GetGlobalPolicyConfigurations, m.Chain(handler.GetGlobalPolicyConfiguration, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("GET") + r.Handle(UpdateGlobalPolicyConfigurations, m.Chain(handler.UpdateGlobalPolicyConfiguration, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("POST") + r.Handle(UpdateGlobalPolicyConfigurationById, m.Chain(handler.UpdateGlobalPolicyConfigurationById, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("Put") + r.Handle(OrgListPolicyRevisions, m.Chain(handler.OrgListPolicyRevisions, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("GET") + r.Handle(OrgDeletePolicy, m.Chain(handler.OrgDeletePolicy, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("DELETE") + r.Handle(OrgListPolicy, m.Chain(handler.OrgListPolicy, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("GET") + + r.Handle(GetDataAgreementById, m.Chain(handler.GetDataAgreementById, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("GET") + r.Handle(AddDataAgreement, m.Chain(handler.AddDataAgreement, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("POST") + r.Handle(UpdateDataAgreement, m.Chain(handler.UpdateDataAgreement, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("PUT") + r.Handle(DeleteDataAgreement, m.Chain(handler.DeleteDataAgreement, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("DELETE") + r.Handle(ListDataAgreements, m.Chain(handler.ListDataAgreements, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("GET") + r.Handle(ListDataAgreementRevisions, m.Chain(handler.ListDataAgreementRevisions, m.Logger(), m.SetApplicationMode(), m.Authorize(e), m.Authenticate())).Methods("GET") + r.Handle(ReadDataAgreementRevision, m.Chain(handler.ReadDataAgreementRevision, m.Logger(), m.SetApplicationMode(), m.Authorize(e), m.Authenticate())).Methods("GET") + + r.Handle(GetDataAttributes, m.Chain(handler.GetDataAttributes, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("GET") + r.Handle(AddDataAttribute, m.Chain(handler.AddDataAttribute, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("POST") + r.Handle(UpdateDataAttributeById, m.Chain(handler.UpdateDataAttributeById, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("PUT") + r.Handle(DeleteDataAttributeById, m.Chain(handler.DeleteDataAttributeById, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("DELETE") + + // Organisation webhooks related api(s) + r.Handle(GetWebhookEventTypes, m.Chain(handler.GetWebhookEventTypes, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("GET") + r.Handle(GetWebhookPayloadContentTypes, m.Chain(handler.GetWebhookPayloadContentTypes, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("GET") + r.Handle(GetAllWebhooks, m.Chain(handler.GetAllWebhooks, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("GET") + r.Handle(CreateWebhook, m.Chain(handler.CreateWebhook, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("POST") + r.Handle(UpdateWebhook, m.Chain(handler.UpdateWebhook, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("PUT") + r.Handle(DeleteWebhook, m.Chain(handler.DeleteWebhook, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("DELETE") + r.Handle(PingWebhook, m.Chain(handler.PingWebhook, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("POST") + r.Handle(GetRecentWebhookDeliveries, m.Chain(handler.GetRecentWebhookDeliveries, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("GET") + r.Handle(GetRecentWebhookDeliveryById, m.Chain(handler.GetRecentWebhookDeliveryById, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("GET") + r.Handle(RedeliverWebhookPayloadByDeliveryID, m.Chain(handler.RedeliverWebhookPayloadByDeliveryID, m.Logger(), m.Authorize(e), m.SetApplicationMode(), m.Authenticate())).Methods("POST") + +} diff --git a/src/httppathsv2/service_paths.go b/src/httppathsv2/service_paths.go new file mode 100644 index 0000000..4d9bf1a --- /dev/null +++ b/src/httppathsv2/service_paths.go @@ -0,0 +1 @@ +package httppathsv2 diff --git a/src/main/main.go b/src/main/main.go index a7d11f0..badf86c 100644 --- a/src/main/main.go +++ b/src/main/main.go @@ -11,7 +11,9 @@ import ( "github.com/bb-consent/api/src/database" "github.com/bb-consent/api/src/email" "github.com/bb-consent/api/src/firebaseUtils" - "github.com/bb-consent/api/src/handler" + handler "github.com/bb-consent/api/src/handlerv1" + "github.com/bb-consent/api/src/httppathsv1" + "github.com/bb-consent/api/src/httppathsv2" "github.com/bb-consent/api/src/kafkaUtils" "github.com/bb-consent/api/src/middleware" "github.com/bb-consent/api/src/notifications" @@ -93,7 +95,8 @@ func main() { } router := mux.NewRouter() - SetRoutes(router, authEnforcer) + httppathsv1.SetRoutes(router, authEnforcer) + httppathsv2.SetRoutes(router, authEnforcer) log.Println("Listening port 80") http.ListenAndServe(":80", router) diff --git a/src/middleware/middleware.go b/src/middleware/middleware.go index 8943ec8..38c0bfd 100644 --- a/src/middleware/middleware.go +++ b/src/middleware/middleware.go @@ -7,7 +7,7 @@ import ( "time" "github.com/bb-consent/api/src/apikey" - "github.com/bb-consent/api/src/handler" + handler "github.com/bb-consent/api/src/handlerv1" "github.com/bb-consent/api/src/rbac" "github.com/casbin/casbin/v2" "github.com/gorilla/mux"