starter.go 2.81 KB
Newer Older
Loïck Bonniot's avatar
Loïck Bonniot committed
1 2 3 4
package contract

import (
	n "net"
5
	"time"
Loïck Bonniot's avatar
Loïck Bonniot committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

	"dfss/auth"
	"dfss/dfssp/api"
	"dfss/dfssp/common"
	"dfss/dfssp/entities"
	"dfss/mgdb"
	"dfss/net"
	"gopkg.in/mgo.v2/bson"
)

// JoinSignature allows a client to wait for other clients connections on a specific contract.
// Firstly, every client present BEFORE the call of this function is sent to the stream.
// Then, client information is sent to the stream as it's available.
//
// Please note that the current user will also receive it's own information.
// There is no timeout, this function will shut down on stream disconnection or on error.
func JoinSignature(db *mgdb.MongoManager, rooms *common.WaitingGroupMap, in *api.JoinSignatureRequest, stream api.Platform_JoinSignatureServer) {
	ctx := stream.Context()
	state, addr, _ := net.GetTLSState(&ctx)
	hash := auth.GetCertificateHash(state.VerifiedChains[0][0])

27
	if !checkJoinSignatureRequest(db, &stream, in.ContractUuid, hash) {
Loïck Bonniot's avatar
Loïck Bonniot committed
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
		return
	}

	// Join room
	roomID := "contract_" + in.ContractUuid
	channel, pendingSigners := rooms.Join(roomID)

	// Send pendingSigners
	for _, p := range pendingSigners {
		err := sendUserToStream(&stream, in.ContractUuid, p.(*api.User))
		if err != nil {
			rooms.Unjoin(roomID, channel)
			return
		}
	}

	// Broadcast self identity
	host, _, _ := n.SplitHostPort(addr.String())
	rooms.Broadcast(roomID, &api.User{
		KeyHash: hash,
		Email:   net.GetCN(&ctx),
		Ip:      host,
		Port:    in.Port,
	})

	// Listen for others
	for {
		select {
		case user, ok := <-channel:
57
			if !ok { // Channel is closed, means that the room is closed
Loïck Bonniot's avatar
Loïck Bonniot committed
58 59 60 61 62 63 64
				return
			}
			err := sendUserToStream(&stream, in.ContractUuid, user.(*api.User))
			if err != nil {
				rooms.Unjoin(roomID, channel)
				return
			}
65 66 67 68
		case <-ctx.Done(): // Disconnect
			rooms.Unjoin(roomID, channel)
			return
		case <-time.After(time.Hour): // Timeout
Loïck Bonniot's avatar
Loïck Bonniot committed
69 70 71 72 73 74
			rooms.Unjoin(roomID, channel)
			return
		}
	}
}

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
func checkJoinSignatureRequest(db *mgdb.MongoManager, stream *api.Platform_JoinSignatureServer, contractUUID string, clientHash []byte) bool {
	if !bson.IsObjectIdHex(contractUUID) {
		_ = (*stream).Send(&api.UserConnected{
			ErrorCode: &api.ErrorCode{
				Code:    api.ErrorCode_INVARG,
				Message: "invalid contract uuid",
			},
		})
		return false
	}

	repository := entities.NewContractRepository(db.Get("contracts"))
	if !repository.CheckAuthorization(clientHash, bson.ObjectIdHex(contractUUID)) {
		_ = (*stream).Send(&api.UserConnected{
			ErrorCode: &api.ErrorCode{
				Code:    api.ErrorCode_INVARG,
				Message: "unauthorized signature",
			},
		})
		return false
	}
	return true
}

Loïck Bonniot's avatar
Loïck Bonniot committed
99 100 101 102 103 104 105
func sendUserToStream(stream *api.Platform_JoinSignatureServer, contractUUID string, user *api.User) error {
	return (*stream).Send(&api.UserConnected{
		ErrorCode:    &api.ErrorCode{Code: api.ErrorCode_SUCCESS},
		ContractUuid: contractUUID,
		User:         user,
	})
}