2018-11-15 12:00:08 +00:00
|
|
|
// Copyright (C) 2018 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
2018-11-14 10:50:15 +00:00
|
|
|
package satellite
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto/sha256"
|
|
|
|
"crypto/subtle"
|
|
|
|
"time"
|
|
|
|
|
2018-11-21 15:51:43 +00:00
|
|
|
"go.uber.org/zap"
|
|
|
|
|
2018-11-14 10:50:15 +00:00
|
|
|
"github.com/skyrings/skyring-common/tools/uuid"
|
|
|
|
"github.com/zeebo/errs"
|
|
|
|
|
|
|
|
"storj.io/storj/pkg/auth"
|
|
|
|
"storj.io/storj/pkg/satellite/satelliteauth"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Service is handling accounts related logic
|
|
|
|
type Service struct {
|
|
|
|
Signer
|
|
|
|
|
|
|
|
store DB
|
2018-11-21 15:51:43 +00:00
|
|
|
log *zap.Logger
|
2018-11-14 10:50:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewService returns new instance of Service
|
2018-11-21 15:51:43 +00:00
|
|
|
func NewService(log *zap.Logger, signer Signer, store DB) (*Service, error) {
|
2018-11-14 10:50:15 +00:00
|
|
|
if signer == nil {
|
|
|
|
return nil, errs.New("signer can't be nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
if store == nil {
|
|
|
|
return nil, errs.New("store can't be nil")
|
|
|
|
}
|
|
|
|
|
2018-11-21 15:51:43 +00:00
|
|
|
if log == nil {
|
|
|
|
return nil, errs.New("log can't be nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Service{Signer: signer, store: store, log: log}, nil
|
2018-11-14 10:50:15 +00:00
|
|
|
}
|
|
|
|
|
2018-11-21 15:51:43 +00:00
|
|
|
// CreateUser gets password hash value and creates new user
|
|
|
|
func (s *Service) CreateUser(ctx context.Context, userInfo UserInfo, companyInfo CompanyInfo) (*User, error) {
|
|
|
|
passwordHash := sha256.Sum256([]byte(userInfo.Password))
|
|
|
|
|
|
|
|
user, err := s.store.Users().Insert(ctx, &User{
|
|
|
|
Email: userInfo.Email,
|
|
|
|
FirstName: userInfo.FirstName,
|
|
|
|
LastName: userInfo.LastName,
|
|
|
|
PasswordHash: passwordHash[:],
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = s.store.Companies().Insert(ctx, &Company{
|
|
|
|
UserID: user.ID,
|
|
|
|
Name: companyInfo.Name,
|
|
|
|
Address: companyInfo.Address,
|
|
|
|
Country: companyInfo.Country,
|
|
|
|
City: companyInfo.City,
|
|
|
|
State: companyInfo.State,
|
|
|
|
PostalCode: companyInfo.PostalCode,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
s.log.Error(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return user, nil
|
|
|
|
}
|
2018-11-14 10:50:15 +00:00
|
|
|
|
2018-11-21 15:51:43 +00:00
|
|
|
// CreateCompany creates Company for authorized User
|
|
|
|
func (s *Service) CreateCompany(ctx context.Context, info CompanyInfo) (*Company, error) {
|
|
|
|
user, err := s.Authorize(ctx)
|
2018-11-14 10:50:15 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-11-21 15:51:43 +00:00
|
|
|
return s.store.Companies().Insert(ctx, &Company{
|
|
|
|
UserID: user.ID,
|
|
|
|
Name: info.Name,
|
|
|
|
Address: info.Address,
|
|
|
|
Country: info.Country,
|
|
|
|
City: info.City,
|
|
|
|
State: info.State,
|
|
|
|
PostalCode: info.PostalCode,
|
|
|
|
})
|
2018-11-14 10:50:15 +00:00
|
|
|
}
|
|
|
|
|
2018-11-21 15:51:43 +00:00
|
|
|
// Token authenticates user by credentials and returns auth token
|
|
|
|
func (s *Service) Token(ctx context.Context, email, password string) (string, error) {
|
2018-11-14 10:50:15 +00:00
|
|
|
passwordHash := sha256.Sum256([]byte(password))
|
|
|
|
|
|
|
|
user, err := s.store.Users().GetByCredentials(ctx, passwordHash[:], email)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
//TODO: move expiration time to constants
|
|
|
|
claims := satelliteauth.Claims{
|
|
|
|
ID: user.ID,
|
|
|
|
Expiration: time.Now().Add(time.Minute * 15),
|
|
|
|
}
|
|
|
|
|
|
|
|
token, err := s.createToken(&claims)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return token, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetUser returns user by id
|
|
|
|
func (s *Service) GetUser(ctx context.Context, id uuid.UUID) (*User, error) {
|
2018-11-21 15:51:43 +00:00
|
|
|
_, err := s.Authorize(ctx)
|
2018-11-14 10:50:15 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-11-21 15:51:43 +00:00
|
|
|
return s.store.Users().Get(ctx, id)
|
|
|
|
}
|
2018-11-14 10:50:15 +00:00
|
|
|
|
2018-11-21 15:51:43 +00:00
|
|
|
// GetCompany returns company by userID
|
|
|
|
func (s *Service) GetCompany(ctx context.Context, userID uuid.UUID) (*Company, error) {
|
|
|
|
_, err := s.Authorize(ctx)
|
2018-11-14 10:50:15 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-11-21 15:51:43 +00:00
|
|
|
return s.store.Companies().GetByUserID(ctx, userID)
|
2018-11-14 10:50:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Service) createToken(claims *satelliteauth.Claims) (string, error) {
|
|
|
|
json, err := claims.JSON()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
token := satelliteauth.Token{Payload: json}
|
|
|
|
err = signToken(&token, s.Signer)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return token.String(), nil
|
|
|
|
}
|
|
|
|
|
2018-11-21 15:51:43 +00:00
|
|
|
// Authorize validates token from context and returns authenticated and authorized User
|
|
|
|
func (s *Service) Authorize(ctx context.Context) (*User, error) {
|
|
|
|
token, ok := auth.GetAPIKey(ctx)
|
|
|
|
if !ok {
|
|
|
|
return nil, errs.New("no api key was provided")
|
|
|
|
}
|
|
|
|
|
|
|
|
claims, err := s.authenticate(string(token))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.authorize(ctx, claims)
|
|
|
|
}
|
|
|
|
|
|
|
|
// authenticate validates toke signature and returns authenticated *satelliteauth.Claims
|
2018-11-14 10:50:15 +00:00
|
|
|
func (s *Service) authenticate(tokenS string) (*satelliteauth.Claims, error) {
|
|
|
|
token, err := satelliteauth.FromBase64URLString(tokenS)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
signature := token.Signature
|
|
|
|
|
|
|
|
err = signToken(&token, s.Signer)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if subtle.ConstantTimeCompare(signature, token.Signature) != 1 {
|
|
|
|
return nil, errs.New("incorrect signature")
|
|
|
|
}
|
|
|
|
|
|
|
|
claims, err := satelliteauth.FromJSON(token.Payload)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return claims, nil
|
|
|
|
}
|
|
|
|
|
2018-11-21 15:51:43 +00:00
|
|
|
// authorize checks claims and returns authorized User
|
|
|
|
func (s *Service) authorize(ctx context.Context, claims *satelliteauth.Claims) (*User, error) {
|
2018-11-14 10:50:15 +00:00
|
|
|
if !claims.Expiration.IsZero() && claims.Expiration.Before(time.Now()) {
|
2018-11-21 15:51:43 +00:00
|
|
|
return nil, errs.New("token is outdated")
|
2018-11-14 10:50:15 +00:00
|
|
|
}
|
|
|
|
|
2018-11-21 15:51:43 +00:00
|
|
|
user, err := s.store.Users().Get(ctx, claims.ID)
|
2018-11-14 10:50:15 +00:00
|
|
|
if err != nil {
|
2018-11-21 15:51:43 +00:00
|
|
|
return nil, errs.New("authorization failed. no user with id: %s", claims.ID.String())
|
2018-11-14 10:50:15 +00:00
|
|
|
}
|
|
|
|
|
2018-11-21 15:51:43 +00:00
|
|
|
return user, nil
|
2018-11-14 10:50:15 +00:00
|
|
|
}
|