2020-04-06 21:58:15 +00:00
|
|
|
package tenant
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/influxdata/influxdb/v2"
|
|
|
|
"github.com/influxdata/influxdb/v2/kv"
|
|
|
|
)
|
|
|
|
|
|
|
|
type OnboardService struct {
|
|
|
|
store *Store
|
|
|
|
authSvc influxdb.AuthorizationService
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewOnboardService(st *Store, as influxdb.AuthorizationService) influxdb.OnboardingService {
|
|
|
|
return &OnboardService{
|
|
|
|
store: st,
|
|
|
|
authSvc: as,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsOnboarding determine if onboarding request is allowed.
|
|
|
|
func (s *OnboardService) IsOnboarding(ctx context.Context) (bool, error) {
|
|
|
|
allowed := false
|
|
|
|
err := s.store.View(ctx, func(tx kv.Tx) error {
|
|
|
|
// we are allowed to onboard a user if we have no users or orgs
|
|
|
|
users, _ := s.store.ListUsers(ctx, tx, influxdb.FindOptions{Limit: 1})
|
|
|
|
orgs, _ := s.store.ListOrgs(ctx, tx, influxdb.FindOptions{Limit: 1})
|
|
|
|
if len(users) == 0 && len(orgs) == 0 {
|
|
|
|
allowed = true
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return allowed, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// OnboardInitialUser allows us to onboard a new user if is onboarding is allowd
|
|
|
|
func (s *OnboardService) OnboardInitialUser(ctx context.Context, req *influxdb.OnboardingRequest) (*influxdb.OnboardingResults, error) {
|
|
|
|
allowed, err := s.IsOnboarding(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !allowed {
|
|
|
|
return nil, ErrOnboardingNotAllowed
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.onboardUser(ctx, req, func(influxdb.ID) []influxdb.Permission { return influxdb.OperPermissions() })
|
|
|
|
}
|
|
|
|
|
2020-05-12 17:14:09 +00:00
|
|
|
// OnboardUser allows us to onboard a new user if is onboarding is allowed
|
2020-04-06 21:58:15 +00:00
|
|
|
func (s *OnboardService) OnboardUser(ctx context.Context, req *influxdb.OnboardingRequest) (*influxdb.OnboardingResults, error) {
|
|
|
|
return s.onboardUser(ctx, req, influxdb.OwnerPermissions)
|
|
|
|
}
|
|
|
|
|
|
|
|
// onboardUser allows us to onboard new users.
|
|
|
|
func (s *OnboardService) onboardUser(ctx context.Context, req *influxdb.OnboardingRequest, permFn func(orgID influxdb.ID) []influxdb.Permission) (*influxdb.OnboardingResults, error) {
|
|
|
|
if req == nil || req.User == "" || req.Password == "" || req.Org == "" || req.Bucket == "" {
|
|
|
|
return nil, ErrOnboardInvalid
|
|
|
|
}
|
|
|
|
|
|
|
|
result := &influxdb.OnboardingResults{}
|
|
|
|
|
|
|
|
err := s.store.Update(ctx, func(tx kv.Tx) error {
|
|
|
|
// create a user
|
|
|
|
user := &influxdb.User{
|
|
|
|
Name: req.User,
|
|
|
|
Status: influxdb.Active,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := s.store.CreateUser(ctx, tx, user); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// create users password
|
|
|
|
if req.Password != "" {
|
|
|
|
passHash, err := encryptPassword(req.Password)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
s.store.SetPassword(ctx, tx, user.ID, passHash)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create users org
|
|
|
|
org := &influxdb.Organization{
|
|
|
|
Name: req.Org,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := s.store.CreateOrg(ctx, tx, org); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// create urm
|
|
|
|
err := s.store.CreateURM(ctx, tx, &influxdb.UserResourceMapping{
|
|
|
|
UserID: user.ID,
|
|
|
|
UserType: influxdb.Owner,
|
|
|
|
MappingType: influxdb.UserMappingType,
|
|
|
|
ResourceType: influxdb.OrgsResourceType,
|
|
|
|
ResourceID: org.ID,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// create orgs buckets
|
|
|
|
ub := &influxdb.Bucket{
|
|
|
|
OrgID: org.ID,
|
|
|
|
Name: req.Bucket,
|
|
|
|
Type: influxdb.BucketTypeUser,
|
|
|
|
RetentionPeriod: time.Duration(req.RetentionPeriod) * time.Hour,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := s.store.CreateBucket(ctx, tx, ub); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
tb := &influxdb.Bucket{
|
|
|
|
OrgID: org.ID,
|
|
|
|
Type: influxdb.BucketTypeSystem,
|
|
|
|
Name: influxdb.TasksSystemBucketName,
|
|
|
|
RetentionPeriod: influxdb.TasksSystemBucketRetention,
|
|
|
|
Description: "System bucket for task logs",
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := s.store.CreateBucket(ctx, tx, tb); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
mb := &influxdb.Bucket{
|
|
|
|
OrgID: org.ID,
|
|
|
|
Type: influxdb.BucketTypeSystem,
|
|
|
|
Name: influxdb.MonitoringSystemBucketName,
|
|
|
|
RetentionPeriod: influxdb.MonitoringSystemBucketRetention,
|
|
|
|
Description: "System bucket for monitoring logs",
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := s.store.CreateBucket(ctx, tx, mb); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
result.User = user
|
|
|
|
result.Org = org
|
|
|
|
result.Bucket = ub
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// bolt doesn't lock per collection or record so we have to close our transaction
|
|
|
|
// before we can reach out to the auth service.
|
|
|
|
result.Auth = &influxdb.Authorization{
|
|
|
|
Description: fmt.Sprintf("%s's Token", req.User),
|
|
|
|
Permissions: permFn(result.Org.ID),
|
|
|
|
Token: req.Token,
|
|
|
|
UserID: result.User.ID,
|
|
|
|
OrgID: result.Org.ID,
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, s.authSvc.CreateAuthorization(ctx, result.Auth)
|
|
|
|
}
|