github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/auth/setup/setup.go (about)

     1  package setup
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/treeverse/lakefs/pkg/auth"
     9  	"github.com/treeverse/lakefs/pkg/auth/acl"
    10  	"github.com/treeverse/lakefs/pkg/auth/model"
    11  	"github.com/treeverse/lakefs/pkg/config"
    12  	"github.com/treeverse/lakefs/pkg/logging"
    13  	"github.com/treeverse/lakefs/pkg/permissions"
    14  )
    15  
    16  const (
    17  	AdminsGroup     = "Admins"
    18  	SuperUsersGroup = "SuperUsers"
    19  	DevelopersGroup = "Developers"
    20  	ViewersGroup    = "Viewers"
    21  )
    22  
    23  func createGroups(ctx context.Context, authService auth.Service, groups []*model.Group) error {
    24  	for _, group := range groups {
    25  		_, err := authService.CreateGroup(ctx, group)
    26  		if err != nil {
    27  			return err
    28  		}
    29  	}
    30  	return nil
    31  }
    32  
    33  func createPolicies(ctx context.Context, authService auth.Service, policies []*model.Policy) error {
    34  	for _, policy := range policies {
    35  		err := authService.WritePolicy(ctx, policy, false)
    36  		if err != nil {
    37  			return err
    38  		}
    39  	}
    40  	return nil
    41  }
    42  
    43  func CreateRBACBasePolicies(ctx context.Context, authService auth.Service, ts time.Time) error {
    44  	all := []string{permissions.All}
    45  
    46  	return createPolicies(ctx, authService, []*model.Policy{
    47  		{
    48  			CreatedAt:   ts,
    49  			DisplayName: "FSFullAccess",
    50  			Statement:   auth.MakeStatementForPolicyTypeOrDie("FSFullAccess", all),
    51  		},
    52  		{
    53  			CreatedAt:   ts,
    54  			DisplayName: "FSReadWriteAll",
    55  			Statement:   auth.MakeStatementForPolicyTypeOrDie("FSReadWrite", all),
    56  		},
    57  		{
    58  			CreatedAt:   ts,
    59  			DisplayName: "FSReadAll",
    60  			Statement:   auth.MakeStatementForPolicyTypeOrDie("FSRead", all),
    61  		},
    62  		{
    63  			CreatedAt:   ts,
    64  			DisplayName: "RepoManagementFullAccess",
    65  			Statement: model.Statements{
    66  				{
    67  					Action: []string{
    68  						"ci:*",
    69  						"retention:*",
    70  						"branches:*",
    71  						"fs:ReadConfig",
    72  					},
    73  					Resource: permissions.All,
    74  					Effect:   model.StatementEffectAllow,
    75  				},
    76  			},
    77  		},
    78  		{
    79  			CreatedAt:   ts,
    80  			DisplayName: "RepoManagementReadAll",
    81  			Statement:   auth.MakeStatementForPolicyTypeOrDie("RepoManagementRead", all),
    82  		},
    83  		{
    84  			CreatedAt:   ts,
    85  			DisplayName: "AuthFullAccess",
    86  			Statement: model.Statements{
    87  				{
    88  					Action: []string{
    89  						"auth:*",
    90  					},
    91  					Resource: permissions.All,
    92  					Effect:   model.StatementEffectAllow,
    93  				},
    94  			},
    95  		},
    96  		{
    97  			CreatedAt:   ts,
    98  			DisplayName: "AuthManageOwnCredentials",
    99  			Statement: auth.MakeStatementForPolicyTypeOrDie(
   100  				"AuthManageOwnCredentials",
   101  				[]string{permissions.UserArn("${user}")},
   102  			),
   103  		},
   104  	})
   105  }
   106  
   107  func attachPolicies(ctx context.Context, authService auth.Service, groupID string, policyIDs []string) error {
   108  	for _, policyID := range policyIDs {
   109  		err := authService.AttachPolicyToGroup(ctx, policyID, groupID)
   110  		if err != nil {
   111  			return err
   112  		}
   113  	}
   114  	return nil
   115  }
   116  
   117  func CreateRBACBaseGroups(ctx context.Context, authService auth.Service, ts time.Time) error {
   118  	err := createGroups(ctx, authService, []*model.Group{
   119  		{CreatedAt: ts, DisplayName: AdminsGroup},
   120  		{CreatedAt: ts, DisplayName: SuperUsersGroup},
   121  		{CreatedAt: ts, DisplayName: DevelopersGroup},
   122  		{CreatedAt: ts, DisplayName: ViewersGroup},
   123  	})
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	err = CreateRBACBasePolicies(ctx, authService, ts)
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	err = attachPolicies(ctx, authService, "Admins", []string{"FSFullAccess", "AuthFullAccess", "RepoManagementFullAccess"})
   134  	if err != nil {
   135  		return err
   136  	}
   137  	err = attachPolicies(ctx, authService, "SuperUsers", []string{"FSFullAccess", "AuthManageOwnCredentials", "RepoManagementReadAll"})
   138  	if err != nil {
   139  		return err
   140  	}
   141  	err = attachPolicies(ctx, authService, "Developers", []string{"FSReadWriteAll", "AuthManageOwnCredentials", "RepoManagementReadAll"})
   142  	if err != nil {
   143  		return err
   144  	}
   145  	err = attachPolicies(ctx, authService, "Viewers", []string{"FSReadAll", "AuthManageOwnCredentials"})
   146  	if err != nil {
   147  		return err
   148  	}
   149  
   150  	return nil
   151  }
   152  
   153  func CreateACLBaseGroups(ctx context.Context, authService auth.Service, ts time.Time) error {
   154  	if _, err := authService.CreateGroup(ctx, &model.Group{CreatedAt: ts, DisplayName: acl.AdminsGroup}); err != nil {
   155  		return fmt.Errorf("setup: create base ACL group %s: %w", acl.AdminsGroup, err)
   156  	}
   157  	if err := acl.WriteGroupACL(ctx, authService, acl.AdminsGroup, model.ACL{Permission: acl.AdminPermission}, ts, false); err != nil {
   158  		return fmt.Errorf("setup: %w", err)
   159  	}
   160  
   161  	if _, err := authService.CreateGroup(ctx, &model.Group{CreatedAt: ts, DisplayName: acl.SupersGroup}); err != nil {
   162  		return fmt.Errorf("setup: create base ACL group %s: %w", acl.SupersGroup, err)
   163  	}
   164  	if err := acl.WriteGroupACL(ctx, authService, acl.SupersGroup, model.ACL{Permission: acl.SuperPermission}, ts, false); err != nil {
   165  		return fmt.Errorf("setup: %w", err)
   166  	}
   167  
   168  	if _, err := authService.CreateGroup(ctx, &model.Group{CreatedAt: ts, DisplayName: acl.WritersGroup}); err != nil {
   169  		return fmt.Errorf("setup: create base ACL group %s: %w", acl.WritersGroup, err)
   170  	}
   171  	if err := acl.WriteGroupACL(ctx, authService, acl.WritersGroup, model.ACL{Permission: acl.WritePermission}, ts, false); err != nil {
   172  		return fmt.Errorf("setup: %w", err)
   173  	}
   174  
   175  	if _, err := authService.CreateGroup(ctx, &model.Group{CreatedAt: ts, DisplayName: acl.ReadersGroup}); err != nil {
   176  		return fmt.Errorf("create base ACL group %s: %w", acl.ReadersGroup, err)
   177  	}
   178  	if err := acl.WriteGroupACL(ctx, authService, acl.ReadersGroup, model.ACL{Permission: acl.ReadPermission}, ts, false); err != nil {
   179  		return fmt.Errorf("setup: %w", err)
   180  	}
   181  
   182  	return nil
   183  }
   184  
   185  // CreateAdminUser setup base groups, policies and create admin user
   186  func CreateAdminUser(ctx context.Context, authService auth.Service, cfg *config.Config, superuser *model.SuperuserConfiguration) (*model.Credential, error) {
   187  	// Set up the basic groups and policies
   188  	now := time.Now()
   189  	err := CreateBaseGroups(ctx, authService, cfg, now)
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  
   194  	return AddAdminUser(ctx, authService, superuser)
   195  }
   196  
   197  func AddAdminUser(ctx context.Context, authService auth.Service, user *model.SuperuserConfiguration) (*model.Credential, error) {
   198  	// verify the admin group exists
   199  	_, err := authService.GetGroup(ctx, AdminsGroup)
   200  	if err != nil {
   201  		return nil, fmt.Errorf("admin group - %w", err)
   202  	}
   203  
   204  	// create admin user
   205  	user.Source = "internal"
   206  	_, err = authService.CreateUser(ctx, &user.User)
   207  	if err != nil {
   208  		return nil, fmt.Errorf("create user - %w", err)
   209  	}
   210  	err = authService.AddUserToGroup(ctx, user.Username, AdminsGroup)
   211  	if err != nil {
   212  		return nil, fmt.Errorf("add user to group - %w", err)
   213  	}
   214  
   215  	var creds *model.Credential
   216  	if user.AccessKeyID == "" {
   217  		// Generate and return a key pair
   218  		creds, err = authService.CreateCredentials(ctx, user.Username)
   219  		if err != nil {
   220  			return nil, fmt.Errorf("create credentials for %s: %w", user.Username, err)
   221  		}
   222  	} else {
   223  		creds, err = authService.AddCredentials(ctx, user.Username, user.AccessKeyID, user.SecretAccessKey)
   224  		if err != nil {
   225  			return nil, fmt.Errorf("add credentials for %s: %w", user.Username, err)
   226  		}
   227  	}
   228  	return creds, nil
   229  }
   230  
   231  func CreateInitialAdminUser(ctx context.Context, authService auth.Service, cfg *config.Config, metadataManger auth.MetadataManager, username string) (*model.Credential, error) {
   232  	return CreateInitialAdminUserWithKeys(ctx, authService, cfg, metadataManger, username, nil, nil)
   233  }
   234  
   235  func CreateInitialAdminUserWithKeys(ctx context.Context, authService auth.Service, cfg *config.Config, metadataManger auth.MetadataManager, username string, accessKeyID *string, secretAccessKey *string) (*model.Credential, error) {
   236  	adminUser := &model.SuperuserConfiguration{
   237  		User: model.User{
   238  			CreatedAt: time.Now(),
   239  			Username:  username,
   240  		},
   241  	}
   242  	if accessKeyID != nil && secretAccessKey != nil {
   243  		adminUser.AccessKeyID = *accessKeyID
   244  		adminUser.SecretAccessKey = *secretAccessKey
   245  	}
   246  
   247  	// create first admin user
   248  	cred, err := CreateAdminUser(ctx, authService, cfg, adminUser)
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  
   253  	// update setup timestamp
   254  	if err := metadataManger.UpdateSetupTimestamp(ctx, time.Now()); err != nil {
   255  		logging.ContextUnavailable().WithError(err).Error("Failed the update setup timestamp")
   256  	}
   257  	return cred, err
   258  }
   259  
   260  func CreateBaseGroups(ctx context.Context, authService auth.Service, cfg *config.Config, ts time.Time) error {
   261  	if cfg.IsAuthUISimplified() {
   262  		return CreateACLBaseGroups(ctx, authService, ts)
   263  	}
   264  	return CreateRBACBaseGroups(ctx, authService, ts)
   265  }