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 {
|
2020-08-03 22:17:37 +00:00
|
|
|
service *Service
|
|
|
|
authSvc influxdb.AuthorizationService
|
|
|
|
alwaysAllow bool
|
2020-04-06 21:58:15 +00:00
|
|
|
}
|
|
|
|
|
2020-08-03 22:17:37 +00:00
|
|
|
type OnboardServiceOptionFn func(*OnboardService)
|
|
|
|
|
|
|
|
// WithAlwaysAllowInitialUser configures the OnboardService to
|
|
|
|
// always return true for IsOnboarding to allow multiple
|
|
|
|
// initial onboard requests.
|
|
|
|
func WithAlwaysAllowInitialUser() OnboardServiceOptionFn {
|
|
|
|
return func(s *OnboardService) {
|
|
|
|
s.alwaysAllow = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewOnboardService(svc *Service, as influxdb.AuthorizationService, opts ...OnboardServiceOptionFn) influxdb.OnboardingService {
|
|
|
|
s := &OnboardService{
|
2020-08-03 15:33:46 +00:00
|
|
|
service: svc,
|
2020-04-06 21:58:15 +00:00
|
|
|
authSvc: as,
|
|
|
|
}
|
2020-08-03 22:17:37 +00:00
|
|
|
|
|
|
|
for _, opt := range opts {
|
|
|
|
opt(s)
|
|
|
|
}
|
|
|
|
|
|
|
|
return s
|
2020-04-06 21:58:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// IsOnboarding determine if onboarding request is allowed.
|
|
|
|
func (s *OnboardService) IsOnboarding(ctx context.Context) (bool, error) {
|
2020-08-03 22:17:37 +00:00
|
|
|
if s.alwaysAllow {
|
|
|
|
return true, nil
|
|
|
|
}
|
2020-08-26 17:28:35 +00:00
|
|
|
|
2020-04-06 21:58:15 +00:00
|
|
|
allowed := false
|
2020-08-03 15:33:46 +00:00
|
|
|
err := s.service.store.View(ctx, func(tx kv.Tx) error {
|
2020-04-06 21:58:15 +00:00
|
|
|
// we are allowed to onboard a user if we have no users or orgs
|
2020-08-03 15:33:46 +00:00
|
|
|
users, _ := s.service.store.ListUsers(ctx, tx, influxdb.FindOptions{Limit: 1})
|
|
|
|
orgs, _ := s.service.store.ListOrgs(ctx, tx, influxdb.FindOptions{Limit: 1})
|
2020-04-06 21:58:15 +00:00
|
|
|
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) {
|
2020-08-05 18:42:17 +00:00
|
|
|
if req == nil || req.User == "" || req.Org == "" || req.Bucket == "" {
|
2020-04-06 21:58:15 +00:00
|
|
|
return nil, ErrOnboardInvalid
|
|
|
|
}
|
|
|
|
|
|
|
|
result := &influxdb.OnboardingResults{}
|
|
|
|
|
2020-08-03 15:33:46 +00:00
|
|
|
// create a user
|
|
|
|
user := &influxdb.User{
|
|
|
|
Name: req.User,
|
|
|
|
Status: influxdb.Active,
|
|
|
|
}
|
2020-04-06 21:58:15 +00:00
|
|
|
|
2020-08-03 15:33:46 +00:00
|
|
|
if err := s.service.CreateUser(ctx, user); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-04-06 21:58:15 +00:00
|
|
|
|
2020-08-03 15:33:46 +00:00
|
|
|
// create users password
|
|
|
|
if req.Password != "" {
|
|
|
|
s.service.SetPassword(ctx, user.ID, req.Password)
|
|
|
|
}
|
2020-04-06 21:58:15 +00:00
|
|
|
|
2020-08-03 15:33:46 +00:00
|
|
|
// create users org
|
|
|
|
org := &influxdb.Organization{
|
|
|
|
Name: req.Org,
|
|
|
|
}
|
2020-04-06 21:58:15 +00:00
|
|
|
|
2020-08-03 15:33:46 +00:00
|
|
|
if err := s.service.CreateOrganization(ctx, org); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-04-06 21:58:15 +00:00
|
|
|
|
2020-08-03 15:33:46 +00:00
|
|
|
// create urm
|
|
|
|
err := s.service.CreateUserResourceMapping(ctx, &influxdb.UserResourceMapping{
|
|
|
|
UserID: user.ID,
|
|
|
|
UserType: influxdb.Owner,
|
|
|
|
MappingType: influxdb.UserMappingType,
|
|
|
|
ResourceType: influxdb.OrgsResourceType,
|
|
|
|
ResourceID: org.ID,
|
2020-04-06 21:58:15 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
2020-08-03 15:33:46 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// create orgs buckets
|
|
|
|
ub := &influxdb.Bucket{
|
|
|
|
OrgID: org.ID,
|
|
|
|
Name: req.Bucket,
|
|
|
|
Type: influxdb.BucketTypeUser,
|
|
|
|
RetentionPeriod: time.Duration(req.RetentionPeriod) * time.Hour,
|
2020-04-06 21:58:15 +00:00
|
|
|
}
|
|
|
|
|
2020-08-03 15:33:46 +00:00
|
|
|
if err := s.service.CreateBucket(ctx, ub); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
result.User = user
|
|
|
|
result.Org = org
|
|
|
|
result.Bucket = ub
|
|
|
|
|
2020-04-06 21:58:15 +00:00
|
|
|
// 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)
|
|
|
|
}
|