Commit c10902cd authored by Tristan Claverie's avatar Tristan Claverie

Merge branch '180_platform_create_contract' into 'master'

180 platform create contract

Missing bits for this MR:

- Authentication checks
- Invitation triggers
- Improved mail tests

This will be fixed in a future MR.

See merge request !21
parents 20a9ed21 b4c9d40c
Pipeline #228 passed with stage
......@@ -19,6 +19,7 @@ Unit tests:
script:
- "ln -s $(pwd) $GOPATH/src/dfss"
- "./build/deps.sh"
- "cd $GOPATH/src/dfss && go install ./..."
- "go test -coverprofile auth.part -v dfss/auth"
- "go test -coverprofile mgdb.part -v dfss/mgdb"
- "go test -coverprofile mails.part -v dfss/mails"
......@@ -26,6 +27,7 @@ Unit tests:
- "go test -coverprofile authority.part -v dfss/dfssp/authority"
- "go test -coverprofile dfssp_user.part -v dfss/dfssp/user"
- "go test -coverprofile dfssp_contract.part -v dfss/dfssp/contract"
- "go test -coverprofile dfssp_templates.part -v dfss/dfssp/templates"
- "echo 'mode: set' *part > c.out"
- "grep -h -v 'mode: set' *part >> c.out"
- "go tool cover -html=c.out -o coverage.html"
......@@ -37,12 +39,14 @@ ARM tests:
script:
- "ln -s -f $(pwd) $GOPATH/src/dfss"
- "./build/deps.sh"
- "cd $GOPATH/src/dfss && go install ./..."
- "go test -cover -short -v dfss/auth"
- "go test -cover -short -v dfss/mgdb"
- "go test -cover -short -v dfss/net"
- "go test -cover -short -v dfss/dfssp/authority"
- "go test -cover -short -v dfss/dfssp/user"
- "go test -cover -short -v dfss/dfssp/contract"
- "go test -cover -short -v dfss/dfssp/templates"
Code lint:
stage: test
......
......@@ -84,9 +84,10 @@ func GetSelfSignedCertificate(days int, serial uint64, country, organization, un
OrganizationalUnit: []string{unit},
CommonName: cn,
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(0, 0, days),
IsCA: true,
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(0, 0, days),
BasicConstraintsValid: true,
IsCA: true,
}
der, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
......
......@@ -5,3 +5,4 @@ go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/protoc-gen-go
go get -u google.golang.org/grpc
go get -u github.com/pborman/uuid
go get -u github.com/bmizerany/assert
package api
const (
// INTERR is the error code for an internal server error
INTERR = -1
// SUCCESS is the error code for a successful requeset
SUCCESS = 0
// INVARG is the error code for an invalid argument
INVARG = 1
)
......@@ -38,6 +38,41 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
const _ = proto.ProtoPackageIsVersion1
type ErrorCode_Code int32
const (
// SUCCESS is the error code for a successful request
ErrorCode_SUCCESS ErrorCode_Code = 0
// INVARG is the error code for an invalid argument
ErrorCode_INVARG ErrorCode_Code = 1
// BADAUTH is the error code for a bad authentication
ErrorCode_BADAUTH ErrorCode_Code = 2
// WARNING is the error code for a success state containing a specific warning message
ErrorCode_WARNING ErrorCode_Code = 3
// INTERR is the error code for an internal server error
ErrorCode_INTERR ErrorCode_Code = -1
)
var ErrorCode_Code_name = map[int32]string{
0: "SUCCESS",
1: "INVARG",
2: "BADAUTH",
3: "WARNING",
-1: "INTERR",
}
var ErrorCode_Code_value = map[string]int32{
"SUCCESS": 0,
"INVARG": 1,
"BADAUTH": 2,
"WARNING": 3,
"INTERR": -1,
}
func (x ErrorCode_Code) String() string {
return proto.EnumName(ErrorCode_Code_name, int32(x))
}
func (ErrorCode_Code) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} }
// RegisterRequest message contains the client's email adress and his
// public key
type RegisterRequest struct {
......@@ -53,8 +88,8 @@ func (*RegisterRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, [
// ErrorCode message contains an error code (see dffs/dfssp/api/errorCodes.go)
// and a message
type ErrorCode struct {
Code int32 `protobuf:"varint,1,opt,name=code" json:"code,omitempty"`
Message string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
Code ErrorCode_Code `protobuf:"varint,1,opt,name=code,enum=api.ErrorCode_Code" json:"code,omitempty"`
Message string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
}
func (m *ErrorCode) Reset() { *m = ErrorCode{} }
......@@ -95,12 +130,13 @@ func (m *Empty) String() string { return proto.CompactTextString(m) }
func (*Empty) ProtoMessage() {}
func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
// PostContractRequest message contains the contract as SHA-512 hash, the list of signers
// as an array of string, and a comment
// PostContractRequest message contains the contract as SHA-512 hash, its filename,
// the list of signers as an array of strings, and a comment
type PostContractRequest struct {
Contract string `protobuf:"bytes,1,opt,name=contract" json:"contract,omitempty"`
Signer []string `protobuf:"bytes,2,rep,name=signer" json:"signer,omitempty"`
Comment string `protobuf:"bytes,3,opt,name=comment" json:"comment,omitempty"`
Hash string `protobuf:"bytes,1,opt,name=hash" json:"hash,omitempty"`
Filename string `protobuf:"bytes,2,opt,name=filename" json:"filename,omitempty"`
Signer []string `protobuf:"bytes,3,rep,name=signer" json:"signer,omitempty"`
Comment string `protobuf:"bytes,4,opt,name=comment" json:"comment,omitempty"`
}
func (m *PostContractRequest) Reset() { *m = PostContractRequest{} }
......@@ -139,6 +175,7 @@ func init() {
proto.RegisterType((*PostContractRequest)(nil), "api.PostContractRequest")
proto.RegisterType((*JoinSignatureRequest)(nil), "api.JoinSignatureRequest")
proto.RegisterType((*ReadySignRequest)(nil), "api.ReadySignRequest")
proto.RegisterEnum("api.ErrorCode_Code", ErrorCode_Code_name, ErrorCode_Code_value)
}
// Reference imports to suppress errors if they are not otherwise used.
......@@ -338,31 +375,37 @@ var _Platform_serviceDesc = grpc.ServiceDesc{
}
var fileDescriptor0 = []byte{
// 416 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x53, 0xd1, 0xaa, 0xd3, 0x40,
0x10, 0xf5, 0x36, 0xb7, 0xf7, 0xb6, 0x63, 0xef, 0xb5, 0x4c, 0xab, 0xc4, 0x3e, 0x88, 0xec, 0x93,
0xf8, 0x50, 0xa1, 0x8a, 0xa0, 0xf8, 0x22, 0xa1, 0x2f, 0x22, 0x52, 0xb6, 0xf4, 0x03, 0x62, 0x32,
0xc6, 0xa5, 0x4d, 0x36, 0xee, 0x6e, 0x84, 0x7e, 0xa3, 0x3f, 0xe5, 0x66, 0xb3, 0x09, 0x8d, 0x06,
0xc1, 0xa7, 0xec, 0x99, 0xd9, 0x73, 0x66, 0xe6, 0xcc, 0x06, 0x30, 0xe3, 0xbb, 0x68, 0x4f, 0xea,
0xa7, 0x48, 0x48, 0xaf, 0x4b, 0x25, 0x8d, 0xc4, 0x20, 0x2e, 0x05, 0x7b, 0x07, 0x8f, 0x38, 0x65,
0x42, 0x1b, 0x52, 0x9c, 0x7e, 0x54, 0xa4, 0x0d, 0x2e, 0x61, 0x4c, 0x79, 0x2c, 0x4e, 0xe1, 0xd5,
0xf3, 0xab, 0x17, 0x53, 0xde, 0x00, 0x9c, 0x43, 0x70, 0xa4, 0x73, 0x38, 0x72, 0xb1, 0xfa, 0x68,
0xa9, 0xd3, 0xad, 0x52, 0x52, 0x45, 0x32, 0x25, 0x44, 0xb8, 0x4e, 0xec, 0xd7, 0x71, 0xc6, 0xdc,
0x9d, 0x31, 0x84, 0xdb, 0x9c, 0xb4, 0x8e, 0x33, 0xf2, 0xb4, 0x16, 0x5a, 0xea, 0xc3, 0x8f, 0x95,
0xf9, 0xfe, 0xef, 0x8a, 0x36, 0x6a, 0xe4, 0x91, 0x0a, 0x4f, 0x6e, 0x00, 0xfb, 0x0c, 0xf7, 0x6d,
0xc3, 0x94, 0x1e, 0x34, 0x29, 0x5c, 0xc1, 0x44, 0x49, 0x69, 0x22, 0x52, 0xc6, 0x09, 0xcc, 0x78,
0x87, 0xf1, 0x19, 0x40, 0x72, 0x12, 0x54, 0x34, 0xd9, 0x91, 0xcb, 0x5e, 0x44, 0xd8, 0x2d, 0x8c,
0xb7, 0x79, 0x69, 0xce, 0x2c, 0x81, 0xc5, 0x4e, 0x6a, 0x13, 0xc9, 0xc2, 0xa8, 0x38, 0x31, 0x6d,
0x67, 0x56, 0x3b, 0xf1, 0x21, 0xdf, 0x5c, 0x87, 0xf1, 0x09, 0xdc, 0x68, 0x91, 0x15, 0xa4, 0xac,
0x6e, 0x60, 0x33, 0x1e, 0xd5, 0x63, 0x27, 0x32, 0xcf, 0x6d, 0x89, 0x30, 0x68, 0xc6, 0xf6, 0x90,
0x7d, 0x81, 0xe5, 0x27, 0x29, 0x8a, 0xbd, 0xbd, 0x17, 0x9b, 0x4a, 0x51, 0x5b, 0x85, 0xc1, 0xac,
0x55, 0x3d, 0x54, 0x22, 0xf5, 0x95, 0x7a, 0xb1, 0xda, 0xe0, 0x52, 0xfa, 0x19, 0xee, 0xb8, 0x3b,
0xb3, 0xb7, 0x30, 0xe7, 0x14, 0xa7, 0xe7, 0x5a, 0xf0, 0x3f, 0xb4, 0x36, 0xbf, 0x46, 0x30, 0xd9,
0x9d, 0x62, 0xf3, 0x4d, 0xaa, 0x1c, 0x37, 0x30, 0x69, 0x0d, 0xc5, 0xe5, 0xda, 0xbe, 0x89, 0xf5,
0x1f, 0x0f, 0x62, 0x75, 0xef, 0xa2, 0xdd, 0xae, 0xd9, 0x03, 0x7c, 0x05, 0xd7, 0xf5, 0xfe, 0x70,
0xee, 0x32, 0x17, 0xab, 0x5c, 0x2d, 0x7a, 0x0a, 0xcd, 0x86, 0x2c, 0xe1, 0x25, 0xc0, 0xa1, 0x50,
0x6d, 0x19, 0x68, 0x04, 0x6b, 0xe3, 0x07, 0xc4, 0xdf, 0xc3, 0xec, 0x72, 0x15, 0x18, 0xba, 0x1b,
0x03, 0xdb, 0x19, 0xe0, 0x7e, 0x80, 0xbb, 0x9e, 0xc3, 0xf8, 0xd4, 0x5d, 0x19, 0x72, 0x7d, 0x80,
0xfd, 0x06, 0xa6, 0x9d, 0x9f, 0xf8, 0xd8, 0x4f, 0xd2, 0xf7, 0xf7, 0x6f, 0xd6, 0xd7, 0x1b, 0xf7,
0x3b, 0xbd, 0xfe, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x17, 0x95, 0x89, 0x4a, 0x64, 0x03, 0x00, 0x00,
// 501 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x53, 0x5f, 0x8f, 0x93, 0x40,
0x10, 0xbf, 0x16, 0xee, 0xda, 0xce, 0xf5, 0x2a, 0x99, 0x56, 0x83, 0x7d, 0x30, 0x17, 0x5e, 0x34,
0x3e, 0xd4, 0xa4, 0x1a, 0x13, 0x8d, 0x2f, 0x88, 0xcd, 0x79, 0xc6, 0x34, 0xcd, 0x72, 0xe8, 0x33,
0xd2, 0xb9, 0x96, 0x5c, 0x61, 0xeb, 0xb2, 0xd5, 0xf4, 0xcb, 0xf8, 0x85, 0xfc, 0x50, 0xca, 0x2e,
0xd0, 0x14, 0x25, 0x26, 0xf2, 0x00, 0xfb, 0x9b, 0x3f, 0xbf, 0x99, 0xf9, 0xcd, 0x02, 0xb8, 0x62,
0x0b, 0xcf, 0x27, 0xf1, 0x2d, 0x8e, 0x28, 0x9b, 0x6c, 0x05, 0x97, 0x1c, 0x8d, 0x70, 0x1b, 0x3b,
0xaf, 0xe0, 0x1e, 0xa3, 0x55, 0x9c, 0x49, 0x12, 0x8c, 0xbe, 0xee, 0x28, 0x93, 0x38, 0x82, 0x53,
0x4a, 0xc2, 0x78, 0x63, 0xb7, 0x2e, 0x5b, 0x4f, 0x7a, 0xac, 0x00, 0x68, 0x81, 0x71, 0x47, 0x7b,
0xbb, 0xad, 0x6d, 0xea, 0xe8, 0xfc, 0x68, 0x41, 0x6f, 0x26, 0x04, 0x17, 0x1e, 0x5f, 0x12, 0x3e,
0x06, 0x33, 0xca, 0xbf, 0x3a, 0x69, 0x30, 0x1d, 0x4e, 0x72, 0xf2, 0xc9, 0xc1, 0x3b, 0x51, 0x2f,
0xa6, 0x03, 0xd0, 0x86, 0x4e, 0x42, 0x59, 0x16, 0xae, 0xa8, 0x24, 0xab, 0xa0, 0x33, 0x07, 0x53,
0x53, 0x9d, 0x43, 0xc7, 0x0f, 0x3c, 0x6f, 0xe6, 0xfb, 0xd6, 0x09, 0x02, 0x9c, 0x5d, 0xcf, 0x3f,
0xb9, 0xec, 0xca, 0x6a, 0x29, 0xc7, 0x5b, 0xf7, 0x9d, 0x1b, 0xdc, 0xbc, 0xb7, 0xda, 0x0a, 0x7c,
0x76, 0xd9, 0xfc, 0x7a, 0x7e, 0x65, 0x19, 0x38, 0x54, 0x51, 0x37, 0x33, 0xc6, 0xac, 0x5f, 0xd5,
0xd3, 0xca, 0x67, 0x3b, 0x77, 0x77, 0x72, 0xfd, 0xef, 0xb9, 0x72, 0xab, 0xe4, 0x77, 0x94, 0x96,
0xcd, 0x14, 0xc0, 0xf9, 0x08, 0x83, 0x4a, 0x16, 0x5a, 0x06, 0x19, 0x09, 0x1c, 0x43, 0x57, 0x70,
0x2e, 0x3d, 0x12, 0x52, 0x13, 0xf4, 0xd9, 0x01, 0xe3, 0x23, 0x80, 0x68, 0x13, 0x53, 0x5a, 0x78,
0xdb, 0xda, 0x7b, 0x64, 0x71, 0x3a, 0x70, 0x3a, 0x4b, 0xb6, 0x72, 0xef, 0x7c, 0x87, 0xe1, 0x82,
0x67, 0xd2, 0xe3, 0xa9, 0x14, 0x61, 0x24, 0xab, 0xce, 0x10, 0xcc, 0x75, 0x98, 0xad, 0xcb, 0xc6,
0xf4, 0x59, 0xd5, 0xbb, 0x8d, 0x37, 0x94, 0x86, 0x49, 0xa5, 0xd3, 0x01, 0xe3, 0x03, 0x38, 0xcb,
0xe2, 0x55, 0x4a, 0xc2, 0x36, 0x2e, 0x8d, 0xdc, 0x53, 0x22, 0x25, 0x6d, 0xc4, 0x93, 0x24, 0x2f,
0x6b, 0x9b, 0x85, 0xb4, 0x25, 0xcc, 0xa5, 0x1d, 0x7d, 0xe0, 0x71, 0xea, 0xe7, 0x71, 0xa1, 0xdc,
0x09, 0xaa, 0x2a, 0x3b, 0xd0, 0x8f, 0xca, 0x66, 0x82, 0x5d, 0xbc, 0x2c, 0x3b, 0xa8, 0xd9, 0x54,
0x77, 0x5b, 0x5e, 0xce, 0x75, 0xc1, 0xf4, 0xd9, 0x79, 0x09, 0x16, 0xa3, 0x70, 0xb9, 0x57, 0x84,
0xff, 0xc1, 0x35, 0xfd, 0xd9, 0x86, 0xee, 0x62, 0x13, 0xca, 0x5b, 0x2e, 0x12, 0x9c, 0x42, 0xb7,
0x12, 0x19, 0x47, 0xfa, 0xc2, 0xfc, 0x71, 0x15, 0xc7, 0x83, 0xfa, 0x35, 0x72, 0x4e, 0xf0, 0x19,
0x98, 0x6a, 0xa7, 0x68, 0x69, 0xcf, 0xd1, 0x7a, 0xc7, 0xc3, 0x1a, 0x43, 0xb1, 0xb5, 0x3c, 0xe1,
0x29, 0x40, 0x90, 0x8a, 0xaa, 0x0c, 0x14, 0x84, 0x6a, 0x19, 0x0d, 0xe4, 0xaf, 0xa1, 0x7f, 0xbc,
0x1e, 0xb4, 0x75, 0x44, 0xc3, 0xc6, 0x1a, 0x72, 0xdf, 0xc0, 0x45, 0x4d, 0x61, 0x7c, 0xa8, 0x43,
0x9a, 0x54, 0x6f, 0xc8, 0x7e, 0x01, 0xbd, 0x83, 0x9e, 0x78, 0xbf, 0x9c, 0xa4, 0xae, 0xef, 0xdf,
0x59, 0x5f, 0xce, 0xf4, 0x8f, 0xfc, 0xfc, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8b, 0x69, 0xb3,
0x9e, 0xde, 0x03, 0x00, 0x00,
}
......@@ -21,7 +21,19 @@ message RegisterRequest {
// ErrorCode message contains an error code (see dffs/dfssp/api/errorCodes.go)
// and a message
message ErrorCode {
int32 code = 1;
enum Code {
// SUCCESS is the error code for a successful request
SUCCESS = 0;
// INVARG is the error code for an invalid argument
INVARG = 1;
// BADAUTH is the error code for a bad authentication
BADAUTH = 2;
// WARNING is the error code for a success state containing a specific warning message
WARNING = 3;
// INTERR is the error code for an internal server error
INTERR = -1;
}
Code code = 1;
string message = 2;
}
......@@ -43,19 +55,20 @@ message RegisteredUser {
message Empty {
}
// PostContractRequest message contains the contract as SHA-512 hash, the list of signers
// as an array of string, and a comment
// PostContractRequest message contains the contract as SHA-512 hash, its filename,
// the list of signers as an array of strings, and a comment
message PostContractRequest {
string contract = 1;
repeated string signer = 2;
string comment = 3;
string hash = 1;
string filename = 2;
repeated string signer = 3;
string comment = 4;
}
// JoinSignatureRequest message contains the contract to join unique identifier
// and the port the client will be listening at
message JoinSignatureRequest {
string contractUuid = 1;
uint32 port =2;
uint32 port = 2;
}
// ReadySignRequest contains the contract unique identitier that is ready to be signed
......
package contract_test // Using another package to avoid import cycles
import (
"fmt"
"os"
"path/filepath"
"testing"
"dfss/dfssp/entities"
"dfss/dfssp/server"
"dfss/mgdb"
"dfss/net"
"github.com/bmizerany/assert"
"gopkg.in/mgo.v2/bson"
)
var err error
var collection *mgdb.MongoCollection
var manager *mgdb.MongoManager
var dbURI string
var repository *entities.ContractRepository
func TestMain(m *testing.M) {
dbURI = os.Getenv("DFSS_MONGO_URI")
if dbURI == "" {
dbURI = "mongodb://localhost/dfss-test"
}
manager, err = mgdb.NewManager(dbURI)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
collection = manager.Get("demo")
repository = entities.NewContractRepository(collection)
// Start platform server
keyPath := filepath.Join(os.Getenv("GOPATH"), "src", "dfss", "dfssp", "testdata")
srv := server.GetServer(keyPath, dbURI, true)
go func() { _ = net.Listen("localhost:9090", srv) }()
// Run
code := m.Run()
dropDataset()
manager.Close()
os.Exit(code)
}
func TestAddSigner(t *testing.T) {
c := entities.NewContract()
id := bson.NewObjectId()
c.AddSigner(nil, "mail1", "hash1")
c.AddSigner(&id, "mail2", "hash2")
signers := c.Signers
if len(signers) != 2 {
t.Fatal("Signers are not inserted correctly")
}
assert.Equal(t, signers[0].Email, "mail1")
assert.Equal(t, signers[0].Hash, "hash1")
assert.Equal(t, signers[0].UserID.Hex(), "000000000000000000000000")
assert.Equal(t, signers[1].Email, "mail2")
assert.Equal(t, signers[1].Hash, "hash2")
assert.Equal(t, signers[1].UserID.Hex(), id.Hex())
}
func assertContractEqual(t *testing.T, contract, fetched entities.Contract) {
assert.Equal(t, contract.File, fetched.File)
assert.Equal(t, contract.Date.Unix(), fetched.Date.Unix())
assert.Equal(t, contract.Comment, fetched.Comment)
assert.Equal(t, contract.Ready, fetched.Ready)
assert.Equal(t, contract.Signers, fetched.Signers)
}
// Insert a contract with 2 users and check the fields are correctly persisted
func TestInsertContract(t *testing.T) {
c := entities.NewContract()
c.AddSigner(nil, "mail1", "hash1")
c.AddSigner(nil, "mail1", "hash1")
c.File.Name = "file"
c.File.Hash = "hashFile"
c.File.Hosted = false
c.Comment = "comment"
c.Ready = true
_, err := repository.Collection.Insert(c)
if err != nil {
t.Fatal("Contract not inserted successfully in the database:", err)
}
fetched := entities.Contract{}
selector := entities.Contract{
ID: c.ID,
}
err = repository.Collection.FindByID(selector, &fetched)
if err != nil {
t.Fatal("Contract could not be successfully retrieved:", err)
}
assertContractEqual(t, *c, fetched)
}
package contract
import (
"crypto/sha512"
"log"
"time"
"dfss/dfssp/api"
"dfss/dfssp/entities"
"dfss/dfssp/templates"
"dfss/mgdb"
"gopkg.in/mgo.v2/bson"
)
// Builder contains internal information to create a new contract.
type Builder struct {
m *mgdb.MongoManager
in *api.PostContractRequest
signers []entities.User
missingSigners []string
contract *entities.Contract
}
// NewContractBuilder creates a new builder from current context.
// Call Execute() on the builder to get a result from it.
func NewContractBuilder(m *mgdb.MongoManager, in *api.PostContractRequest) *Builder {
return &Builder{
m: m,
in: in,
}
}
// Execute triggers the creation of a new contract.
func (c *Builder) Execute() *api.ErrorCode {
inputError := c.checkInput()
if inputError != nil {
return inputError
}
err := c.fetchSigners()
if err != nil {
log.Println(err)
return &api.ErrorCode{Code: api.ErrorCode_INTERR, Message: "Database error"}
}
err = c.addContract()
if err != nil {
log.Println(err)
return &api.ErrorCode{Code: api.ErrorCode_INTERR}
}
if len(c.missingSigners) > 0 {
c.sendPendingContractMail()
return &api.ErrorCode{Code: api.ErrorCode_WARNING, Message: "Some users are not ready yet"}
}
c.sendNewContractMail()
return &api.ErrorCode{Code: api.ErrorCode_SUCCESS}
}
// checkInput checks that a PostContractRequest is well-formed
func (c *Builder) checkInput() *api.ErrorCode {
if len(c.in.Signer) == 0 {
return &api.ErrorCode{Code: api.ErrorCode_INVARG, Message: "Expecting at least one signer"}
}
if len(c.in.Filename) == 0 {
return &api.ErrorCode{Code: api.ErrorCode_INVARG, Message: "Expecting a valid filename"}
}
if len(c.in.Hash) != sha512.Size {
return &api.ErrorCode{Code: api.ErrorCode_INVARG, Message: "Expecting a valid sha512 hash"}
}
return nil
}
// fetchSigners fetches authenticated users for this contract from the DB
func (c *Builder) fetchSigners() error {
var users []entities.User
// Fetch users where email is part of the signers slice in request
// and authentication is valid
err := c.m.Get("users").FindAll(bson.M{
"expiration": bson.M{"$gt": time.Now()},
"email": bson.M{"$in": c.in.Signer},
}, &users)
if err != nil {
return err
}
// Locate missing users
for _, s := range c.in.Signer {
found := false
for _, u := range users {
if s == u.Email {
found = true
break
}
}
if !found {
c.missingSigners = append(c.missingSigners, s) // a list of not valid mail adress
}
}
c.signers = users
return nil
}
// addContract inserts the contract into the DB
func (c *Builder) addContract() error {
contract := entities.NewContract()
for _, s := range c.signers {
contract.AddSigner(&s.ID, s.Email, s.CertHash)
}
for _, s := range c.missingSigners {
contract.AddSigner(nil, s, "")
}
contract.Comment = c.in.Comment
contract.Ready = len(c.missingSigners) == 0
contract.File.Name = c.in.Filename
contract.File.Hash = c.in.Hash
contract.File.Hosted = false
_, err := c.m.Get("contracts").Insert(contract)
c.contract = contract
return err
}
// sendNewContractMail sends a mail to each known signer in a contract containing the DFSS file
func (c *Builder) sendNewContractMail() {
conn := templates.MailConn()
if conn == nil {
return
}
defer func() { _ = conn.Close() }()
rcpts := make([]string, len(c.contract.Signers))
for i, s := range c.contract.Signers {
rcpts[i] = s.Email
}
content, err := templates.Get("contract", c)
if err != nil {
log.Println(err)
return
}
file, err := GetJSON(c.contract, nil)
if err != nil {
log.Println(err)
return
}
fileSmallHash := c.contract.ID.Hex()[0:8] // Pretty hash instead of huge one
_ = conn.Send(
rcpts,
"[DFSS] You are invited to sign "+c.contract.File.Name,
content,
[]string{"application/json"},
[]string{fileSmallHash + ".json"},
[][]byte{file},
)
}
// sendPendingContractMail sends a mail to non-authenticated signers to invite them
func (c *Builder) sendPendingContractMail() {
conn := templates.MailConn()
if conn == nil {
return
}
defer func() { _ = conn.Close() }()
content, err := templates.Get("invitation", c)
if err != nil {
log.Println(err)
return
}
_ = conn.Send(c.missingSigners, "[DFSS] You are invited to sign "+c.contract.File.Name, content, nil, nil, nil)
}
package contract_test
import (
"crypto/sha512"
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"
"dfss/auth"
"dfss/dfssp/api"
"dfss/dfssp/entities"
"dfss/net"
"github.com/bmizerany/assert"
"golang.org/x/net/context"
)
var user1, user2, user3 *entities.User
var defaultHash = sha512.Sum512([]byte{0})
var defaultHashStr = string(defaultHash[:sha512.Size])
func createDataset() {
user1 = entities.NewUser() // Regular user
user2 = entities.NewUser() // Regular user
user3 = entities.NewUser() // Non-auth user