Skip to content

Add commands for manage users groups and access#339

Open
Jabejixo wants to merge 5 commits intomainfrom
add-user-group-access-managing
Open

Add commands for manage users groups and access#339
Jabejixo wants to merge 5 commits intomainfrom
add-user-group-access-managing

Conversation

@Jabejixo
Copy link
Copy Markdown
Contributor

@Jabejixo Jabejixo commented Apr 27, 2026

Description

New d8 iam command tree for managing local users, groups, and access grants. Five top-level subcommands under internal/iam/:

  • d8 iam usercreate / delete / reset-password / reset2fa / lock / unlock.
  • d8 iam groupcreate / delete / add-member / remove-member.
  • d8 iam accessgrant / revoke.
  • d8 iam getuser, group, rule.
  • d8 iam listusers|user, groups|group, rules|rule.

Read verbs are top-level (d8 iam get user alice, d8 iam list users), not per-domain wrappers.

Subjects are positional. grant / revoke take -n/--namespace (repeatable, AR) or --scope cluster|all-namespaces|labels=K=V[,K2=V2,...] (CAR with namespaceSelector.labelSelector). Capabilities (--allow-scale, --port-forwarding) compose with any scope. d8-managed grants get a deterministic name, so grant / revoke are idempotent.

Password input is unified across user create and user reset-password: interactive (default), --password-stdin, --generate-password, or --password-hash. The CLI handles the format difference between User.spec.password (base64-bcrypt) and UserOperation.spec.resetPassword.newPasswordHash (raw bcrypt).

Shell completion covers command names, resource names, namespaces, access levels, scope values, rule refs, and output formats. Common k8s helpers (PrintObject, NewDynamicClient, AddOutputFlag, CompleteResourceNames, ...) live in internal/utilk8s/. The previously top-level d8 user was moved under d8 iam user.

Why do we need it, and what problem does it solve?

Managing users, groups, and access in Deckhouse today means hand-crafting User, Group, AuthorizationRule, and ClusterAuthorizationRule CRs and applying them via kubectl. There is no first-class CLI for inventory, "who has access to what", or safe revocation.

d8 iam provides:

  • a single discoverable command tree with consistent flag semantics;
  • effective-access inventory: iam list users|groups (aggregated table) and iam get user|group <name> (direct grants, transitive group membership, inherited grants, effective summary, warnings for cycles / orphaned members / manually maintained rules), with SuperAdmin wildcard capabilities surfaced as implicit;
  • iam list rules / iam get rule with reverse lookup of subjects to local User / Group CRs;
  • idempotent grant / revoke

Why do we need it in the patch release (if we do)?

Not necessarily.

Changelog entries

section: deckhouse-cli
type: feature
summary: Add `d8 iam` command tree for managing local users, groups, and access grants

@Jabejixo Jabejixo force-pushed the add-user-group-access-managing branch from e82de0c to 13a6cc9 Compare April 27, 2026 07:52
Signed-off-by: Ivan Zvyagintsev <ivan.zvyagintsev@flant.com>
@Jabejixo Jabejixo force-pushed the add-user-group-access-managing branch from 13a6cc9 to cabfa61 Compare April 27, 2026 07:53
Signed-off-by: Ivan Zvyagintsev <ivan.zvyagintsev@flant.com>
Signed-off-by: Ivan Zvyagintsev <ivan.zvyagintsev@flant.com>
Signed-off-by: Ivan Zvyagintsev <ivan.zvyagintsev@flant.com>
Signed-off-by: Ivan Zvyagintsev <ivan.zvyagintsev@flant.com>
@Jabejixo Jabejixo marked this pull request as ready for review April 29, 2026 07:10
@Jabejixo Jabejixo requested a review from ldmonster as a code owner April 29, 2026 07:10
Copy link
Copy Markdown

@AlwxSin AlwxSin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are lack of some test scenarios:

  • no grant -> revoke round-trip
  • no remove-member tests
  • no tests for UserOperation creation
  • no tests for group create/delete

hash8,
)

if len(name) > 253 {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 8-char hash that guarantees uniqueness sits at the end of the name. I suggest add the hash in the end, after length check.

return fmt.Errorf("setting members: %w", err)
}

_, err = groupClient.Update(ctx, obj, metav1.UpdateOptions{})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding retry.RetryOnConflict from k8s.io/client-go/util/retry for case when someone do the update between .Get and .Update

if err := unstructured.SetNestedSlice(obj.Object, members, "spec", "members"); err != nil {
return EnsureMemberResult{}, fmt.Errorf("setting members on group %q: %w", groupName, err)
}
if _, err := groupClient.Update(ctx, obj, metav1.UpdateOptions{}); err != nil {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding retry.RetryOnConflict from k8s.io/client-go/util/retry for case when someone do the update between .Get and .Update

case iamtypes.KindGroup:
subjectPrincipal = subjectName
if _, err := dyn.Resource(iamtypes.GroupGVR).Get(cmd.Context(), subjectName, metav1.GetOptions{}); err != nil {
fmt.Fprintf(cmd.ErrOrStderr(), "Warning: local Group CR %q not found. Grant may target an external provider group.\n", subjectName)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using check apierrors.IsNotFound(err) for Not found. If not, just print err (there are possible Forbidden, Timeout errors).

}

func findViaGroup(inv *accessInventory, userName, grantGroupName string) string {
directGroups, transitiveGroups := inv.ResolveUserGroups(userName)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using cache for groups. For now code walks over all groups even if .ResolveUserGroups returns all groups

# Revoke a labels-scoped grant (must match the original --scope value)
d8 iam access revoke group admins --access-level Editor --scope labels=team=platform,tier=prod`)

func newRevokeCommand() *cobra.Command {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grant has --dry-run flag, but revoke hasn't. Consider adding dry run flag.

return fmt.Errorf("deleting User %q: %w", name, err)
}

fmt.Fprintf(cmd.ErrOrStderr(), "Warning: user %q may still be referenced in Group memberships. Use \"d8 iam group remove-member\" to clean up.\n", name)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we automate this? There is a task for that, that we should remove deleted user from groups.

if !managedOnly && !manualOnly {
return rows
}
out := rows[:0]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rows[:0] will mutate outer variable.

Suggested change
out := rows[:0]
out := make([]ruleRow, 0, len(rows))

PortForwarding bool `json:"portForwarding"`
ManagedByD8 bool `json:"managedByD8Cli"`
Subjects []subjectJSON `json:"subjects"`
CreationTime time.Time `json:"creationTimestamp,omitempty"`
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My linter tells me, that omitempty is useless against time.Time.
Either:

  • *time.Time json:"omitempty"
  • time.Time json:"omitzero"

return nil, cobra.ShellCompDirectiveError
}

ri := dyn.Resource(gvr)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider use dyn.Resource(gvr).Namespace(namespace) if namespace != ""

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants