Remove legacy document signing endpoints
Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -338,7 +338,7 @@ func (p *Presenter) RenderInvitation(ctx context.Context, invitationURLPath stri
|
|||||||
return fmt.Sprintf(subjectInvitation, organizationName), textBody, htmlBody, err
|
return fmt.Sprintf(subjectInvitation, organizationName), textBody, htmlBody, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Presenter) RenderDocumentSigning(ctx context.Context, signinURLPath string, token string, organizationName string) (subject string, textBody string, htmlBody *string, err error) {
|
func (p *Presenter) RenderDocumentSigning(ctx context.Context, signinURLPath string, organizationName string) (subject string, textBody string, htmlBody *string, err error) {
|
||||||
vars, err := p.getCommonVariables(ctx)
|
vars, err := p.getCommonVariables(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", nil, fmt.Errorf("cannot get common variables: %w", err)
|
return "", "", nil, fmt.Errorf("cannot get common variables: %w", err)
|
||||||
@@ -346,7 +346,6 @@ func (p *Presenter) RenderDocumentSigning(ctx context.Context, signinURLPath str
|
|||||||
|
|
||||||
signingURL := baseurl.MustParse(vars.BaseURL).
|
signingURL := baseurl.MustParse(vars.BaseURL).
|
||||||
AppendPath(signinURLPath).
|
AppendPath(signinURLPath).
|
||||||
WithQuery("token", token).
|
|
||||||
MustString()
|
MustString()
|
||||||
|
|
||||||
data := struct {
|
data := struct {
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||||
"github.com/jackc/pgx/v5"
|
|
||||||
"go.gearno.de/crypto/uuid"
|
"go.gearno.de/crypto/uuid"
|
||||||
"go.gearno.de/kit/pg"
|
"go.gearno.de/kit/pg"
|
||||||
"go.probo.inc/probo/packages/emails"
|
"go.probo.inc/probo/packages/emails"
|
||||||
@@ -23,7 +22,6 @@ import (
|
|||||||
"go.probo.inc/probo/pkg/html2pdf"
|
"go.probo.inc/probo/pkg/html2pdf"
|
||||||
"go.probo.inc/probo/pkg/mail"
|
"go.probo.inc/probo/pkg/mail"
|
||||||
"go.probo.inc/probo/pkg/page"
|
"go.probo.inc/probo/pkg/page"
|
||||||
"go.probo.inc/probo/pkg/statelesstoken"
|
|
||||||
"go.probo.inc/probo/pkg/validator"
|
"go.probo.inc/probo/pkg/validator"
|
||||||
"go.probo.inc/probo/pkg/watermarkpdf"
|
"go.probo.inc/probo/pkg/watermarkpdf"
|
||||||
)
|
)
|
||||||
@@ -79,11 +77,6 @@ type (
|
|||||||
SignatoryIDs []gid.GID
|
SignatoryIDs []gid.GID
|
||||||
}
|
}
|
||||||
|
|
||||||
SigningRequestData struct {
|
|
||||||
OrganizationID gid.GID `json:"organization_id"`
|
|
||||||
PeopleID gid.GID `json:"people_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
BulkPublishVersionsRequest struct {
|
BulkPublishVersionsRequest struct {
|
||||||
DocumentIDs []gid.GID
|
DocumentIDs []gid.GID
|
||||||
PublishedBy gid.GID
|
PublishedBy gid.GID
|
||||||
@@ -137,8 +130,6 @@ func (udvr *UpdateDocumentVersionRequest) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TokenTypeSigningRequest = "signing_request"
|
|
||||||
|
|
||||||
documentExportEmailExpiresIn = 24 * time.Hour
|
documentExportEmailExpiresIn = 24 * time.Hour
|
||||||
|
|
||||||
maxFilenameLength = 200
|
maxFilenameLength = 200
|
||||||
@@ -593,59 +584,6 @@ func (s *DocumentService) Create(
|
|||||||
return document, documentVersion, nil
|
return document, documentVersion, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DocumentService) ListSigningRequests(
|
|
||||||
ctx context.Context,
|
|
||||||
organizationID gid.GID,
|
|
||||||
profileID gid.GID,
|
|
||||||
) ([]map[string]any, error) {
|
|
||||||
q := `
|
|
||||||
SELECT
|
|
||||||
p.title,
|
|
||||||
pv.id AS document_version_id,
|
|
||||||
o.name AS organization_name
|
|
||||||
FROM
|
|
||||||
documents p
|
|
||||||
INNER JOIN document_versions pv ON pv.document_id = p.id
|
|
||||||
INNER JOIN document_version_signatures pvs ON pvs.document_version_id = pv.id
|
|
||||||
INNER JOIN organizations o ON o.id = p.organization_id
|
|
||||||
WHERE
|
|
||||||
p.tenant_id = $1
|
|
||||||
AND pvs.signed_by_profile_id = $2
|
|
||||||
AND pvs.signed_at IS NULL
|
|
||||||
AND pv.status = 'PUBLISHED'
|
|
||||||
AND pv.version_number = (
|
|
||||||
SELECT MAX(pv2.version_number)
|
|
||||||
FROM document_versions pv2
|
|
||||||
WHERE pv2.document_id = pv.document_id
|
|
||||||
AND pv2.status = 'PUBLISHED'
|
|
||||||
)
|
|
||||||
`
|
|
||||||
|
|
||||||
var results []map[string]any
|
|
||||||
err := s.svc.pg.WithConn(
|
|
||||||
ctx,
|
|
||||||
func(conn pg.Conn) error {
|
|
||||||
rows, err := conn.Query(ctx, q, s.svc.scope.GetTenantID(), profileID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot query documents: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
results, err = pgx.CollectRows(rows, pgx.RowToMap)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DocumentService) SendSigningNotifications(
|
func (s *DocumentService) SendSigningNotifications(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
organizationID gid.GID,
|
organizationID gid.GID,
|
||||||
@@ -664,25 +602,11 @@ func (s *DocumentService) SendSigningNotifications(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, signatory := range signatories {
|
for _, signatory := range signatories {
|
||||||
token, err := statelesstoken.NewToken(
|
|
||||||
s.svc.tokenSecret,
|
|
||||||
TokenTypeSigningRequest,
|
|
||||||
time.Hour*24*30,
|
|
||||||
SigningRequestData{
|
|
||||||
OrganizationID: organizationID,
|
|
||||||
PeopleID: signatory.ID,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot create signing request token: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
emailPresenter := emails.NewPresenter(s.svc.fileManager, s.svc.bucket, s.svc.baseURL, signatory.FullName)
|
emailPresenter := emails.NewPresenter(s.svc.fileManager, s.svc.bucket, s.svc.baseURL, signatory.FullName)
|
||||||
|
|
||||||
subject, textBody, htmlBody, err := emailPresenter.RenderDocumentSigning(
|
subject, textBody, htmlBody, err := emailPresenter.RenderDocumentSigning(
|
||||||
ctx,
|
ctx,
|
||||||
"/documents/signing-requests",
|
"/organizations/"+organizationID.String()+"/employee",
|
||||||
token,
|
|
||||||
organization.Name,
|
organization.Name,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -713,31 +637,6 @@ func (s *DocumentService) SendSigningNotifications(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DocumentService) SignDocumentVersion(
|
|
||||||
ctx context.Context,
|
|
||||||
documentVersionID gid.GID,
|
|
||||||
signatory gid.GID,
|
|
||||||
) error {
|
|
||||||
err := s.svc.pg.WithTx(
|
|
||||||
ctx,
|
|
||||||
func(conn pg.Conn) error {
|
|
||||||
var err error
|
|
||||||
_, err = s.signDocumentVersionInTx(ctx, conn, documentVersionID, signatory)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot sign document version: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot sign document version: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DocumentService) SignDocumentVersionByIdentity(
|
func (s *DocumentService) SignDocumentVersionByIdentity(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
documentVersionID gid.GID,
|
documentVersionID gid.GID,
|
||||||
|
|||||||
@@ -637,7 +637,7 @@ func (s *TrustCenterService) EmailPresenterConfig(ctx context.Context, complianc
|
|||||||
baseURL := url.URL{
|
baseURL := url.URL{
|
||||||
Scheme: parsedBaseURL.Scheme,
|
Scheme: parsedBaseURL.Scheme,
|
||||||
Host: parsedBaseURL.Host,
|
Host: parsedBaseURL.Host,
|
||||||
Path: "/trust/" + compliancePage.Slug,
|
Path: "/trust/" + compliancePage.ID.String(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if customDomain != nil && customDomain.SSLStatus == coredata.CustomDomainSSLStatusActive {
|
if customDomain != nil && customDomain.SSLStatus == coredata.CustomDomainSSLStatusActive {
|
||||||
|
|||||||
@@ -18,14 +18,11 @@ package console_v1
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"go.gearno.de/crypto/uuid"
|
|
||||||
"go.gearno.de/kit/httpserver"
|
"go.gearno.de/kit/httpserver"
|
||||||
"go.gearno.de/kit/log"
|
"go.gearno.de/kit/log"
|
||||||
"go.probo.inc/probo/pkg/baseurl"
|
"go.probo.inc/probo/pkg/baseurl"
|
||||||
@@ -40,7 +37,6 @@ import (
|
|||||||
"go.probo.inc/probo/pkg/server/api/authn"
|
"go.probo.inc/probo/pkg/server/api/authn"
|
||||||
"go.probo.inc/probo/pkg/server/api/authz"
|
"go.probo.inc/probo/pkg/server/api/authz"
|
||||||
"go.probo.inc/probo/pkg/server/api/console/v1/types"
|
"go.probo.inc/probo/pkg/server/api/console/v1/types"
|
||||||
"go.probo.inc/probo/pkg/statelesstoken"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -196,125 +192,6 @@ func NewMux(
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Get(
|
|
||||||
"/documents/signing-requests",
|
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
token := r.Header.Get("Authorization")
|
|
||||||
if token == "" {
|
|
||||||
http.Error(w, "token is required", http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
token = strings.TrimPrefix(token, "Bearer ")
|
|
||||||
data, err := statelesstoken.ValidateToken[probo.SigningRequestData](tokenSecret, probo.TokenTypeSigningRequest, token)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "invalid token", http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
svc := proboSvc.WithTenant(data.Data.OrganizationID.TenantID())
|
|
||||||
|
|
||||||
requests, err := svc.Documents.ListSigningRequests(r.Context(), data.Data.OrganizationID, data.Data.PeopleID)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
if err := json.NewEncoder(w).Encode(requests); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
r.Get(
|
|
||||||
"/documents/signing-requests/{document_version_id}/pdf",
|
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
token := r.URL.Query().Get("token")
|
|
||||||
if token == "" {
|
|
||||||
http.Error(w, "token is required", http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := statelesstoken.ValidateToken[probo.SigningRequestData](tokenSecret, probo.TokenTypeSigningRequest, token)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "invalid token", http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
documentVersionID, err := gid.ParseGID(chi.URLParam(r, "document_version_id"))
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "invalid document version id", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
svc := proboSvc.WithTenant(data.Data.OrganizationID.TenantID())
|
|
||||||
|
|
||||||
// Get the people to get their email for watermark
|
|
||||||
profile, err := iamSvc.OrganizationService.GetProfile(r.Context(), data.Data.PeopleID)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "cannot get user", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate PDF with watermark
|
|
||||||
pdfData, err := svc.Documents.ExportPDF(r.Context(), documentVersionID, probo.ExportPDFOptions{
|
|
||||||
WithWatermark: true,
|
|
||||||
WatermarkEmail: &profile.EmailAddress,
|
|
||||||
WithSignatures: false,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
uuid, err := uuid.NewV7()
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "cannot generate uuid", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/pdf")
|
|
||||||
w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s.pdf\"", uuid.String()))
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
_, _ = w.Write(pdfData)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
r.Post(
|
|
||||||
"/documents/signing-requests/{document_version_id}/sign",
|
|
||||||
func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
token := r.Header.Get("Authorization")
|
|
||||||
if token == "" {
|
|
||||||
http.Error(w, "token is required", http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
token = strings.TrimPrefix(token, "Bearer ")
|
|
||||||
data, err := statelesstoken.ValidateToken[probo.SigningRequestData](tokenSecret, probo.TokenTypeSigningRequest, token)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "invalid token", http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
documentVersionID, err := gid.ParseGID(chi.URLParam(r, "document_version_id"))
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "invalid document version id", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
svc := proboSvc.WithTenant(data.Data.OrganizationID.TenantID())
|
|
||||||
|
|
||||||
if err := svc.Documents.SignDocumentVersion(r.Context(), documentVersionID, data.Data.PeopleID); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user