Skip to content

feat: adding Nautobot Admin Groups and Permission sync - #2157

Open
abhimanyu003 wants to merge 2 commits into
mainfrom
nautobotOPPermissions
Open

feat: adding Nautobot Admin Groups and Permission sync#2157
abhimanyu003 wants to merge 2 commits into
mainfrom
nautobotOPPermissions

Conversation

@abhimanyu003

@abhimanyu003 abhimanyu003 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

This PR adds Nautobot Admin Groups and Permission sync.
Example of config map this will sync form.

This is only for admin that's why it's nested into admin folder to keep things cleaner

apiVersion: v1
kind: ConfigMap
metadata:
  name: permission-groups
  namespace: nautobot
data:
  permission-groups.yaml: |
    permissions:
      - name: read-all
        description: Allow read access to all objects
        can_view: true
        object_types:
          - "dcim.device"
          - "dcim.location"
          - "dcim.rack"
          - "ipam.prefix"
          - "ipam.vlan"
        groups:
          - 'infra.baremetal.admin'
          - 'infra.controlplane.admin'
          - 'infra.ironops'
          - 'infra.fabricops'
          - 'infra.dctechs'
          - 'rxdb.manager'
          - 'sandbox.manager'
          - 'sddc.manager'
          - 'rxdb.member'
          - 'sandbox.member'
          - 'sddc.member'
          - 'rackspace.all'

      - name: user-api-tokens
        description: Allow users to manage their own API tokens
        can_view: true
        can_add: true
        can_change: true
        can_delete: true
        object_types:
          - "users.token"
        groups:
          - 'infra.baremetal.admin'
          - 'infra.controlplane.admin'
          - 'infra.ironops'
          - 'infra.fabricops'
          - 'infra.dctechs'
          - 'rxdb.manager'
          - 'sandbox.manager'
          - 'sddc.manager'
          - 'rxdb.member'
          - 'sandbox.member'
          - 'sddc.member'
          - 'rackspace.all'

      - name: napalm-read
        description: Allow NAPALM read operations on devices
        can_view: true
        additional_actions:
          - "napalm_read"
        object_types:
          - "dcim.device"
        groups:
          - 'infra.baremetal.admin'

      - name: job-execution
        description: Allow job execution
        can_view: true
        can_add: true
        can_change: true
        object_types:
          - "extras.job"
          - "extras.jobresult"
        groups:
          - 'infra.baremetal.admin'
          - 'infra.controlplane.admin'

      - name: sys-admin
        description: Full system administration access
        can_view: true
        can_add: true
        can_change: true
        can_delete: true
        object_types:
          - "dcim.device"
          - "dcim.location"
          - "dcim.rack"
          - "ipam.prefix"
          - "ipam.vlan"
          - "extras.job"
        groups:
          - 'infra.controlplane.admin'

      - name: storage-admin
        description: Storage administration access
        can_view: true
        can_add: true
        can_change: true
        can_delete: true
        object_types:
          - "dcim.device"
          - "dcim.rack"
        groups:
          - 'infra.baremetal.admin'
          - 'infra.controlplane.admin'

Groups Screenshot
image

Permission Screenshot

image

@cardoe
cardoe requested a review from a team July 29, 2026 20:24
permissionGroupData map[string]string,
) error {
log := logf.FromContext(ctx)
log.Info("syncing permission groups", "count", len(permissionGroupData))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think log statements should be at the end of the code. In case of count zero, since this function is later on calling SyncAll, (len(permissionGroupData)) at the end should be logged.

"context"
"net/http"

"github.com/charmbracelet/log"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A suggestion, we should be using "k8s.io/klog/v2". This has more chance of being maintained and up to date with latest vulnerabilities.
Reference Cluster API vSphere

}

func (s *GroupService) Create(ctx context.Context, req nb.GroupRequest) (*nb.Group, error) {
group, resp, err := s.client.APIClient.UsersAPI.UsersGroupsCreate(ctx).GroupRequest(req).Execute()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Again an observation! This calls multiple nested functions. We should try to make this more modular.

group, resp, err := s.client.APIClient.UsersAPI.UsersGroupsCreate(ctx).GroupRequest(req).Execute()
if err != nil {
bodyString := helpers.ReadResponseBody(resp)
s.client.AddReport("createNewGroup", "failed to create", "model", req.Name, "error", err.Error(), "response_body", bodyString)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is this different from logging?

}

func (s *GroupService) GetByName(ctx context.Context, name string) *nb.Group {
list, resp, err := s.client.APIClient.UsersAPI.UsersGroupsList(ctx).Name([]string{name}).Execute()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Same observation, this call should be more modular.

}

func (s *GroupService) ListAll(ctx context.Context) []nb.Group {
return helpers.PaginatedList(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

return should not hold a function call.


// GetOrCreate fetches a group by name or creates it if it doesn't exist.
func (s *GroupService) GetOrCreate(ctx context.Context, name string) (*nb.Group, error) {
existing := s.GetByName(ctx, 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.

existing , err:= s.GetByName(ctx, name). This is more common practice in golang.

}

func (s *PermissionService) Create(ctx context.Context, req nb.ObjectPermissionRequest) (*nb.ObjectPermission, error) {
perm, resp, err := s.client.APIClient.UsersAPI.UsersPermissionsCreate(ctx).ObjectPermissionRequest(req).Execute()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think, we can make this call more modular.

}
}

func (s *PermissionGroupSync) SyncAll(ctx context.Context, data map[string]string) error {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SyncAll should be SyncAllGroup.

s.client.AddReport("yamlFailed", "file: "+key+" error: "+err.Error())
return err
}
allPermissions = append(allPermissions, config.Permissions...)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This piece of code looks really good :)

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