mirror of
https://github.com/ferdzo/fs.git
synced 2026-04-05 14:26:26 +00:00
Compare commits
2 Commits
f57c7b8390
...
cfb9b591ac
| Author | SHA1 | Date | |
|---|---|---|---|
| cfb9b591ac | |||
| b27f1186cf |
56
README.md
56
README.md
@@ -49,6 +49,61 @@ Admin API (JSON):
|
||||
- `PUT /_admin/v1/users/{accessKeyId}/status`
|
||||
- `DELETE /_admin/v1/users/{accessKeyId}`
|
||||
|
||||
Admin API policy examples (SigV4):
|
||||
```bash
|
||||
ENDPOINT="http://localhost:3000"
|
||||
REGION="us-east-1"
|
||||
ADMIN_ACCESS_KEY="${FS_ROOT_USER}"
|
||||
ADMIN_SECRET_KEY="${FS_ROOT_PASSWORD}"
|
||||
SIGV4="aws:amz:${REGION}:s3"
|
||||
```
|
||||
|
||||
Replace user policy with one scoped statement:
|
||||
```bash
|
||||
curl --aws-sigv4 "$SIGV4" \
|
||||
--user "${ADMIN_ACCESS_KEY}:${ADMIN_SECRET_KEY}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-X PUT "${ENDPOINT}/_admin/v1/users/test-user/policy" \
|
||||
-d '{
|
||||
"policy": {
|
||||
"statements": [
|
||||
{
|
||||
"effect": "allow",
|
||||
"actions": ["s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
|
||||
"bucket": "backup",
|
||||
"prefix": "restic/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
Set multiple statements (for multiple buckets):
|
||||
```bash
|
||||
curl --aws-sigv4 "$SIGV4" \
|
||||
--user "${ADMIN_ACCESS_KEY}:${ADMIN_SECRET_KEY}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-X PUT "${ENDPOINT}/_admin/v1/users/test-user/policy" \
|
||||
-d '{
|
||||
"policy": {
|
||||
"statements": [
|
||||
{
|
||||
"effect": "allow",
|
||||
"actions": ["s3:ListBucket", "s3:GetObject"],
|
||||
"bucket": "test-bucket",
|
||||
"prefix": "*"
|
||||
},
|
||||
{
|
||||
"effect": "allow",
|
||||
"actions": ["s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
|
||||
"bucket": "test-bucket-2",
|
||||
"prefix": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
Admin CLI:
|
||||
- `fs admin user create --access-key backup-user --role readwrite`
|
||||
- `fs admin user list`
|
||||
@@ -56,6 +111,7 @@ Admin CLI:
|
||||
- `fs admin user set-status backup-user --status disabled`
|
||||
- `fs admin user set-role backup-user --role readonly --bucket backup-bucket --prefix restic/`
|
||||
- `fs admin user set-role backup-user --role readwrite --bucket backups-2` (appends another statement)
|
||||
- `fs admin user remove-role backup-user --role readonly --bucket backup-bucket --prefix restic/`
|
||||
- `fs admin user set-role backup-user --role admin --replace` (replaces all statements)
|
||||
- `fs admin user delete backup-user`
|
||||
- `fs admin diag health`
|
||||
|
||||
@@ -22,6 +22,7 @@ func newAdminUserCommand(opts *adminOptions) *cobra.Command {
|
||||
cmd.AddCommand(newAdminUserDeleteCommand(opts))
|
||||
cmd.AddCommand(newAdminUserSetStatusCommand(opts))
|
||||
cmd.AddCommand(newAdminUserSetRoleCommand(opts))
|
||||
cmd.AddCommand(newAdminUserRemoveRoleCommand(opts))
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -253,6 +254,68 @@ func newAdminUserSetRoleCommand(opts *adminOptions) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newAdminUserRemoveRoleCommand(opts *adminOptions) *cobra.Command {
|
||||
var (
|
||||
role string
|
||||
bucket string
|
||||
prefix string
|
||||
)
|
||||
cmd := &cobra.Command{
|
||||
Use: "remove-role <access-key-id>",
|
||||
Short: "Remove one role policy statement from user",
|
||||
Args: requireAccessKeyArg("fs admin user remove-role <access-key-id> --role admin|readwrite|readonly [--bucket <name>] [--prefix <path>]"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
policy, err := buildPolicyFromRole(rolePolicyOptions{
|
||||
Role: role,
|
||||
Bucket: bucket,
|
||||
Prefix: prefix,
|
||||
})
|
||||
if err != nil {
|
||||
return usageError("fs admin user remove-role <access-key-id> --role admin|readwrite|readonly [--bucket <name>] [--prefix <path>]", err.Error())
|
||||
}
|
||||
if len(policy.Statements) == 0 {
|
||||
return usageError("fs admin user remove-role <access-key-id> --role admin|readwrite|readonly [--bucket <name>] [--prefix <path>]", "no statement to remove")
|
||||
}
|
||||
|
||||
client, err := newAdminAPIClient(opts, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existing, err := client.GetUser(context.Background(), args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existing.Policy == nil || len(existing.Policy.Statements) == 0 {
|
||||
return fmt.Errorf("user %q has no policy statements", args[0])
|
||||
}
|
||||
|
||||
target := policy.Statements[0]
|
||||
nextPolicy, removed := removePolicyStatements(existing.Policy, target)
|
||||
if removed == 0 {
|
||||
return fmt.Errorf("no matching statement found for role=%s bucket=%s prefix=%s", role, bucket, prefix)
|
||||
}
|
||||
if len(nextPolicy.Statements) == 0 {
|
||||
return fmt.Errorf("cannot remove the last policy statement; add another role first or use set-role --replace")
|
||||
}
|
||||
|
||||
out, err := client.SetUserPolicy(context.Background(), args[0], nextPolicy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if opts.JSON {
|
||||
return writeJSON(cmd.OutOrStdout(), out)
|
||||
}
|
||||
return writeUserTable(cmd.OutOrStdout(), out, false)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&role, "role", "readwrite", "Role: admin|readwrite|readonly")
|
||||
cmd.Flags().StringVar(&bucket, "bucket", "*", "Bucket scope, defaults to *")
|
||||
cmd.Flags().StringVar(&prefix, "prefix", "*", "Prefix scope, defaults to *")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func mergePolicyStatements(existing *adminPolicy, addition adminPolicy) adminPolicy {
|
||||
merged := adminPolicy{}
|
||||
if existing != nil {
|
||||
@@ -289,6 +352,25 @@ func policyStatementsEqual(a, b adminPolicyStatement) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func removePolicyStatements(existing *adminPolicy, target adminPolicyStatement) (adminPolicy, int) {
|
||||
out := adminPolicy{}
|
||||
if existing == nil {
|
||||
return out, 0
|
||||
}
|
||||
out.Principal = existing.Principal
|
||||
out.Statements = make([]adminPolicyStatement, 0, len(existing.Statements))
|
||||
|
||||
removed := 0
|
||||
for _, stmt := range existing.Statements {
|
||||
if policyStatementsEqual(stmt, target) {
|
||||
removed++
|
||||
continue
|
||||
}
|
||||
out.Statements = append(out.Statements, stmt)
|
||||
}
|
||||
return out, removed
|
||||
}
|
||||
|
||||
func requireAccessKeyArg(usage string) cobra.PositionalArgs {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
|
||||
Reference in New Issue
Block a user