Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[$4002] Feature/doc sign go #4142

Merged
merged 2 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cla-backend-go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ require (
github.com/ProtonMail/go-crypto v0.0.0-20230321155629-9a39f2531310 // indirect
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/cloudflare/circl v1.3.2 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
Expand All @@ -83,6 +84,7 @@ require (
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-github/v50 v50.2.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions cla-backend-go/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185/go.mod h1:cFRxtTwTOJkz2x3rQUNCYKWC93yP1VKjR8NUhqFxZNU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
Expand Down Expand Up @@ -269,6 +271,8 @@ github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/V
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/dep v0.5.4/go.mod h1:6RZ2Wai7dSWk7qL55sDYk+8UPFqcW7all2KDBraPPFA=
Expand Down
3 changes: 3 additions & 0 deletions cla-backend-go/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@ provider:
DOCUSIGN_USERNAME: ${file(./env.json):docusign-username, ssm:/cla-docusign-username-${opt:stage}}
DOCUSIGN_PASSWORD: ${file(./env.json):docusign-password, ssm:/cla-docusign-password-${opt:stage}}
DOCUSIGN_INTEGRATOR_KEY: ${file(./env.json):docusign-integrator-key, ssm:/cla-docusign-integrator-key-${opt:stage}}
DOCUSIGN_AUTH_SERVER: ${file(./env.json):docusign-auth-server, ssm:/cla-docusign-auth-server-${opt:stage}}
DOCUSIGN_PRIVATE_KEY: ${file(./env.json):docusign-auth-server, ssm:/cla-docusign-private-key-${opt:stage}}
DOCUSIGN_USER_ID: ${file(./env.json):docusign-auth-server, ssm:/cla-docusign-user-id-${opt:stage}}
CLA_API_BASE: ${file(./env.json):cla-api-base, ssm:/cla-api-base-${opt:stage}}
CLA_CONTRIBUTOR_BASE: ${file(./env.json):cla-contributor-base, ssm:/cla-contributor-base-${opt:stage}}
CLA_CONTRIBUTOR_V2_BASE: ${file(./env.json):cla-contributor-v2-base, ssm:/cla-contributor-v2-base-${opt:stage}}
Expand Down
74 changes: 74 additions & 0 deletions cla-backend-go/swagger/cla.v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4125,6 +4125,36 @@ paths:
tags:
- gitlab-sign


/request-individual-signature:
post:
summary: Request for icla sign
description: Initiate the icla signing with docusign
security: []
operationId: requestIndividualSignature
parameters:
- $ref: "#/parameters/x-request-id"
- name: input
in: body
schema:
$ref: '#/definitions/icla-signature-input'
required: true
responses:
'200':
description: 'Success'
headers:
x-request-id:
type: string
description: The unique request ID value - assigned/set by the API Gateway based on the session
schema:
$ref: '#/definitions/individual-signature-output'
'400':
$ref: '#/responses/invalid-request'
'500':
$ref: '#/responses/internal-server-error'
tags:
- sign

responses:
unauthorized:
description: Unauthorized
Expand Down Expand Up @@ -5497,6 +5527,40 @@ definitions:
corporate-contributor:
$ref: './common/corporate-contributor.yaml'

icla-signature-input:
type: object
required:
- project_sfid
- company_sfid
properties:
project_sfid:
type: string
example: 'a0941000005ouJFAAY'
description: salesforce id of the project
company_sfid:
type: string
example: '0014100000Te0fMAAR'
description: salesforce id of the company
send_as_email:
type: boolean
example: false
description: send signing request as email. This should be set to true when requestor is not signatory.
authority_name:
type: string
example: "Derk Miyamoto"
description: the name of the CLA signatory
minLength: 2
maxLength: 255
authority_email:
$ref: './common/properties/email.yaml'
description: the email of the CLA Signatory
return_url:
type: string
example: 'https://corporate.dev.lfcla.com/#/company/eb4d7d71-693f-4047-bf8d-10d0e7764969'
description: on signing the document, page will get redirected to this url. This is valid only when send_as_email is false
format: uri


corporate-signature-input:
type: object
required:
Expand Down Expand Up @@ -5544,6 +5608,16 @@ definitions:
type: string
description: signing url

individual-signature-output:
type: object
properties:
signature_id:
type: string
description: id of the signature
sign_url:
type: string
description: signing url

signed_document:
type: object
properties:
Expand Down
102 changes: 102 additions & 0 deletions cla-backend-go/v2/docusign_auth/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright The Linux Foundation and each contributor to CommunityBridge.
// SPDX-License-Identifier: MIT

package docusignauth

import (
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"

jwt "github.com/dgrijalva/jwt-go"
)

var (
baseURL = os.Getenv("DOCUSIGN_AUTH_SERVER")
oauthTokenURL = baseURL + "/oauth/token"
jwtGrantAssertion = "urn:ietf:params:oauth:grant-type:jwt-bearer"
)

type TokenResponse struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
}

func GetAccessToken(integrationKey, userGUID, privateKey string) (string, error) {
block, _ := pem.Decode([]byte(privateKey))
if block == nil {
return "", fmt.Errorf("failed to parse private key")
}

privateKeyParsed, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return "", err
}

// Generate the JWT token
tokenString, err := generateJWT(integrationKey, userGUID, privateKeyParsed.D.String())
if err != nil {
return "", err
}

// Make the HTTP request to get the access token
body := strings.NewReader(fmt.Sprintf("grant_type=%s&assertion=%s", jwtGrantAssertion, tokenString))
req, err := http.NewRequest("POST", oauthTokenURL, body)
if err != nil {
return "", err
}

req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close() // nolint

if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("failed to get access token, status: %s", resp.Status)
}

respBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}

var tokenResponse TokenResponse
if err := json.Unmarshal(respBody, &tokenResponse); err != nil {
return "", err
}

return tokenResponse.AccessToken, nil
}

func generateJWT(integrationKey, userGUID, privateKey string) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
"iss": integrationKey, // Integration Key
"sub": userGUID, // User GUID
"aud": baseURL, // Base URL
"iat": time.Now().Unix(), // Issued At
"exp": time.Now().Add(1 * time.Hour).Unix(), // Expiration time - 1 hour is recommended
"scope": "signature", // Permission scope
})

signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(privateKey))
if err != nil {
return "", err
}

tokenString, err := token.SignedString(signKey)
if err != nil {
return "", err
}

return tokenString, nil
}
47 changes: 47 additions & 0 deletions cla-backend-go/v2/main/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package main

import (
"fmt"

"github.com/communitybridge/easycla/cla-backend-go/v2/docusign_auth"
)

func main() {
integrationKey := "557677f2-1e2f-4955-aaa6-1ef44630e01d"
userID := "3a1d118f-3083-4c25-8306-11b7400f7c03"
privateKey :=`
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAh0M2mIGaJjP8S/FxZR7nRsatCR/KpCPBFBbxalZffykqtTID
KNeDhJ5RvJKAVoJlLaLoUYSYloVaeSAwQdbn4F+Lsnll3mCGocwdl/W8998Lc/Ln
MaNQhpekBoXaq8vbj251jxnRcsdI9yl/YyQo3jZnM77OWtEF7dyvS6V9cMprT2Ca
eIJPj0Ck3/P9rGwE3DdiEXZDetgkuNQyMavfvLKltCNu3qQXFA95PXlHs2E57OrS
CNIQT37jfInXuIyCoGjDSq+U3ZE047UG5Id8/OFvcP1z5iUdwvueoEximt/kSvR0
ZcNfjqyJnnYq80OKwTdalZEOzAEkt/U2QuB9jwIDAQABAoIBAB01GstopOgd7ptZ
efJrb2ZdjUy8lC3IWK9lWuDq4LkdIw84Su1dSBVxeFXfTp4fjwiBNmgv2SEbj5M7
K6Bz7uMIzqoNw7z2m+vBHxzKn/DoNVlmuJyD1uYRRYZxDext2y3IHNN3MD54IN3a
FJtMWhTNq5BFYdrDauPXdPTBOeqKQgMoQl6OwHIx4WJXYxRgIgrvBYRzLSwG/u4V
tAvj1/J0PSSOY5A1qn6L2ii9abVElFr2zZC5MtG3oG1TLCidzxZFt/5qSHGo+0tU
l/7YRXWxadfYrPmS9akbC7SQn9WJQdIlfejtuQ6hmKTCCjhTp2BUba3kTI3PcM46
GzW3WQECgYEA08lwWFZng3bm3KrDx3ljjZ+cuL8vOXfLeYuaIPmNu5eXrR5ZR9Ar
UyUmnZbL8rhK9dNCmRGX293uuppwJolfMAy+kcvQ4HsRQ5tOgXmP8VhKzA4RRc+h
I7uIGQ7NmKI6Wm/wJ+T3Okw0h8ze8MW66/D5IHDQSoGQZH9kkA9+MQECgYEAo4AS
jB/b/SWhwrRUI8m+5Tjy5WO9ZkRZ54zpRnAr5HsjSBgb72IGbJKwleKwb3FxwPTQ
ZlgKP2O5rGzZOEZcIryIliCpV4C/XeFVwUKxo/A5jkL+U9ZQwoA0/0ssdE/7uLzG
FiKcFK7OhsFg+dNEX1ForM9cGN5OzY8sGzylHo8CgYAc0Nq1WkRJUeNFgQKUYILY
ITB8vp6ZTiBkUEdPV0Uekhi0GF4DdGKAtJxVctAbHVItsmnsU8V6x+6UezDpPWWz
Lvi686Ve9b+6mCYNXdHk/6NlskBNZFvDdd+lsSruKpyP840UkIXG69l15L0su2qc
cbQj4tWkXY6c7exr4X/FAQKBgDmGTABFDU9puBoa/CeDSci4Wq1ehDrA/ai8KS8B
NFA1CtrIsLtuj7gPfFWf5levYEh1WgVIIILhAWiq+1oTV0NZdezsHOiOgcX0DAns
/zcgw/9LjtPMaamlFgBkYIWjxnre4ArVrniQcFV1IDuFm16189ApPMv7G1qzbt8+
XRH9AoGAceSrZLbceDkqgprTTV1BEOFY+Ti9edBIUW+CqN6KkB66OXHGWlab/PZc
OCHRPAjEijZcVXm8IPC2yi/s0agQAB8dKO2L1X0EtvxkWSg2s/YXFpp0QccQToTo
lRFb9injNzixUOq18Z62XcC/yqMB7QtgRw5x5OQk0HNWpL7h6KM=
-----END RSA PRIVATE KEY-----
`
token, err := docusignauth.GetAccessToken(integrationKey, userID, privateKey)
if err != nil {
fmt.Println(err)
}

fmt.Println(token)
}
18 changes: 18 additions & 0 deletions cla-backend-go/v2/sign/docusign.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright The Linux Foundation and each contributor to LFX.
// SPDX-License-Identifier: MIT

package sign

import (
"context"
"log"
)

func (s *service) getAccessToken(ctx context.Context) (string, error) {
f := logrus.Fields{
"functionName": "sign.getAccessToken",
}

// Get the access token
jwtAssertion, jwterr := jwtToken()
}
24 changes: 24 additions & 0 deletions cla-backend-go/v2/sign/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package sign

import (
"context"
"errors"
"fmt"
"strings"
Expand All @@ -20,6 +21,7 @@ import (
"github.com/communitybridge/easycla/cla-backend-go/utils"
"github.com/communitybridge/easycla/cla-backend-go/v2/organization-service/client/organizations"
"github.com/go-openapi/runtime/middleware"

)

// Configure API call
Expand Down Expand Up @@ -75,6 +77,28 @@ func Configure(api *operations.EasyclaAPI, service Service) {
}
return sign.NewRequestCorporateSignatureOK().WithPayload(resp)
})

api.SignRequestIndividualSignatureHandler = sign.RequestIndividualSignatureHandlerFunc(
func(params sign.RequestIndividualSignatureParams) middleware.Responder {
reqId := utils.GetRequestID(params.XREQUESTID)
ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqId)
f := logrus.Fields{
"functionName": "v2.sign.handlers.SignRequestIndividualSignatureHandler",
utils.XREQUESTID: ctx.Value(utils.XREQUESTID),
"CompanyID": params.Input.CompanySfid,
"ProjectSFID": params.Input.ProjectSfid,
"authorityName": params.Input.AuthorityName,
"authorityEmail": params.Input.AuthorityEmail,
}
log.WithFields(f).Debug("processing request")
resp, err := service.RequestIndividualSignature(ctx, params.Input)
if err != nil {
log.WithFields(f).WithError(err).Warn("problem requesting individual signature")
return sign.NewRequestIndividualSignatureBadRequest().WithPayload(errorResponse(reqId, err))
}
return sign.NewRequestIndividualSignatureOK().WithPayload(resp)
})

}

type codedResponse interface {
Expand Down
17 changes: 17 additions & 0 deletions cla-backend-go/v2/sign/jwt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright The Linux Foundation and each contributor to LFX.
// SPDX-License-Identifier: MIT

package sign

import (
"github.com/golang-jwt/jwt"
"github.com/sirupsen/logrus"
)

const

func jwtToken() (string, error) {
claims := jwt.MapClaims{
"iss": ,
}
}
Loading
Loading