2019-11-21 22:34:49 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package referrals
|
|
|
|
|
2019-11-25 21:36:36 +00:00
|
|
|
import (
|
|
|
|
"context"
|
2019-11-21 22:34:49 +00:00
|
|
|
|
2019-11-25 21:36:36 +00:00
|
|
|
"github.com/skyrings/skyring-common/tools/uuid"
|
2019-11-08 20:40:39 +00:00
|
|
|
"github.com/spacemonkeygo/monkit/v3"
|
2019-11-25 21:36:36 +00:00
|
|
|
"github.com/zeebo/errs"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
|
2019-12-27 11:48:47 +00:00
|
|
|
"storj.io/common/pb"
|
|
|
|
"storj.io/common/rpc"
|
|
|
|
"storj.io/common/signing"
|
|
|
|
"storj.io/common/storj"
|
2019-12-16 17:59:01 +00:00
|
|
|
"storj.io/storj/private/dbutil"
|
2019-11-25 21:36:36 +00:00
|
|
|
"storj.io/storj/satellite/console"
|
|
|
|
)
|
|
|
|
|
|
|
|
var mon = monkit.Package()
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ErrUsedEmail is an error class for reporting already used emails.
|
|
|
|
ErrUsedEmail = errs.Class("email used error")
|
|
|
|
)
|
|
|
|
|
|
|
|
// Config is for referrals service.
|
2019-11-21 22:34:49 +00:00
|
|
|
type Config struct {
|
|
|
|
ReferralManagerURL storj.NodeURL
|
|
|
|
}
|
2019-11-25 21:36:36 +00:00
|
|
|
|
|
|
|
// Service allows communicating with the Referral Manager
|
|
|
|
//
|
|
|
|
// architecture: Service
|
|
|
|
type Service struct {
|
|
|
|
log *zap.Logger
|
|
|
|
signer signing.Signer
|
|
|
|
config Config
|
|
|
|
dialer rpc.Dialer
|
|
|
|
db console.Users
|
|
|
|
passwordCost int
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewService returns a service for handling referrals information.
|
|
|
|
func NewService(log *zap.Logger, signer signing.Signer, config Config, dialer rpc.Dialer, db console.Users, passwordCost int) *Service {
|
|
|
|
return &Service{
|
|
|
|
log: log,
|
|
|
|
signer: signer,
|
|
|
|
config: config,
|
|
|
|
dialer: dialer,
|
|
|
|
db: db,
|
|
|
|
passwordCost: passwordCost,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetTokens returns tokens based on user ID.
|
|
|
|
func (service *Service) GetTokens(ctx context.Context, userID *uuid.UUID) (tokens []uuid.UUID, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
if userID.IsZero() {
|
|
|
|
return nil, errs.New("user ID is not defined")
|
|
|
|
}
|
|
|
|
|
|
|
|
conn, err := service.referralManagerConn(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
err = conn.Close()
|
|
|
|
}()
|
|
|
|
|
2020-03-25 12:15:27 +00:00
|
|
|
client := pb.NewDRPCReferralManagerClient(conn)
|
2019-11-25 21:36:36 +00:00
|
|
|
response, err := client.GetTokens(ctx, &pb.GetTokensRequest{
|
|
|
|
OwnerUserId: userID[:],
|
|
|
|
OwnerSatelliteId: service.signer.ID(),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
tokensInBytes := response.GetTokenSecrets()
|
|
|
|
if tokensInBytes != nil && len(tokensInBytes) == 0 {
|
|
|
|
return nil, errs.New("no available tokens")
|
|
|
|
}
|
|
|
|
|
|
|
|
tokens = make([]uuid.UUID, len(tokensInBytes))
|
|
|
|
for i := range tokensInBytes {
|
2019-12-16 17:59:01 +00:00
|
|
|
token, err := dbutil.BytesToUUID(tokensInBytes[i])
|
2019-11-25 21:36:36 +00:00
|
|
|
if err != nil {
|
|
|
|
service.log.Debug("failed to convert bytes to UUID", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
tokens[i] = token
|
|
|
|
}
|
|
|
|
|
|
|
|
return tokens, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateUser validates user's registration information and creates a new user.
|
|
|
|
func (service *Service) CreateUser(ctx context.Context, user CreateUser) (_ *console.User, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
if err := user.IsValid(); err != nil {
|
|
|
|
return nil, ErrValidation.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(user.ReferralToken) == 0 {
|
|
|
|
return nil, errs.New("referral token is not defined")
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = service.db.GetByEmail(ctx, user.Email)
|
|
|
|
if err == nil {
|
|
|
|
return nil, ErrUsedEmail.New("")
|
|
|
|
}
|
|
|
|
|
|
|
|
userID, err := uuid.New()
|
|
|
|
if err != nil {
|
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = service.redeemToken(ctx, userID, user.ReferralToken)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), service.passwordCost)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
newUser := &console.User{
|
|
|
|
ID: *userID,
|
|
|
|
Email: user.Email,
|
|
|
|
FullName: user.FullName,
|
|
|
|
ShortName: user.ShortName,
|
|
|
|
PasswordHash: hash,
|
|
|
|
}
|
|
|
|
|
|
|
|
u, err := service.db.Insert(ctx,
|
|
|
|
newUser,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return u, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (service *Service) redeemToken(ctx context.Context, userID *uuid.UUID, token string) error {
|
|
|
|
conn, err := service.referralManagerConn(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return errs.Wrap(err)
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
err = conn.Close()
|
|
|
|
}()
|
|
|
|
|
|
|
|
if userID.IsZero() || len(token) == 0 {
|
|
|
|
return errs.New("invalid argument")
|
|
|
|
}
|
|
|
|
|
|
|
|
referralToken, err := uuid.Parse(token)
|
|
|
|
if err != nil {
|
|
|
|
return errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2020-03-25 12:15:27 +00:00
|
|
|
client := pb.NewDRPCReferralManagerClient(conn)
|
2019-11-25 21:36:36 +00:00
|
|
|
_, err = client.RedeemToken(ctx, &pb.RedeemTokenRequest{
|
|
|
|
Token: referralToken[:],
|
|
|
|
RedeemUserId: userID[:],
|
|
|
|
RedeemSatelliteId: service.signer.ID(),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return errs.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (service *Service) referralManagerConn(ctx context.Context) (*rpc.Conn, error) {
|
|
|
|
if service.config.ReferralManagerURL.IsZero() {
|
|
|
|
return nil, errs.New("missing referral manager url configuration")
|
|
|
|
}
|
|
|
|
|
|
|
|
return service.dialer.DialAddressID(ctx, service.config.ReferralManagerURL.Address, service.config.ReferralManagerURL.ID)
|
|
|
|
}
|