@@ -88,7 +88,7 @@ func MapSAMLRoleToSystemRole(samlRole string) *coredata.MembershipRole {
|
||||
|
||||
func isValidRole(role string) bool {
|
||||
switch role {
|
||||
case "OWNER", "ADMIN", "VIEWER":
|
||||
case "OWNER", "ADMIN", "EMPLOYEE", "VIEWER":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
||||
@@ -27,10 +27,11 @@ type (
|
||||
)
|
||||
|
||||
const (
|
||||
RoleOwner Role = "OWNER"
|
||||
RoleAdmin Role = "ADMIN"
|
||||
RoleViewer Role = "VIEWER"
|
||||
RoleFull Role = "FULL"
|
||||
RoleOwner Role = "OWNER"
|
||||
RoleAdmin Role = "ADMIN"
|
||||
RoleEmployee Role = "EMPLOYEE"
|
||||
RoleViewer Role = "VIEWER"
|
||||
RoleFull Role = "FULL"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -43,6 +44,7 @@ const (
|
||||
ActionGetBusinessOwner Action = "getBusinessOwner"
|
||||
ActionGetCustomDomain Action = "getCustomDomain"
|
||||
ActionGetDataPrivacyAgreement Action = "getDataPrivacyAgreement"
|
||||
ActionGetDocument Action = "getDocument"
|
||||
ActionGetFile Action = "getFile"
|
||||
ActionGetFileUrl Action = "getFileUrl"
|
||||
ActionGetFramework Action = "getFramework"
|
||||
@@ -53,6 +55,8 @@ const (
|
||||
ActionGetOrganization Action = "getOrganization"
|
||||
ActionGetOwner Action = "getOwner"
|
||||
ActionGetSecurityOwner Action = "getSecurityOwner"
|
||||
ActionGetSigned Action = "getSigned"
|
||||
ActionGetSignableDocument Action = "getSignableDocument"
|
||||
ActionGetSnapshot Action = "getSnapshot"
|
||||
ActionGetTask Action = "getTask"
|
||||
ActionGetTrustCenter Action = "getTrustCenter"
|
||||
@@ -62,7 +66,6 @@ const (
|
||||
ActionActiveCount Action = "activeCount"
|
||||
ActionAudit Action = "audit"
|
||||
ActionAvailableDocumentAccesses Action = "availableDocumentAccesses"
|
||||
ActionDocument Action = "document"
|
||||
ActionDocumentVersion Action = "documentVersion"
|
||||
ActionDownloadUrl Action = "downloadUrl"
|
||||
ActionMemberships Action = "memberships"
|
||||
@@ -77,36 +80,38 @@ const (
|
||||
ActionTotalCount Action = "totalCount"
|
||||
ActionTrustCenterFile Action = "trustCenterFile"
|
||||
|
||||
ActionListAccesses Action = "listAccesses"
|
||||
ActionListAssets Action = "listAssets"
|
||||
ActionListAudits Action = "listAudits"
|
||||
ActionListComplianceReports Action = "listComplianceReports"
|
||||
ActionListContacts Action = "listContacts"
|
||||
ActionListContinualImprovements Action = "listContinualImprovements"
|
||||
ActionListControls Action = "listControls"
|
||||
ActionListData Action = "listData"
|
||||
ActionListDocuments Action = "listDocuments"
|
||||
ActionListEvidences Action = "listEvidences"
|
||||
ActionListFrameworks Action = "listFrameworks"
|
||||
ActionListInvitations Action = "listInvitations"
|
||||
ActionListMeasures Action = "listMeasures"
|
||||
ActionListMeetings Action = "listMeetings"
|
||||
ActionListMembers Action = "listMembers"
|
||||
ActionListNonconformities Action = "listNonconformities"
|
||||
ActionListObligations Action = "listObligations"
|
||||
ActionListPeople Action = "listPeople"
|
||||
ActionListProcessingActivities Action = "listProcessingActivities"
|
||||
ActionListReferences Action = "listReferences"
|
||||
ActionListRiskAssessments Action = "listRiskAssessments"
|
||||
ActionListRisks Action = "listRisks"
|
||||
ActionListSAMLConfigurations Action = "listSAMLConfigurations"
|
||||
ActionListServices Action = "listServices"
|
||||
ActionListSlackConnections Action = "listSlackConnections"
|
||||
ActionListSnapshots Action = "listSnapshots"
|
||||
ActionListTasks Action = "listTasks"
|
||||
ActionListTrustCenterFiles Action = "listTrustCenterFiles"
|
||||
ActionListVendors Action = "listVendors"
|
||||
ActionListVersions Action = "listVersions"
|
||||
ActionListAccesses Action = "listAccesses"
|
||||
ActionListAssets Action = "listAssets"
|
||||
ActionListAudits Action = "listAudits"
|
||||
ActionListComplianceReports Action = "listComplianceReports"
|
||||
ActionListContacts Action = "listContacts"
|
||||
ActionListContinualImprovements Action = "listContinualImprovements"
|
||||
ActionListControls Action = "listControls"
|
||||
ActionListData Action = "listData"
|
||||
ActionListDocuments Action = "listDocuments"
|
||||
ActionListEvidences Action = "listEvidences"
|
||||
ActionListFrameworks Action = "listFrameworks"
|
||||
ActionListInvitations Action = "listInvitations"
|
||||
ActionListMeasures Action = "listMeasures"
|
||||
ActionListMeetings Action = "listMeetings"
|
||||
ActionListMembers Action = "listMembers"
|
||||
ActionListNonconformities Action = "listNonconformities"
|
||||
ActionListObligations Action = "listObligations"
|
||||
ActionListPeople Action = "listPeople"
|
||||
ActionListProcessingActivities Action = "listProcessingActivities"
|
||||
ActionListReferences Action = "listReferences"
|
||||
ActionListRiskAssessments Action = "listRiskAssessments"
|
||||
ActionListRisks Action = "listRisks"
|
||||
ActionListSAMLConfigurations Action = "listSAMLConfigurations"
|
||||
ActionListServices Action = "listServices"
|
||||
ActionListSlackConnections Action = "listSlackConnections"
|
||||
ActionListSnapshots Action = "listSnapshots"
|
||||
ActionListTasks Action = "listTasks"
|
||||
ActionListTrustCenterFiles Action = "listTrustCenterFiles"
|
||||
ActionListVendors Action = "listVendors"
|
||||
ActionListVersions Action = "listVersions"
|
||||
ActionListSignableDocuments Action = "listSignableDocuments"
|
||||
ActionListSignableDocumentVersion Action = "listSignableDocumentVersion"
|
||||
|
||||
ActionCreateAsset Action = "createAsset"
|
||||
ActionCreateAudit Action = "createAudit"
|
||||
@@ -222,10 +227,12 @@ const (
|
||||
ActionBulkPublishDocumentVersions Action = "bulkPublishDocumentVersions"
|
||||
ActionBulkRequestSignatures Action = "bulkRequestSignatures"
|
||||
ActionCancelSignatureRequest Action = "cancelSignatureRequest"
|
||||
ActionSignDocument Action = "signDocument"
|
||||
ActionConfirmEmail Action = "confirmEmail"
|
||||
ActionDisableSAML Action = "disableSAML"
|
||||
ActionEnableSAML Action = "enableSAML"
|
||||
ActionExportDocumentVersionPDF Action = "exportDocumentVersionPDF"
|
||||
ActionExportSignableVersionDocumentPDF Action = "exportSignableVersionDocumentPDF"
|
||||
ActionExportFramework Action = "exportFramework"
|
||||
ActionGenerateDocumentChangelog Action = "generateDocumentChangelog"
|
||||
ActionGenerateFrameworkStateOfApplicability Action = "generateFrameworkStateOfApplicability"
|
||||
@@ -248,45 +255,47 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
AllRoles = []Role{RoleOwner, RoleAdmin, RoleViewer, RoleFull}
|
||||
EditRoles = []Role{RoleOwner, RoleAdmin, RoleFull}
|
||||
AllRoles = []Role{RoleOwner, RoleAdmin, RoleEmployee, RoleViewer, RoleFull}
|
||||
NonEmployeeRoles = []Role{RoleOwner, RoleAdmin, RoleViewer, RoleFull}
|
||||
EditRoles = []Role{RoleOwner, RoleAdmin, RoleFull}
|
||||
)
|
||||
|
||||
var Permissions = map[uint16]map[Action][]Role{
|
||||
coredata.OrganizationEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetLogoUrl: AllRoles,
|
||||
ActionGetHorizontalLogoUrl: AllRoles,
|
||||
ActionMemberships: AllRoles,
|
||||
ActionPeoples: AllRoles,
|
||||
ActionTotalCount: AllRoles,
|
||||
ActionListMembers: AllRoles,
|
||||
ActionListInvitations: AllRoles,
|
||||
ActionListSlackConnections: AllRoles,
|
||||
ActionListFrameworks: AllRoles,
|
||||
ActionListControls: AllRoles,
|
||||
ActionListVendors: AllRoles,
|
||||
ActionListPeople: AllRoles,
|
||||
ActionListDocuments: AllRoles,
|
||||
ActionListMeetings: AllRoles,
|
||||
ActionListMeasures: AllRoles,
|
||||
ActionListRisks: AllRoles,
|
||||
ActionListTasks: AllRoles,
|
||||
ActionListAssets: AllRoles,
|
||||
ActionListData: AllRoles,
|
||||
ActionListAudits: AllRoles,
|
||||
ActionListNonconformities: AllRoles,
|
||||
ActionListObligations: AllRoles,
|
||||
ActionListContinualImprovements: AllRoles,
|
||||
ActionListProcessingActivities: AllRoles,
|
||||
ActionListSnapshots: AllRoles,
|
||||
ActionListTrustCenterFiles: AllRoles,
|
||||
ActionGetTrustCenter: AllRoles,
|
||||
ActionAudit: AllRoles,
|
||||
ActionGetCustomDomain: AllRoles,
|
||||
ActionListSAMLConfigurations: AllRoles,
|
||||
ActionConfirmEmail: AllRoles,
|
||||
ActionAcceptInvitation: AllRoles,
|
||||
ActionGet: AllRoles,
|
||||
ActionListSignableDocuments: AllRoles,
|
||||
ActionGetLogoUrl: AllRoles,
|
||||
|
||||
ActionListDocuments: NonEmployeeRoles,
|
||||
ActionGetHorizontalLogoUrl: NonEmployeeRoles,
|
||||
ActionMemberships: NonEmployeeRoles,
|
||||
ActionPeoples: NonEmployeeRoles,
|
||||
ActionTotalCount: NonEmployeeRoles,
|
||||
ActionListMembers: NonEmployeeRoles,
|
||||
ActionListInvitations: NonEmployeeRoles,
|
||||
ActionListSlackConnections: NonEmployeeRoles,
|
||||
ActionListFrameworks: NonEmployeeRoles,
|
||||
ActionListControls: NonEmployeeRoles,
|
||||
ActionListVendors: NonEmployeeRoles,
|
||||
ActionListPeople: NonEmployeeRoles,
|
||||
ActionListMeetings: NonEmployeeRoles,
|
||||
ActionListMeasures: NonEmployeeRoles,
|
||||
ActionListRisks: NonEmployeeRoles,
|
||||
ActionListTasks: NonEmployeeRoles,
|
||||
ActionListAssets: NonEmployeeRoles,
|
||||
ActionListData: NonEmployeeRoles,
|
||||
ActionListAudits: NonEmployeeRoles,
|
||||
ActionListNonconformities: NonEmployeeRoles,
|
||||
ActionListObligations: NonEmployeeRoles,
|
||||
ActionListContinualImprovements: NonEmployeeRoles,
|
||||
ActionListProcessingActivities: NonEmployeeRoles,
|
||||
ActionListSnapshots: NonEmployeeRoles,
|
||||
ActionListTrustCenterFiles: NonEmployeeRoles,
|
||||
ActionGetTrustCenter: NonEmployeeRoles,
|
||||
ActionGetCustomDomain: NonEmployeeRoles,
|
||||
ActionListSAMLConfigurations: NonEmployeeRoles,
|
||||
ActionConfirmEmail: NonEmployeeRoles,
|
||||
ActionAcceptInvitation: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateOrganization: EditRoles,
|
||||
ActionDeleteOrganizationHorizontalLogo: EditRoles,
|
||||
@@ -325,11 +334,11 @@ var Permissions = map[uint16]map[Action][]Role{
|
||||
ActionDeleteOrganization: {RoleOwner},
|
||||
},
|
||||
coredata.TrustCenterEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetNdaFileUrl: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionListAccesses: AllRoles,
|
||||
ActionListReferences: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetNdaFileUrl: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
ActionListAccesses: NonEmployeeRoles,
|
||||
ActionListReferences: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateTrustCenter: EditRoles,
|
||||
ActionUploadTrustCenterNDA: EditRoles,
|
||||
@@ -338,62 +347,59 @@ var Permissions = map[uint16]map[Action][]Role{
|
||||
ActionCreateTrustCenterReference: EditRoles,
|
||||
},
|
||||
coredata.TrustCenterAccessEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionActiveCount: AllRoles,
|
||||
ActionPendingRequestCount: AllRoles,
|
||||
ActionAvailableDocumentAccesses: AllRoles,
|
||||
ActionDocument: AllRoles,
|
||||
ActionReport: AllRoles,
|
||||
ActionTrustCenterFile: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionActiveCount: NonEmployeeRoles,
|
||||
ActionPendingRequestCount: NonEmployeeRoles,
|
||||
ActionAvailableDocumentAccesses: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateTrustCenterAccess: EditRoles,
|
||||
ActionDeleteTrustCenterAccess: EditRoles,
|
||||
},
|
||||
coredata.TrustCenterReferenceEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetLogoUrl: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetLogoUrl: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateTrustCenterReference: EditRoles,
|
||||
ActionDeleteTrustCenterReference: EditRoles,
|
||||
},
|
||||
coredata.TrustCenterFileEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetFileUrl: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetFileUrl: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateTrustCenterFile: EditRoles,
|
||||
ActionGetTrustCenterFile: EditRoles,
|
||||
ActionDeleteTrustCenterFile: EditRoles,
|
||||
},
|
||||
coredata.UserEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
},
|
||||
coredata.MembershipEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetAuthMethod: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetAuthMethod: NonEmployeeRoles,
|
||||
},
|
||||
coredata.InvitationEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
|
||||
ActionDeleteInvitation: EditRoles,
|
||||
},
|
||||
coredata.PeopleEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
|
||||
ActionUpdatePeople: EditRoles,
|
||||
ActionDeletePeople: EditRoles,
|
||||
},
|
||||
coredata.VendorEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionListComplianceReports: AllRoles,
|
||||
ActionGetBusinessAssociateAgreement: AllRoles,
|
||||
ActionGetDataPrivacyAgreement: AllRoles,
|
||||
ActionListContacts: AllRoles,
|
||||
ActionListServices: AllRoles,
|
||||
ActionListRiskAssessments: AllRoles,
|
||||
ActionGetBusinessOwner: AllRoles,
|
||||
ActionGetSecurityOwner: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
ActionListComplianceReports: NonEmployeeRoles,
|
||||
ActionGetBusinessAssociateAgreement: NonEmployeeRoles,
|
||||
ActionGetDataPrivacyAgreement: NonEmployeeRoles,
|
||||
ActionListContacts: NonEmployeeRoles,
|
||||
ActionListServices: NonEmployeeRoles,
|
||||
ActionListRiskAssessments: NonEmployeeRoles,
|
||||
ActionGetBusinessOwner: NonEmployeeRoles,
|
||||
ActionGetSecurityOwner: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateVendor: EditRoles,
|
||||
ActionDeleteVendor: EditRoles,
|
||||
@@ -407,49 +413,49 @@ var Permissions = map[uint16]map[Action][]Role{
|
||||
ActionAssessVendor: EditRoles,
|
||||
},
|
||||
coredata.VendorComplianceReportEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetVendor: AllRoles,
|
||||
ActionGetFile: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetVendor: NonEmployeeRoles,
|
||||
ActionGetFile: NonEmployeeRoles,
|
||||
|
||||
ActionDeleteVendorComplianceReport: EditRoles,
|
||||
},
|
||||
coredata.VendorBusinessAssociateAgreementEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetVendor: AllRoles,
|
||||
ActionGetFileUrl: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetVendor: NonEmployeeRoles,
|
||||
ActionGetFileUrl: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateVendorBusinessAssociateAgreement: EditRoles,
|
||||
ActionDeleteVendorBusinessAssociateAgreement: EditRoles,
|
||||
},
|
||||
coredata.VendorContactEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetVendor: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetVendor: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateVendorContact: EditRoles,
|
||||
ActionDeleteVendorContact: EditRoles,
|
||||
},
|
||||
coredata.VendorServiceEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetVendor: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetVendor: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateVendorService: EditRoles,
|
||||
ActionDeleteVendorService: EditRoles,
|
||||
},
|
||||
coredata.VendorDataPrivacyAgreementEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetVendor: AllRoles,
|
||||
ActionGetFileUrl: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetVendor: NonEmployeeRoles,
|
||||
ActionGetFileUrl: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateVendorDataPrivacyAgreement: EditRoles,
|
||||
ActionDeleteVendorDataPrivacyAgreement: EditRoles,
|
||||
},
|
||||
coredata.VendorRiskAssessmentEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
},
|
||||
coredata.FrameworkEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionListControls: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
ActionListControls: NonEmployeeRoles,
|
||||
|
||||
ActionCreateControl: EditRoles,
|
||||
ActionUpdateFramework: EditRoles,
|
||||
@@ -458,12 +464,12 @@ var Permissions = map[uint16]map[Action][]Role{
|
||||
ActionExportFramework: EditRoles,
|
||||
},
|
||||
coredata.ControlEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetFramework: AllRoles,
|
||||
ActionListMeasures: AllRoles,
|
||||
ActionListDocuments: AllRoles,
|
||||
ActionListAudits: AllRoles,
|
||||
ActionListSnapshots: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetFramework: NonEmployeeRoles,
|
||||
ActionListMeasures: NonEmployeeRoles,
|
||||
ActionListDocuments: NonEmployeeRoles,
|
||||
ActionListAudits: NonEmployeeRoles,
|
||||
ActionListSnapshots: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateControl: EditRoles,
|
||||
ActionDeleteControl: EditRoles,
|
||||
@@ -477,23 +483,23 @@ var Permissions = map[uint16]map[Action][]Role{
|
||||
ActionDeleteControlSnapshotMapping: EditRoles,
|
||||
},
|
||||
coredata.MeasureEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionListEvidences: AllRoles,
|
||||
ActionListTasks: AllRoles,
|
||||
ActionListRisks: AllRoles,
|
||||
ActionListControls: AllRoles,
|
||||
ActionTotalCount: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionListEvidences: NonEmployeeRoles,
|
||||
ActionListTasks: NonEmployeeRoles,
|
||||
ActionListRisks: NonEmployeeRoles,
|
||||
ActionListControls: NonEmployeeRoles,
|
||||
ActionTotalCount: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateMeasure: EditRoles,
|
||||
ActionDeleteMeasure: EditRoles,
|
||||
ActionUploadMeasureEvidence: EditRoles,
|
||||
},
|
||||
coredata.TaskEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetAssignedTo: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionGetMeasure: AllRoles,
|
||||
ActionListEvidences: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetAssignedTo: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
ActionGetMeasure: NonEmployeeRoles,
|
||||
ActionListEvidences: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateTask: EditRoles,
|
||||
ActionDeleteTask: EditRoles,
|
||||
@@ -501,28 +507,31 @@ var Permissions = map[uint16]map[Action][]Role{
|
||||
ActionUnassignTask: EditRoles,
|
||||
},
|
||||
coredata.EvidenceEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetFile: AllRoles,
|
||||
ActionGetTask: AllRoles,
|
||||
ActionGetMeasure: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetFile: NonEmployeeRoles,
|
||||
ActionGetTask: NonEmployeeRoles,
|
||||
ActionGetMeasure: NonEmployeeRoles,
|
||||
|
||||
ActionDeleteEvidence: EditRoles,
|
||||
},
|
||||
coredata.DocumentEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionExportDocumentVersionPDF: AllRoles,
|
||||
ActionGetOwner: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionListVersions: AllRoles,
|
||||
ActionListControls: AllRoles,
|
||||
ActionTotalCount: AllRoles,
|
||||
ActionListSignableDocumentVersion: AllRoles,
|
||||
ActionGetSigned: AllRoles,
|
||||
ActionGetSignableDocument: AllRoles,
|
||||
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetOwner: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
ActionBulkExportDocuments: NonEmployeeRoles,
|
||||
ActionTotalCount: NonEmployeeRoles,
|
||||
ActionListControls: NonEmployeeRoles,
|
||||
ActionListVersions: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateDocument: EditRoles,
|
||||
ActionDeleteDocument: EditRoles,
|
||||
ActionPublishDocumentVersion: EditRoles,
|
||||
ActionBulkPublishDocumentVersions: EditRoles,
|
||||
ActionBulkDeleteDocuments: EditRoles,
|
||||
ActionBulkExportDocuments: EditRoles,
|
||||
ActionGenerateDocumentChangelog: EditRoles,
|
||||
ActionCreateDraftDocumentVersion: EditRoles,
|
||||
ActionDeleteDraftDocumentVersion: EditRoles,
|
||||
@@ -533,33 +542,35 @@ var Permissions = map[uint16]map[Action][]Role{
|
||||
ActionCancelSignatureRequest: EditRoles,
|
||||
},
|
||||
coredata.DocumentVersionEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetFile: AllRoles,
|
||||
ActionGetOwner: AllRoles,
|
||||
ActionDocument: AllRoles,
|
||||
ActionSignatures: AllRoles,
|
||||
ActionExportDocumentVersionPDF: AllRoles,
|
||||
ActionSignDocument: AllRoles,
|
||||
|
||||
ActionUpdateDocumentVersion: EditRoles,
|
||||
ActionRequestSignature: EditRoles,
|
||||
ActionBulkRequestSignatures: EditRoles,
|
||||
ActionSendSigningNotifications: EditRoles,
|
||||
ActionCancelSignatureRequest: EditRoles,
|
||||
ActionExportSignableVersionDocumentPDF: AllRoles,
|
||||
ActionGetSigned: AllRoles,
|
||||
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetFile: NonEmployeeRoles,
|
||||
ActionGetOwner: NonEmployeeRoles,
|
||||
ActionGetDocument: NonEmployeeRoles,
|
||||
ActionSignatures: NonEmployeeRoles,
|
||||
ActionExportDocumentVersionPDF: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateDocumentVersion: EditRoles,
|
||||
ActionRequestSignature: EditRoles,
|
||||
},
|
||||
coredata.DocumentVersionSignatureEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionDocumentVersion: AllRoles,
|
||||
ActionSignedBy: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionDocumentVersion: NonEmployeeRoles,
|
||||
ActionSignedBy: NonEmployeeRoles,
|
||||
},
|
||||
coredata.RiskEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetOwner: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionTotalCount: AllRoles,
|
||||
ActionListControls: AllRoles,
|
||||
ActionListMeasures: AllRoles,
|
||||
ActionListDocuments: AllRoles,
|
||||
ActionListObligations: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetOwner: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
ActionTotalCount: NonEmployeeRoles,
|
||||
ActionListControls: NonEmployeeRoles,
|
||||
ActionListMeasures: NonEmployeeRoles,
|
||||
ActionListDocuments: NonEmployeeRoles,
|
||||
ActionListObligations: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateRisk: EditRoles,
|
||||
ActionDeleteRisk: EditRoles,
|
||||
@@ -571,32 +582,32 @@ var Permissions = map[uint16]map[Action][]Role{
|
||||
ActionDeleteRiskObligationMapping: EditRoles,
|
||||
},
|
||||
coredata.AssetEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetOwner: AllRoles,
|
||||
ActionListVendors: AllRoles,
|
||||
ActionGetAssetType: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetOwner: NonEmployeeRoles,
|
||||
ActionListVendors: NonEmployeeRoles,
|
||||
ActionGetAssetType: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateAsset: EditRoles,
|
||||
ActionDeleteAsset: EditRoles,
|
||||
},
|
||||
coredata.DatumEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetOwner: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionListVendors: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetOwner: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
ActionListVendors: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateDatum: EditRoles,
|
||||
ActionDeleteDatum: EditRoles,
|
||||
},
|
||||
coredata.AuditEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetFile: AllRoles,
|
||||
ActionGetFramework: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionReport: AllRoles,
|
||||
ActionReportUrl: AllRoles,
|
||||
ActionListControls: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetFile: NonEmployeeRoles,
|
||||
ActionGetFramework: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
ActionReport: NonEmployeeRoles,
|
||||
ActionReportUrl: NonEmployeeRoles,
|
||||
ActionListControls: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateAudit: EditRoles,
|
||||
ActionDeleteAudit: EditRoles,
|
||||
@@ -604,51 +615,50 @@ var Permissions = map[uint16]map[Action][]Role{
|
||||
ActionDeleteAuditReport: EditRoles,
|
||||
},
|
||||
coredata.ReportEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetFile: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionGetSnapshot: AllRoles,
|
||||
ActionDownloadUrl: AllRoles,
|
||||
ActionAudit: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetFile: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
ActionGetSnapshot: NonEmployeeRoles,
|
||||
ActionDownloadUrl: NonEmployeeRoles,
|
||||
},
|
||||
coredata.NonconformityEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetOwner: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionAudit: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetOwner: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
ActionAudit: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateNonconformity: EditRoles,
|
||||
ActionDeleteNonconformity: EditRoles,
|
||||
},
|
||||
coredata.ObligationEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionGetOwner: AllRoles,
|
||||
ActionListRisks: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
ActionGetOwner: NonEmployeeRoles,
|
||||
ActionListRisks: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateObligation: EditRoles,
|
||||
ActionDeleteObligation: EditRoles,
|
||||
},
|
||||
coredata.ContinualImprovementEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetOwner: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetOwner: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateContinualImprovement: EditRoles,
|
||||
ActionDeleteContinualImprovement: EditRoles,
|
||||
},
|
||||
coredata.ProcessingActivityEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionListVendors: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
ActionListVendors: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateProcessingActivity: EditRoles,
|
||||
ActionDeleteProcessingActivity: EditRoles,
|
||||
},
|
||||
coredata.SnapshotEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionListControls: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
ActionListControls: NonEmployeeRoles,
|
||||
|
||||
ActionDeleteSnapshot: EditRoles,
|
||||
},
|
||||
@@ -669,19 +679,18 @@ var Permissions = map[uint16]map[Action][]Role{
|
||||
ActionVerifyDomain: {RoleOwner},
|
||||
},
|
||||
coredata.FileEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionDownloadUrl: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionDownloadUrl: NonEmployeeRoles,
|
||||
},
|
||||
coredata.TrustCenterDocumentAccessEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionDocument: {RoleOwner, RoleAdmin},
|
||||
ActionReport: {RoleOwner, RoleAdmin},
|
||||
ActionTrustCenterFile: {RoleOwner, RoleAdmin},
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionReport: NonEmployeeRoles,
|
||||
ActionTrustCenterFile: NonEmployeeRoles,
|
||||
},
|
||||
coredata.MeetingEntityType: {
|
||||
ActionGet: AllRoles,
|
||||
ActionGetOrganization: AllRoles,
|
||||
ActionTotalCount: AllRoles,
|
||||
ActionGet: NonEmployeeRoles,
|
||||
ActionGetOrganization: NonEmployeeRoles,
|
||||
ActionTotalCount: NonEmployeeRoles,
|
||||
|
||||
ActionUpdateMeeting: EditRoles,
|
||||
ActionDeleteMeeting: EditRoles,
|
||||
|
||||
@@ -124,6 +124,60 @@ LIMIT 1;
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Document) LoadByIDWithFilter(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
documentID gid.GID,
|
||||
filter *DocumentFilter,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
current_published_version,
|
||||
trust_center_visibility,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
documents
|
||||
WHERE
|
||||
%s
|
||||
AND deleted_at IS NULL
|
||||
AND id = @document_id
|
||||
AND %s
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"document_id": documentID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
maps.Copy(args, filter.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query documents: %w", err)
|
||||
}
|
||||
|
||||
document, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Document])
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return &ErrDocumentNotFound{Identifier: documentID.String()}
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot collect document: %w", err)
|
||||
}
|
||||
|
||||
*p = document
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Documents) CountByOrganizationID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
@@ -400,28 +454,17 @@ func (p *Documents) CountByControlID(
|
||||
filter *DocumentFilter,
|
||||
) (int, error) {
|
||||
q := `
|
||||
WITH plcs AS (
|
||||
SELECT
|
||||
p.id,
|
||||
p.tenant_id,
|
||||
p.search_vector,
|
||||
p.trust_center_visibility,
|
||||
p.deleted_at
|
||||
FROM
|
||||
documents p
|
||||
INNER JOIN
|
||||
controls_documents cp ON p.id = cp.document_id
|
||||
WHERE
|
||||
cp.control_id = @control_id
|
||||
WITH scoped_documents AS (
|
||||
SELECT *
|
||||
FROM documents
|
||||
WHERE %s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
)
|
||||
SELECT
|
||||
COUNT(id)
|
||||
FROM
|
||||
plcs
|
||||
WHERE
|
||||
%s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
SELECT COUNT(scoped_documents.id)
|
||||
FROM scoped_documents
|
||||
INNER JOIN controls_documents cp ON scoped_documents.id = cp.document_id
|
||||
WHERE cp.control_id = @control_id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
|
||||
@@ -448,46 +491,28 @@ func (p *Documents) LoadByControlID(
|
||||
filter *DocumentFilter,
|
||||
) error {
|
||||
q := `
|
||||
WITH plcs AS (
|
||||
SELECT
|
||||
p.id,
|
||||
p.tenant_id,
|
||||
p.search_vector,
|
||||
p.organization_id,
|
||||
p.owner_id,
|
||||
p.title,
|
||||
p.document_type,
|
||||
p.classification,
|
||||
p.current_published_version,
|
||||
p.trust_center_visibility,
|
||||
p.created_at,
|
||||
p.updated_at,
|
||||
p.deleted_at
|
||||
FROM
|
||||
documents p
|
||||
INNER JOIN
|
||||
controls_documents cp ON p.id = cp.document_id
|
||||
WHERE
|
||||
cp.control_id = @control_id
|
||||
WITH scoped_documents AS (
|
||||
SELECT *
|
||||
FROM documents
|
||||
WHERE %s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
AND %s
|
||||
)
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
current_published_version,
|
||||
trust_center_visibility,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
plcs
|
||||
WHERE
|
||||
%s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
AND %s
|
||||
scoped_documents.id,
|
||||
scoped_documents.organization_id,
|
||||
scoped_documents.owner_id,
|
||||
scoped_documents.title,
|
||||
scoped_documents.document_type,
|
||||
scoped_documents.classification,
|
||||
scoped_documents.current_published_version,
|
||||
scoped_documents.trust_center_visibility,
|
||||
scoped_documents.created_at,
|
||||
scoped_documents.updated_at
|
||||
FROM scoped_documents
|
||||
INNER JOIN controls_documents cp ON scoped_documents.id = cp.document_id
|
||||
WHERE cp.control_id = @control_id
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
@@ -519,28 +544,17 @@ func (p *Documents) CountByRiskID(
|
||||
filter *DocumentFilter,
|
||||
) (int, error) {
|
||||
q := `
|
||||
WITH plcs AS (
|
||||
SELECT
|
||||
p.id,
|
||||
p.tenant_id,
|
||||
p.search_vector,
|
||||
p.trust_center_visibility,
|
||||
p.deleted_at
|
||||
FROM
|
||||
documents p
|
||||
INNER JOIN
|
||||
risks_documents rp ON p.id = rp.document_id
|
||||
WHERE
|
||||
rp.risk_id = @risk_id
|
||||
WITH scoped_documents AS (
|
||||
SELECT *
|
||||
FROM documents
|
||||
WHERE %s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
)
|
||||
SELECT
|
||||
COUNT(id)
|
||||
FROM
|
||||
plcs
|
||||
WHERE
|
||||
%s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
SELECT COUNT(scoped_documents.id)
|
||||
FROM scoped_documents
|
||||
INNER JOIN risks_documents rp ON scoped_documents.id = rp.document_id
|
||||
WHERE rp.risk_id = @risk_id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
|
||||
@@ -567,46 +581,28 @@ func (p *Documents) LoadByRiskID(
|
||||
filter *DocumentFilter,
|
||||
) error {
|
||||
q := `
|
||||
WITH plcs AS (
|
||||
SELECT
|
||||
p.id,
|
||||
p.tenant_id,
|
||||
p.organization_id,
|
||||
p.owner_id,
|
||||
p.title,
|
||||
p.document_type,
|
||||
p.classification,
|
||||
p.current_published_version,
|
||||
p.trust_center_visibility,
|
||||
p.created_at,
|
||||
p.updated_at,
|
||||
p.search_vector,
|
||||
p.deleted_at
|
||||
FROM
|
||||
documents p
|
||||
INNER JOIN
|
||||
risks_documents rp ON p.id = rp.document_id
|
||||
WHERE
|
||||
rp.risk_id = @risk_id
|
||||
WITH scoped_documents AS (
|
||||
SELECT *
|
||||
FROM documents
|
||||
WHERE %s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
AND %s
|
||||
)
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
current_published_version,
|
||||
trust_center_visibility,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
plcs
|
||||
WHERE
|
||||
%s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
AND %s
|
||||
scoped_documents.id,
|
||||
scoped_documents.organization_id,
|
||||
scoped_documents.owner_id,
|
||||
scoped_documents.title,
|
||||
scoped_documents.document_type,
|
||||
scoped_documents.classification,
|
||||
scoped_documents.current_published_version,
|
||||
scoped_documents.trust_center_visibility,
|
||||
scoped_documents.created_at,
|
||||
scoped_documents.updated_at
|
||||
FROM scoped_documents
|
||||
INNER JOIN risks_documents rp ON scoped_documents.id = rp.document_id
|
||||
WHERE rp.risk_id = @risk_id
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
@@ -653,3 +649,61 @@ UPDATE documents SET deleted_at = @deleted_at WHERE %s AND id = ANY(@document_id
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Document) IsLastSignableVersionSignedByUserEmail(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
documentID gid.GID,
|
||||
userEmail string,
|
||||
) (bool, error) {
|
||||
q := `
|
||||
WITH last_signable_version AS (
|
||||
SELECT
|
||||
d.id AS document_id,
|
||||
d.tenant_id,
|
||||
dv.version_number,
|
||||
dvs.state
|
||||
FROM documents d
|
||||
INNER JOIN document_versions dv ON dv.document_id = d.id
|
||||
INNER JOIN document_version_signatures dvs ON dvs.document_version_id = dv.id
|
||||
INNER JOIN peoples p ON dvs.signed_by = p.id
|
||||
WHERE d.id = @document_id
|
||||
AND p.primary_email_address = @user_email
|
||||
AND dv.version_number = (
|
||||
SELECT MAX(dv2.version_number)
|
||||
FROM document_versions dv2
|
||||
INNER JOIN document_version_signatures dvs2 ON dvs2.document_version_id = dv2.id
|
||||
INNER JOIN peoples p2 ON dvs2.signed_by = p2.id
|
||||
WHERE dv2.document_id = d.id
|
||||
AND p2.primary_email_address = @user_email
|
||||
)
|
||||
)
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM last_signable_version
|
||||
WHERE %s
|
||||
AND state = 'SIGNED'
|
||||
) AS signed
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"document_id": documentID,
|
||||
"user_email": userEmail,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cannot query document signed status: %w", err)
|
||||
}
|
||||
|
||||
signed, err := pgx.CollectOneRow(rows, pgx.RowTo[bool])
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cannot collect signed status: %w", err)
|
||||
}
|
||||
|
||||
return signed, nil
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ type (
|
||||
DocumentFilter struct {
|
||||
query *string
|
||||
trustCenterVisibilities []TrustCenterVisibility
|
||||
published *bool
|
||||
userEmail *string
|
||||
}
|
||||
)
|
||||
|
||||
@@ -40,7 +42,17 @@ func NewDocumentTrustCenterFilter() *DocumentFilter {
|
||||
}
|
||||
}
|
||||
|
||||
func (f *DocumentFilter) SQLArguments() pgx.StrictNamedArgs {
|
||||
func (f *DocumentFilter) WithPublished(published *bool) *DocumentFilter {
|
||||
f.published = published
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *DocumentFilter) WithUserEmail(userEmail *string) *DocumentFilter {
|
||||
f.userEmail = userEmail
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *DocumentFilter) SQLArguments() pgx.NamedArgs {
|
||||
var visibilities []string
|
||||
if f.trustCenterVisibilities != nil {
|
||||
visibilities = make([]string, len(f.trustCenterVisibilities))
|
||||
@@ -48,9 +60,11 @@ func (f *DocumentFilter) SQLArguments() pgx.StrictNamedArgs {
|
||||
visibilities[i] = v.String()
|
||||
}
|
||||
}
|
||||
return pgx.StrictNamedArgs{
|
||||
return pgx.NamedArgs{
|
||||
"query": f.query,
|
||||
"trust_center_visibilities": visibilities,
|
||||
"published": f.published,
|
||||
"user_email": f.userEmail,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,5 +85,25 @@ func (f *DocumentFilter) SQLFragment() string {
|
||||
trust_center_visibility = ANY(@trust_center_visibilities::trust_center_visibility[])
|
||||
ELSE TRUE
|
||||
END
|
||||
AND
|
||||
CASE
|
||||
WHEN @published::boolean IS NULL THEN TRUE
|
||||
WHEN @published::boolean IS TRUE THEN current_published_version IS NOT NULL
|
||||
WHEN @published::boolean IS FALSE THEN current_published_version IS NULL
|
||||
END
|
||||
AND
|
||||
CASE
|
||||
WHEN @user_email::text IS NULL THEN TRUE
|
||||
ELSE EXISTS (
|
||||
SELECT 1
|
||||
FROM document_versions dv
|
||||
INNER JOIN document_version_signatures dvs ON dv.id = dvs.document_version_id
|
||||
INNER JOIN peoples p ON dvs.signed_by = p.id
|
||||
WHERE dv.document_id = documents.id
|
||||
AND dv.status = 'PUBLISHED'
|
||||
AND p.primary_email_address = @user_email::text
|
||||
AND dvs.state IN ('REQUESTED', 'SIGNED')
|
||||
)
|
||||
END
|
||||
)`
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ func (p *DocumentVersions) LoadByDocumentID(
|
||||
scope Scoper,
|
||||
documentID gid.GID,
|
||||
cursor *page.Cursor[DocumentVersionOrderField],
|
||||
filter *DocumentVersionFilter,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
@@ -100,14 +101,16 @@ WHERE
|
||||
%s
|
||||
AND document_id = @document_id
|
||||
AND %s
|
||||
AND %s
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"document_id": documentID,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
maps.Copy(args, cursor.SQLArguments())
|
||||
maps.Copy(args, filter.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
|
||||
55
pkg/coredata/document_version_filter.go
Normal file
55
pkg/coredata/document_version_filter.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package coredata
|
||||
|
||||
import (
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
type (
|
||||
DocumentVersionFilter struct {
|
||||
userEmail *string
|
||||
}
|
||||
)
|
||||
|
||||
func NewDocumentVersionFilter() *DocumentVersionFilter {
|
||||
return &DocumentVersionFilter{}
|
||||
}
|
||||
|
||||
func (f *DocumentVersionFilter) WithUserEmail(userEmail *string) *DocumentVersionFilter {
|
||||
f.userEmail = userEmail
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *DocumentVersionFilter) SQLArguments() pgx.StrictNamedArgs {
|
||||
return pgx.StrictNamedArgs{
|
||||
"user_email": f.userEmail,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *DocumentVersionFilter) SQLFragment() string {
|
||||
return `
|
||||
(
|
||||
@user_email::text IS NULL
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM document_version_signatures dvs
|
||||
INNER JOIN peoples p ON dvs.signed_by = p.id
|
||||
WHERE dvs.document_version_id = document_versions.id
|
||||
AND p.primary_email_address = @user_email::text
|
||||
AND dvs.state IN ('REQUESTED', 'SIGNED')
|
||||
)
|
||||
)`
|
||||
}
|
||||
@@ -20,7 +20,6 @@ type (
|
||||
|
||||
const (
|
||||
DocumentVersionOrderFieldCreatedAt DocumentVersionOrderField = "CREATED_AT"
|
||||
DocumentVersionOrderFieldVersion DocumentVersionOrderField = "VERSION"
|
||||
)
|
||||
|
||||
func (p DocumentVersionOrderField) Column() string {
|
||||
|
||||
@@ -57,6 +57,8 @@ type (
|
||||
ErrDocumentVersionSignatureAlreadyExists struct {
|
||||
message string
|
||||
}
|
||||
|
||||
ErrDocumentVersionSignatureAlreadySigned struct{}
|
||||
)
|
||||
|
||||
func (e ErrDocumentVersionSignatureNotFound) Error() string {
|
||||
@@ -67,6 +69,10 @@ func (e ErrDocumentVersionSignatureAlreadyExists) Error() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
func (e ErrDocumentVersionSignatureAlreadySigned) Error() string {
|
||||
return "document version already signed"
|
||||
}
|
||||
|
||||
func (pvs DocumentVersionSignature) CursorKey(orderBy DocumentVersionSignatureOrderField) page.CursorKey {
|
||||
switch orderBy {
|
||||
case DocumentVersionSignatureOrderFieldCreatedAt:
|
||||
@@ -412,3 +418,41 @@ WHERE
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pvs *DocumentVersionSignature) IsSignedByUserEmail(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
documentVersionID gid.GID,
|
||||
userEmail string,
|
||||
) (bool, error) {
|
||||
q := `
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM document_version_signatures dvs
|
||||
INNER JOIN peoples p ON dvs.signed_by = p.id
|
||||
WHERE dvs.document_version_id = @document_version_id
|
||||
AND p.primary_email_address = @user_email
|
||||
AND dvs.state = 'SIGNED'
|
||||
AND dvs.tenant_id = @tenant_id
|
||||
) AS signed
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"document_version_id": documentVersionID,
|
||||
"user_email": userEmail,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cannot query document version signature: %w", err)
|
||||
}
|
||||
|
||||
signed, err := pgx.CollectOneRow(rows, pgx.RowTo[bool])
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cannot collect signed status: %w", err)
|
||||
}
|
||||
|
||||
return signed, nil
|
||||
}
|
||||
|
||||
@@ -22,9 +22,10 @@ import (
|
||||
type MembershipRole string
|
||||
|
||||
const (
|
||||
MembershipRoleOwner MembershipRole = "OWNER"
|
||||
MembershipRoleAdmin MembershipRole = "ADMIN"
|
||||
MembershipRoleViewer MembershipRole = "VIEWER"
|
||||
MembershipRoleOwner MembershipRole = "OWNER"
|
||||
MembershipRoleAdmin MembershipRole = "ADMIN"
|
||||
MembershipRoleEmployee MembershipRole = "EMPLOYEE"
|
||||
MembershipRoleViewer MembershipRole = "VIEWER"
|
||||
)
|
||||
|
||||
func (r MembershipRole) String() string {
|
||||
@@ -47,6 +48,8 @@ func (r *MembershipRole) Scan(value any) error {
|
||||
*r = MembershipRoleOwner
|
||||
case "ADMIN":
|
||||
*r = MembershipRoleAdmin
|
||||
case "EMPLOYEE":
|
||||
*r = MembershipRoleEmployee
|
||||
case "VIEWER":
|
||||
*r = MembershipRoleViewer
|
||||
default:
|
||||
|
||||
1
pkg/coredata/migrations/20251113T000000Z.sql
Normal file
1
pkg/coredata/migrations/20251113T000000Z.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TYPE authz_role RENAME VALUE 'MEMBER' TO 'EMPLOYEE';
|
||||
@@ -132,24 +132,24 @@ func (p *People) LoadByEmail(
|
||||
primaryEmailAddress string,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
kind,
|
||||
full_name,
|
||||
primary_email_address,
|
||||
additional_email_addresses,
|
||||
position,
|
||||
contract_start_date,
|
||||
contract_end_date,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
peoples
|
||||
WHERE
|
||||
%s
|
||||
AND primary_email_address = @primary_email_address
|
||||
LIMIT 1;
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
kind,
|
||||
full_name,
|
||||
primary_email_address,
|
||||
additional_email_addresses,
|
||||
position,
|
||||
contract_start_date,
|
||||
contract_end_date,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
peoples
|
||||
WHERE
|
||||
%s
|
||||
AND primary_email_address = @primary_email_address
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
@@ -176,6 +176,62 @@ func (p *People) LoadByEmail(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *People) LoadByEmailAndOrganizationID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
primaryEmailAddress string,
|
||||
organizationID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
kind,
|
||||
full_name,
|
||||
primary_email_address,
|
||||
additional_email_addresses,
|
||||
position,
|
||||
contract_start_date,
|
||||
contract_end_date,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
peoples
|
||||
WHERE
|
||||
%s
|
||||
AND primary_email_address = @primary_email_address
|
||||
AND organization_id = @organization_id
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"primary_email_address": primaryEmailAddress,
|
||||
"organization_id": organizationID,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query people: %w", err)
|
||||
}
|
||||
|
||||
people, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[People])
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return &ErrPeopleNotFound{Identifier: primaryEmailAddress}
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot collect people: %w", err)
|
||||
}
|
||||
|
||||
*p = people
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Peoples) LoadByIDs(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -164,6 +164,32 @@ func (s *DocumentService) Get(
|
||||
return document, nil
|
||||
}
|
||||
|
||||
func (s *DocumentService) GetWithFilter(
|
||||
ctx context.Context,
|
||||
documentID gid.GID,
|
||||
filter *coredata.DocumentFilter,
|
||||
) (*coredata.Document, error) {
|
||||
document := &coredata.Document{}
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
err := document.LoadByIDWithFilter(ctx, conn, s.svc.scope, documentID, filter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot load document: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return document, nil
|
||||
}
|
||||
|
||||
func (s DocumentService) GenerateChangelog(
|
||||
ctx context.Context,
|
||||
documentID gid.GID,
|
||||
@@ -559,39 +585,13 @@ func (s *DocumentService) SignDocumentVersion(
|
||||
documentVersionID gid.GID,
|
||||
signatory gid.GID,
|
||||
) error {
|
||||
documentVersion := &coredata.DocumentVersion{}
|
||||
documentVersionSignature := &coredata.DocumentVersionSignature{}
|
||||
now := time.Now()
|
||||
|
||||
err := s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := documentVersion.LoadByID(ctx, conn, s.svc.scope, documentVersionID); err != nil {
|
||||
return fmt.Errorf("cannot load document version %q: %w", documentVersionID, err)
|
||||
}
|
||||
|
||||
if documentVersion.Status != coredata.DocumentStatusPublished {
|
||||
return fmt.Errorf("cannot sign unpublished version")
|
||||
}
|
||||
|
||||
if err := documentVersionSignature.LoadByDocumentVersionIDAndSignatory(ctx, conn, s.svc.scope, documentVersionID, signatory); err != nil {
|
||||
return fmt.Errorf("cannot load document version signature: %w", err)
|
||||
}
|
||||
|
||||
if documentVersionSignature.State == coredata.DocumentVersionSignatureStateSigned {
|
||||
return fmt.Errorf("document version already signed")
|
||||
}
|
||||
|
||||
documentVersionSignature.State = coredata.DocumentVersionSignatureStateSigned
|
||||
documentVersionSignature.SignedAt = &now
|
||||
documentVersionSignature.UpdatedAt = now
|
||||
|
||||
if err := documentVersion.Update(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot update document version: %w", err)
|
||||
}
|
||||
|
||||
if err := documentVersionSignature.Update(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot update document version signature: %w", err)
|
||||
var err error
|
||||
_, err = s.signDocumentVersionInTx(ctx, conn, documentVersionID, signatory)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot sign document version: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -605,6 +605,80 @@ func (s *DocumentService) SignDocumentVersion(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DocumentService) SignDocumentVersionByEmail(
|
||||
ctx context.Context,
|
||||
documentVersionID gid.GID,
|
||||
userEmail string,
|
||||
) (*coredata.DocumentVersionSignature, error) {
|
||||
var documentVersionSignature *coredata.DocumentVersionSignature
|
||||
|
||||
err := s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
documentVersion := &coredata.DocumentVersion{}
|
||||
if err := documentVersion.LoadByID(ctx, conn, s.svc.scope, documentVersionID); err != nil {
|
||||
return fmt.Errorf("cannot get document version: %w", err)
|
||||
}
|
||||
|
||||
people := &coredata.People{}
|
||||
if err := people.LoadByEmailAndOrganizationID(ctx, conn, s.svc.scope, userEmail, documentVersion.OrganizationID); err != nil {
|
||||
return fmt.Errorf("cannot find people record for user email in organization %q: %w", documentVersion.OrganizationID, err)
|
||||
}
|
||||
|
||||
var signErr error
|
||||
documentVersionSignature, signErr = s.signDocumentVersionInTx(ctx, conn, documentVersionID, people.ID)
|
||||
return signErr
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot sign document version: %w", err)
|
||||
}
|
||||
|
||||
return documentVersionSignature, nil
|
||||
}
|
||||
|
||||
func (s *DocumentService) signDocumentVersionInTx(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
documentVersionID gid.GID,
|
||||
signatory gid.GID,
|
||||
) (*coredata.DocumentVersionSignature, error) {
|
||||
documentVersion := &coredata.DocumentVersion{}
|
||||
documentVersionSignature := &coredata.DocumentVersionSignature{}
|
||||
now := time.Now()
|
||||
|
||||
if err := documentVersion.LoadByID(ctx, conn, s.svc.scope, documentVersionID); err != nil {
|
||||
return nil, fmt.Errorf("cannot load document version %q: %w", documentVersionID, err)
|
||||
}
|
||||
|
||||
if documentVersion.Status != coredata.DocumentStatusPublished {
|
||||
return nil, fmt.Errorf("cannot sign unpublished version")
|
||||
}
|
||||
|
||||
if err := documentVersionSignature.LoadByDocumentVersionIDAndSignatory(ctx, conn, s.svc.scope, documentVersionID, signatory); err != nil {
|
||||
return nil, fmt.Errorf("cannot load document version signature: %w", err)
|
||||
}
|
||||
|
||||
if documentVersionSignature.State == coredata.DocumentVersionSignatureStateSigned {
|
||||
return nil, &coredata.ErrDocumentVersionSignatureAlreadySigned{}
|
||||
}
|
||||
|
||||
documentVersionSignature.State = coredata.DocumentVersionSignatureStateSigned
|
||||
documentVersionSignature.SignedAt = &now
|
||||
documentVersionSignature.UpdatedAt = now
|
||||
|
||||
if err := documentVersion.Update(ctx, conn, s.svc.scope); err != nil {
|
||||
return nil, fmt.Errorf("cannot update document version: %w", err)
|
||||
}
|
||||
|
||||
if err := documentVersionSignature.Update(ctx, conn, s.svc.scope); err != nil {
|
||||
return nil, fmt.Errorf("cannot update document version signature: %w", err)
|
||||
}
|
||||
|
||||
return documentVersionSignature, nil
|
||||
}
|
||||
|
||||
func (s *DocumentService) UpdateVersion(
|
||||
ctx context.Context,
|
||||
req UpdateDocumentVersionRequest,
|
||||
@@ -810,6 +884,36 @@ func (s *DocumentService) ListSignatures(
|
||||
return page.NewPage(documentVersionSignatures, cursor), nil
|
||||
}
|
||||
|
||||
func (s *DocumentService) IsVersionSignedByUserEmail(
|
||||
ctx context.Context,
|
||||
documentVersionID gid.GID,
|
||||
userEmail string,
|
||||
) (bool, error) {
|
||||
documentVersionSignature := &coredata.DocumentVersionSignature{}
|
||||
|
||||
var signed bool
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
var err error
|
||||
signed, err = documentVersionSignature.IsSignedByUserEmail(
|
||||
ctx,
|
||||
conn,
|
||||
s.svc.scope,
|
||||
documentVersionID,
|
||||
userEmail,
|
||||
)
|
||||
return err
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cannot check if document version is signed: %w", err)
|
||||
}
|
||||
|
||||
return signed, nil
|
||||
}
|
||||
|
||||
func (s *DocumentService) CreateDraft(
|
||||
ctx context.Context,
|
||||
documentID gid.GID,
|
||||
@@ -998,13 +1102,20 @@ func (s *DocumentService) ListVersions(
|
||||
ctx context.Context,
|
||||
documentID gid.GID,
|
||||
cursor *page.Cursor[coredata.DocumentVersionOrderField],
|
||||
filter *coredata.DocumentVersionFilter,
|
||||
) (*page.Page[*coredata.DocumentVersion, coredata.DocumentVersionOrderField], error) {
|
||||
var documentVersions coredata.DocumentVersions
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
return documentVersions.LoadByDocumentID(ctx, conn, s.svc.scope, documentID, cursor)
|
||||
|
||||
err := documentVersions.LoadByDocumentID(ctx, conn, s.svc.scope, documentID, cursor, filter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot load document versions: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1035,6 +1146,36 @@ func (s *DocumentService) GetVersion(
|
||||
return documentVersion, nil
|
||||
}
|
||||
|
||||
func (s *DocumentService) IsSigned(
|
||||
ctx context.Context,
|
||||
documentID gid.GID,
|
||||
userEmail string,
|
||||
) (bool, error) {
|
||||
document := &coredata.Document{}
|
||||
|
||||
var signed bool
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
var err error
|
||||
signed, err = document.IsLastSignableVersionSignedByUserEmail(
|
||||
ctx,
|
||||
conn,
|
||||
s.svc.scope,
|
||||
documentID,
|
||||
userEmail,
|
||||
)
|
||||
return err
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cannot check if document is signed: %w", err)
|
||||
}
|
||||
|
||||
return signed, nil
|
||||
}
|
||||
|
||||
func (s *DocumentService) CountForOrganizationID(
|
||||
ctx context.Context,
|
||||
organizationID gid.GID,
|
||||
|
||||
@@ -108,6 +108,31 @@ func (s PeopleService) Get(
|
||||
return people, nil
|
||||
}
|
||||
|
||||
func (s PeopleService) GetByEmailAndOrganizationID(
|
||||
ctx context.Context,
|
||||
primaryEmailAddress string,
|
||||
organizationID gid.GID,
|
||||
) (*coredata.People, error) {
|
||||
people := &coredata.People{}
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
err := people.LoadByEmailAndOrganizationID(ctx, conn, s.svc.scope, primaryEmailAddress, organizationID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot load people by email and organization ID: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return people, nil
|
||||
}
|
||||
|
||||
func (s PeopleService) CountForOrganizationID(
|
||||
ctx context.Context,
|
||||
organizationID gid.GID,
|
||||
|
||||
@@ -94,6 +94,7 @@ enum InvitationStatus
|
||||
enum MembershipRole @goModel(model: "go.probo.inc/probo/pkg/coredata.MembershipRole") {
|
||||
OWNER @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleOwner")
|
||||
ADMIN @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleAdmin")
|
||||
EMPLOYEE @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleEmployee")
|
||||
VIEWER @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleViewer")
|
||||
}
|
||||
|
||||
@@ -522,10 +523,6 @@ enum BusinessImpact
|
||||
|
||||
enum DocumentVersionOrderField
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentVersionOrderField") {
|
||||
VERSION
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionOrderFieldVersion"
|
||||
)
|
||||
CREATED_AT
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionOrderFieldCreatedAt"
|
||||
@@ -2035,6 +2032,29 @@ type Document implements Node {
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type SignableDocument @goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.SignableDocument"
|
||||
){
|
||||
id: ID!
|
||||
title: String!
|
||||
description: String
|
||||
documentType: DocumentType!
|
||||
classification: DocumentClassification!
|
||||
signed: Boolean! @goField(forceResolver: true)
|
||||
|
||||
versions(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: DocumentVersionOrder
|
||||
filter: DocumentVersionFilter
|
||||
): DocumentVersionConnection! @goField(forceResolver: true)
|
||||
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type Meeting implements Node {
|
||||
id: ID!
|
||||
name: String!
|
||||
@@ -2259,6 +2279,17 @@ type Viewer {
|
||||
before: CursorKey
|
||||
orderBy: OrganizationOrder
|
||||
): OrganizationConnection! @goField(forceResolver: true)
|
||||
|
||||
signableDocuments(
|
||||
organizationId: ID!
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: DocumentOrder
|
||||
): SignableDocumentConnection! @goField(forceResolver: true)
|
||||
|
||||
signableDocument(id: ID!): SignableDocument @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
# Connection Types
|
||||
@@ -2514,6 +2545,22 @@ type EvidenceEdge {
|
||||
node: Evidence!
|
||||
}
|
||||
|
||||
type SignableDocumentConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.SignableDocumentConnection"
|
||||
) {
|
||||
edges: [SignableDocumentEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type SignableDocumentEdge
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.SignableDocumentEdge"
|
||||
) {
|
||||
cursor: CursorKey!
|
||||
node: SignableDocument!
|
||||
}
|
||||
|
||||
type DocumentConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.DocumentConnection"
|
||||
@@ -2914,9 +2961,16 @@ type Mutation {
|
||||
input: SendSigningNotificationsInput!
|
||||
): SendSigningNotificationsPayload! cancelSignatureRequest(
|
||||
input: CancelSignatureRequestInput!
|
||||
): CancelSignatureRequestPayload! exportDocumentVersionPDF(
|
||||
): CancelSignatureRequestPayload!
|
||||
signDocument(
|
||||
input: SignDocumentInput!
|
||||
): SignDocumentPayload!
|
||||
exportDocumentVersionPDF(
|
||||
input: ExportDocumentVersionPDFInput!
|
||||
): ExportDocumentVersionPDFPayload!
|
||||
exportSignableVersionDocumentPDF(
|
||||
input: ExportSignableDocumentVersionPDFInput!
|
||||
): ExportSignableDocumentVersionPDFPayload!
|
||||
createVendorRiskAssessment(
|
||||
input: CreateVendorRiskAssessmentInput!
|
||||
): CreateVendorRiskAssessmentPayload!
|
||||
@@ -3488,6 +3542,10 @@ input ExportDocumentVersionPDFInput {
|
||||
withSignatures: Boolean!
|
||||
}
|
||||
|
||||
input ExportSignableDocumentVersionPDFInput {
|
||||
documentVersionId: ID!
|
||||
}
|
||||
|
||||
input DeleteDocumentInput {
|
||||
documentId: ID!
|
||||
}
|
||||
@@ -4072,6 +4130,10 @@ type ExportDocumentVersionPDFPayload {
|
||||
data: String!
|
||||
}
|
||||
|
||||
type ExportSignableDocumentVersionPDFPayload {
|
||||
data: String!
|
||||
}
|
||||
|
||||
type UpdateDocumentPayload {
|
||||
document: Document!
|
||||
}
|
||||
@@ -4186,6 +4248,8 @@ type DocumentVersion implements Node @goModel(model: "go.probo.inc/probo/pkg/ser
|
||||
filter: DocumentVersionSignatureFilter
|
||||
): DocumentVersionSignatureConnection! @goField(forceResolver: true)
|
||||
|
||||
signed: Boolean! @goField(forceResolver: true)
|
||||
|
||||
publishedAt: Datetime
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
@@ -4347,6 +4411,14 @@ type CancelSignatureRequestPayload {
|
||||
deletedDocumentVersionSignatureId: ID!
|
||||
}
|
||||
|
||||
input SignDocumentInput {
|
||||
documentVersionId: ID!
|
||||
}
|
||||
|
||||
type SignDocumentPayload {
|
||||
documentVersionSignature: DocumentVersionSignature!
|
||||
}
|
||||
|
||||
type UploadMeasureEvidencePayload {
|
||||
evidenceEdge: EvidenceEdge!
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
83
pkg/server/api/console/v1/types/signable_document.go
Normal file
83
pkg/server/api/console/v1/types/signable_document.go
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
SignableDocumentConnection struct {
|
||||
Edges []*SignableDocumentEdge
|
||||
PageInfo *PageInfo
|
||||
}
|
||||
|
||||
SignableDocumentEdge struct {
|
||||
Cursor page.CursorKey
|
||||
Node *SignableDocument
|
||||
}
|
||||
|
||||
SignableDocument struct {
|
||||
ID gid.GID
|
||||
Title string
|
||||
Description *string
|
||||
DocumentType coredata.DocumentType
|
||||
Classification coredata.DocumentClassification
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
)
|
||||
|
||||
func (SignableDocument) IsNode() {}
|
||||
func (this SignableDocument) GetID() gid.GID { return this.ID }
|
||||
|
||||
func NewSignableDocumentConnection(
|
||||
p *page.Page[*SignableDocument, coredata.DocumentOrderField],
|
||||
) *SignableDocumentConnection {
|
||||
var edges = make([]*SignableDocumentEdge, len(p.Data))
|
||||
|
||||
for i := range edges {
|
||||
edges[i] = NewSignableDocumentEdge(p.Data[i], p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &SignableDocumentConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
}
|
||||
}
|
||||
|
||||
func NewSignableDocumentEdge(document *SignableDocument, orderBy coredata.DocumentOrderField) *SignableDocumentEdge {
|
||||
return &SignableDocumentEdge{
|
||||
Cursor: document.CursorKey(orderBy),
|
||||
Node: document,
|
||||
}
|
||||
}
|
||||
|
||||
func (d SignableDocument) CursorKey(orderBy coredata.DocumentOrderField) page.CursorKey {
|
||||
switch orderBy {
|
||||
case coredata.DocumentOrderFieldCreatedAt:
|
||||
return page.NewCursorKey(d.ID, d.CreatedAt)
|
||||
case coredata.DocumentOrderFieldTitle:
|
||||
return page.NewCursorKey(d.ID, d.Title)
|
||||
case coredata.DocumentOrderFieldDocumentType:
|
||||
return page.NewCursorKey(d.ID, d.DocumentType)
|
||||
}
|
||||
|
||||
panic("unsupported order by")
|
||||
}
|
||||
@@ -1153,6 +1153,14 @@ type ExportFrameworkPayload struct {
|
||||
ExportJobID gid.GID `json:"exportJobId"`
|
||||
}
|
||||
|
||||
type ExportSignableDocumentVersionPDFInput struct {
|
||||
DocumentVersionID gid.GID `json:"documentVersionId"`
|
||||
}
|
||||
|
||||
type ExportSignableDocumentVersionPDFPayload struct {
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
type File struct {
|
||||
ID gid.GID `json:"id"`
|
||||
MimeType string `json:"mimeType"`
|
||||
@@ -1680,6 +1688,14 @@ type Session struct {
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
}
|
||||
|
||||
type SignDocumentInput struct {
|
||||
DocumentVersionID gid.GID `json:"documentVersionId"`
|
||||
}
|
||||
|
||||
type SignDocumentPayload struct {
|
||||
DocumentVersionSignature *DocumentVersionSignature `json:"documentVersionSignature"`
|
||||
}
|
||||
|
||||
type SlackConnection struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Channel *string `json:"channel,omitempty"`
|
||||
@@ -2506,9 +2522,11 @@ type VerifyDomainPayload struct {
|
||||
}
|
||||
|
||||
type Viewer struct {
|
||||
ID gid.GID `json:"id"`
|
||||
User *User `json:"user"`
|
||||
Organizations *OrganizationConnection `json:"organizations"`
|
||||
ID gid.GID `json:"id"`
|
||||
User *User `json:"user"`
|
||||
Organizations *OrganizationConnection `json:"organizations"`
|
||||
SignableDocuments *SignableDocumentConnection `json:"signableDocuments"`
|
||||
SignableDocument *SignableDocument `json:"signableDocument,omitempty"`
|
||||
}
|
||||
|
||||
type Role string
|
||||
|
||||
@@ -714,7 +714,9 @@ func (r *documentResolver) Versions(ctx context.Context, obj *types.Document, fi
|
||||
|
||||
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
||||
|
||||
page, err := prb.Documents.ListVersions(ctx, obj.ID, cursor)
|
||||
versionFilter := coredata.NewDocumentVersionFilter()
|
||||
|
||||
page, err := prb.Documents.ListVersions(ctx, obj.ID, cursor, versionFilter)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list document versions: %w", err))
|
||||
}
|
||||
@@ -785,7 +787,7 @@ func (r *documentConnectionResolver) TotalCount(ctx context.Context, obj *types.
|
||||
|
||||
// Document is the resolver for the document field.
|
||||
func (r *documentVersionResolver) Document(ctx context.Context, obj *types.DocumentVersion) (*types.Document, error) {
|
||||
r.MustBeAuthorized(ctx, obj.ID, authz.ActionDocument)
|
||||
r.MustBeAuthorized(ctx, obj.ID, authz.ActionGetDocument)
|
||||
|
||||
prb := r.ProboService(ctx, obj.ID.TenantID())
|
||||
|
||||
@@ -862,6 +864,24 @@ func (r *documentVersionResolver) Signatures(ctx context.Context, obj *types.Doc
|
||||
return types.NewDocumentVersionSignatureConnection(page), nil
|
||||
}
|
||||
|
||||
// Signed is the resolver for the signed field.
|
||||
func (r *documentVersionResolver) Signed(ctx context.Context, obj *types.DocumentVersion) (bool, error) {
|
||||
r.MustBeAuthorized(ctx, obj.ID, authz.ActionGetSigned)
|
||||
|
||||
user := UserFromContext(ctx)
|
||||
if user == nil {
|
||||
panic(fmt.Errorf("user not found in context"))
|
||||
}
|
||||
|
||||
prb := r.ProboService(ctx, obj.ID.TenantID())
|
||||
signed, err := prb.Documents.IsVersionSignedByUserEmail(ctx, obj.ID, user.EmailAddress)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot check if document version is signed: %w", err))
|
||||
}
|
||||
|
||||
return signed, nil
|
||||
}
|
||||
|
||||
// DocumentVersion is the resolver for the documentVersion field.
|
||||
func (r *documentVersionSignatureResolver) DocumentVersion(ctx context.Context, obj *types.DocumentVersionSignature) (*types.DocumentVersion, error) {
|
||||
r.MustBeAuthorized(ctx, obj.ID, authz.ActionDocumentVersion)
|
||||
@@ -3551,6 +3571,31 @@ func (r *mutationResolver) CancelSignatureRequest(ctx context.Context, input typ
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SignDocument is the resolver for the signDocument field.
|
||||
func (r *mutationResolver) SignDocument(ctx context.Context, input types.SignDocumentInput) (*types.SignDocumentPayload, error) {
|
||||
r.MustBeAuthorized(ctx, input.DocumentVersionID, authz.ActionSignDocument)
|
||||
|
||||
user := UserFromContext(ctx)
|
||||
if user == nil {
|
||||
panic(fmt.Errorf("user not found in context"))
|
||||
}
|
||||
|
||||
prb := r.ProboService(ctx, input.DocumentVersionID.TenantID())
|
||||
|
||||
documentVersionSignature, err := prb.Documents.SignDocumentVersionByEmail(ctx, input.DocumentVersionID, user.EmailAddress)
|
||||
if err != nil {
|
||||
var errAlreadySigned *coredata.ErrDocumentVersionSignatureAlreadySigned
|
||||
if errors.As(err, &errAlreadySigned) {
|
||||
return nil, gqlutils.Conflict(errAlreadySigned)
|
||||
}
|
||||
panic(fmt.Errorf("cannot sign document: %w", err))
|
||||
}
|
||||
|
||||
return &types.SignDocumentPayload{
|
||||
DocumentVersionSignature: types.NewDocumentVersionSignature(documentVersionSignature),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ExportDocumentVersionPDF is the resolver for the exportDocumentVersionPDF field.
|
||||
func (r *mutationResolver) ExportDocumentVersionPDF(ctx context.Context, input types.ExportDocumentVersionPDFInput) (*types.ExportDocumentVersionPDFPayload, error) {
|
||||
r.MustBeAuthorized(ctx, input.DocumentVersionID, authz.ActionExportDocumentVersionPDF)
|
||||
@@ -3573,6 +3618,49 @@ func (r *mutationResolver) ExportDocumentVersionPDF(ctx context.Context, input t
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ExportSignableVersionDocumentPDF is the resolver for the exportSignableVersionDocumentPDF field.
|
||||
func (r *mutationResolver) ExportSignableVersionDocumentPDF(ctx context.Context, input types.ExportSignableDocumentVersionPDFInput) (*types.ExportSignableDocumentVersionPDFPayload, error) {
|
||||
r.MustBeAuthorized(ctx, input.DocumentVersionID, authz.ActionExportSignableVersionDocumentPDF)
|
||||
|
||||
prb := r.ProboService(ctx, input.DocumentVersionID.TenantID())
|
||||
|
||||
documentVersion, err := prb.Documents.GetVersion(ctx, input.DocumentVersionID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get document version: %w", err))
|
||||
}
|
||||
|
||||
user := UserFromContext(ctx)
|
||||
if user == nil {
|
||||
panic(fmt.Errorf("user not found in context"))
|
||||
}
|
||||
|
||||
documentFilter := coredata.NewDocumentFilter(nil).WithUserEmail(&user.EmailAddress)
|
||||
|
||||
_, err = prb.Documents.GetWithFilter(ctx, documentVersion.DocumentID, documentFilter)
|
||||
if err != nil {
|
||||
var errNotFound *coredata.ErrDocumentNotFound
|
||||
if errors.As(err, &errNotFound) {
|
||||
return nil, gqlutils.NotFound(errNotFound)
|
||||
}
|
||||
panic(fmt.Errorf("cannot get signable document: %w", err))
|
||||
}
|
||||
|
||||
options := probo.ExportPDFOptions{
|
||||
WithSignatures: false,
|
||||
WithWatermark: true,
|
||||
WatermarkEmail: &user.EmailAddress,
|
||||
}
|
||||
|
||||
pdf, err := prb.Documents.ExportPDF(ctx, input.DocumentVersionID, options)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot export signable document PDF: %w", err))
|
||||
}
|
||||
|
||||
return &types.ExportSignableDocumentVersionPDFPayload{
|
||||
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateVendorRiskAssessment is the resolver for the createVendorRiskAssessment field.
|
||||
func (r *mutationResolver) CreateVendorRiskAssessment(ctx context.Context, input types.CreateVendorRiskAssessmentInput) (*types.CreateVendorRiskAssessmentPayload, error) {
|
||||
r.MustBeAuthorized(ctx, input.VendorID, authz.ActionCreateVendorRiskAssessment)
|
||||
@@ -5994,6 +6082,59 @@ func (r *sAMLConfigurationResolver) TestLoginURL(ctx context.Context, obj *types
|
||||
return fmt.Sprintf("%s/connect/saml/login/%s", parts[0], obj.ID), nil
|
||||
}
|
||||
|
||||
// Signed is the resolver for the signed field.
|
||||
func (r *signableDocumentResolver) Signed(ctx context.Context, obj *types.SignableDocument) (bool, error) {
|
||||
r.MustBeAuthorized(ctx, obj.ID, authz.ActionGetSigned)
|
||||
|
||||
user := UserFromContext(ctx)
|
||||
if user == nil {
|
||||
panic(fmt.Errorf("user not found in context"))
|
||||
}
|
||||
|
||||
prb := r.ProboService(ctx, obj.ID.TenantID())
|
||||
|
||||
signed, err := prb.Documents.IsSigned(ctx, obj.ID, user.EmailAddress)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot check if document is signed: %w", err))
|
||||
}
|
||||
|
||||
return signed, nil
|
||||
}
|
||||
|
||||
// Versions is the resolver for the versions field.
|
||||
func (r *signableDocumentResolver) Versions(ctx context.Context, obj *types.SignableDocument, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionOrderBy, filter *types.DocumentVersionFilter) (*types.DocumentVersionConnection, error) {
|
||||
r.MustBeAuthorized(ctx, obj.ID, authz.ActionListSignableDocumentVersion)
|
||||
|
||||
prb := r.ProboService(ctx, obj.ID.TenantID())
|
||||
|
||||
pageOrderBy := page.OrderBy[coredata.DocumentVersionOrderField]{
|
||||
Field: coredata.DocumentVersionOrderFieldCreatedAt,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
}
|
||||
if orderBy != nil {
|
||||
pageOrderBy = page.OrderBy[coredata.DocumentVersionOrderField]{
|
||||
Field: orderBy.Field,
|
||||
Direction: orderBy.Direction,
|
||||
}
|
||||
}
|
||||
|
||||
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
||||
|
||||
user := UserFromContext(ctx)
|
||||
if user == nil {
|
||||
panic(fmt.Errorf("user not found in context"))
|
||||
}
|
||||
|
||||
versionFilter := coredata.NewDocumentVersionFilter().WithUserEmail(&user.EmailAddress)
|
||||
|
||||
page, err := prb.Documents.ListVersions(ctx, obj.ID, cursor, versionFilter)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list signable document versions: %w", err))
|
||||
}
|
||||
|
||||
return types.NewDocumentVersionConnection(page), nil
|
||||
}
|
||||
|
||||
// Organization is the resolver for the organization field.
|
||||
func (r *snapshotResolver) Organization(ctx context.Context, obj *types.Snapshot) (*types.Organization, error) {
|
||||
r.MustBeAuthorized(ctx, obj.ID, authz.ActionGetOrganization)
|
||||
@@ -6349,7 +6490,7 @@ func (r *trustCenterAccessResolver) AvailableDocumentAccesses(ctx context.Contex
|
||||
|
||||
// Document is the resolver for the document field.
|
||||
func (r *trustCenterDocumentAccessResolver) Document(ctx context.Context, obj *types.TrustCenterDocumentAccess) (*types.Document, error) {
|
||||
r.MustBeAuthorized(ctx, obj.TrustCenterAccessID, authz.ActionDocument)
|
||||
r.MustBeAuthorized(ctx, obj.ID, authz.ActionGet)
|
||||
|
||||
if obj.DocumentID == nil {
|
||||
return nil, nil
|
||||
@@ -6979,6 +7120,85 @@ func (r *viewerResolver) Organizations(ctx context.Context, obj *types.Viewer, f
|
||||
return types.NewOrganizationConnection(page), nil
|
||||
}
|
||||
|
||||
// SignableDocuments is the resolver for the signableDocuments field.
|
||||
func (r *viewerResolver) SignableDocuments(ctx context.Context, obj *types.Viewer, organizationID gid.GID, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.SignableDocumentConnection, error) {
|
||||
r.MustBeAuthorized(ctx, organizationID, authz.ActionListSignableDocuments)
|
||||
|
||||
prb := r.ProboService(ctx, organizationID.TenantID())
|
||||
|
||||
pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{
|
||||
Field: coredata.DocumentOrderFieldCreatedAt,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
}
|
||||
if orderBy != nil {
|
||||
pageOrderBy = page.OrderBy[coredata.DocumentOrderField]{
|
||||
Field: orderBy.Field,
|
||||
Direction: orderBy.Direction,
|
||||
}
|
||||
}
|
||||
|
||||
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
||||
|
||||
user := UserFromContext(ctx)
|
||||
if user == nil {
|
||||
panic(fmt.Errorf("user not found in context"))
|
||||
}
|
||||
|
||||
documentFilter := coredata.NewDocumentFilter(nil).WithUserEmail(&user.EmailAddress)
|
||||
|
||||
documentsPage, err := prb.Documents.ListByOrganizationID(ctx, organizationID, cursor, documentFilter)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list organization signable documents: %w", err))
|
||||
}
|
||||
|
||||
signableDocuments := make([]*types.SignableDocument, len(documentsPage.Data))
|
||||
for i, doc := range documentsPage.Data {
|
||||
signableDocuments[i] = &types.SignableDocument{
|
||||
ID: doc.ID,
|
||||
Title: doc.Title,
|
||||
DocumentType: doc.DocumentType,
|
||||
Classification: doc.Classification,
|
||||
CreatedAt: doc.CreatedAt,
|
||||
UpdatedAt: doc.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
page := page.NewPage(signableDocuments, documentsPage.Cursor)
|
||||
|
||||
return types.NewSignableDocumentConnection(page), nil
|
||||
}
|
||||
|
||||
// SignableDocument is the resolver for the signableDocument field.
|
||||
func (r *viewerResolver) SignableDocument(ctx context.Context, obj *types.Viewer, id gid.GID) (*types.SignableDocument, error) {
|
||||
r.MustBeAuthorized(ctx, id, authz.ActionGetSignableDocument)
|
||||
|
||||
prb := r.ProboService(ctx, id.TenantID())
|
||||
|
||||
user := UserFromContext(ctx)
|
||||
if user == nil {
|
||||
panic(fmt.Errorf("user not found in context"))
|
||||
}
|
||||
|
||||
documentFilter := coredata.NewDocumentFilter(nil).WithUserEmail(&user.EmailAddress)
|
||||
document, err := prb.Documents.GetWithFilter(ctx, id, documentFilter)
|
||||
if err != nil {
|
||||
var errNotFound *coredata.ErrDocumentNotFound
|
||||
if errors.As(err, &errNotFound) {
|
||||
return nil, gqlutils.NotFound(errNotFound)
|
||||
}
|
||||
panic(fmt.Errorf("cannot get signable document: %w", err))
|
||||
}
|
||||
|
||||
return &types.SignableDocument{
|
||||
ID: document.ID,
|
||||
Title: document.Title,
|
||||
DocumentType: document.DocumentType,
|
||||
Classification: document.Classification,
|
||||
CreatedAt: document.CreatedAt,
|
||||
UpdatedAt: document.UpdatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Asset returns schema.AssetResolver implementation.
|
||||
func (r *Resolver) Asset() schema.AssetResolver { return &assetResolver{r} }
|
||||
|
||||
@@ -7144,6 +7364,11 @@ func (r *Resolver) SAMLConfiguration() schema.SAMLConfigurationResolver {
|
||||
return &sAMLConfigurationResolver{r}
|
||||
}
|
||||
|
||||
// SignableDocument returns schema.SignableDocumentResolver implementation.
|
||||
func (r *Resolver) SignableDocument() schema.SignableDocumentResolver {
|
||||
return &signableDocumentResolver{r}
|
||||
}
|
||||
|
||||
// Snapshot returns schema.SnapshotResolver implementation.
|
||||
func (r *Resolver) Snapshot() schema.SnapshotResolver { return &snapshotResolver{r} }
|
||||
|
||||
@@ -7277,6 +7502,7 @@ type reportResolver struct{ *Resolver }
|
||||
type riskResolver struct{ *Resolver }
|
||||
type riskConnectionResolver struct{ *Resolver }
|
||||
type sAMLConfigurationResolver struct{ *Resolver }
|
||||
type signableDocumentResolver struct{ *Resolver }
|
||||
type snapshotResolver struct{ *Resolver }
|
||||
type snapshotConnectionResolver struct{ *Resolver }
|
||||
type taskResolver struct{ *Resolver }
|
||||
|
||||
@@ -1595,7 +1595,7 @@ func (r *Resolver) ListDocumentVersionsTool(ctx context.Context, req *mcp.CallTo
|
||||
cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy)
|
||||
svc := r.ProboService(ctx, input.DocumentID)
|
||||
|
||||
page, err := svc.Documents.ListVersions(ctx, input.DocumentID, cursor)
|
||||
page, err := svc.Documents.ListVersions(ctx, input.DocumentID, cursor, coredata.NewDocumentVersionFilter())
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list document versions: %w", err))
|
||||
}
|
||||
|
||||
@@ -46,7 +46,8 @@ func (s *DocumentService) ListVersions(
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
return documentVersions.LoadByDocumentID(ctx, conn, s.svc.scope, documentID, cursor)
|
||||
filter := coredata.NewDocumentVersionFilter()
|
||||
return documentVersions.LoadByDocumentID(ctx, conn, s.svc.scope, documentID, cursor, filter)
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user