At Sourcegraph, Entitle - Permission management is used for time bound access management to systems. Entitle has built-in integrations for GCP/AWS/Terraform Cloud/etc. To extend Entitle’s capabilities to support privileged access to our services (Dotcom Site-Admin, SSC Admin, Sourcegraph Analytics) SAMS implements Entitle’s REST API integration specification. This allows Sourcegraph teammates to request roles to access our services using their SAMS account for authentication.

Adding roles

Add the role to the SAMS-SDK roles package. Convention for adding roles is to publicly declare the role so that it can easily be imported into your code. Collect all roles into a []roleInfo and add a new line appending your roles to the registeredRoles slice. This is used by SAMS to populate Entitle.

The resourceType in roleInfo determines if the role applies service-wide or for individual resources owned by the service.

e.g. Dotcom site admin uses a service level role. Enterprise portal customer admin uses a resource level role with the enterprise_subscription resource type.

var (
	// Dotcom site admin
	RoleDotcomSiteAdmin = ToRole(services.Dotcom, "site_admin")

	dotcomRoles = []roleInfo{
		{
			id:           RoleDotcomSiteAdmin,
			service:      services.Dotcom,
			resourceType: Service,
		},
	}
)

var (
	// Enterprise Portal customer admin
	RoleEnterprisePortalCustomerAdmin = ToRole(services.EnterprisePortal, "customer_admin")

	enterprisePortalRoles = []roleInfo{
		{
			id:           RoleEnterprisePortalCustomerAdmin,
			service:      services.EnterprisePortal,
			resourceType: EnterpriseSubscription,
		},
	}
)

If your role is for a new service, you need to first add the service to the SAMS-SDK services package.

SAMS SDK scopes

Before using the SAMS SDK to query user roles or register role resources you must ensure your client has the necessary scopes:

Query user roles

User roles are queryable using the SAMS SDK RPC GetUserRolesByID(ctx context.Context, userID, service string) ([]*clientsv1.Role, error). Clients must iterate through the returned slice of roles to determine if the desired role is present:

message Role {
  // The fully qualified role name e.g. `dotcom::site_admin`
  string role_id = 1;
  // The service the role is scoped to.
  string service = 2;
  // If the role applies to a resource this is the ID of the resource.
  optional string resource_id = 3;
  // If the role applies to a resource the is the ResourceType of the resource.
  optional string resource_type = 4;
}

Subscribe changes of user roles

Changes of user roles are propagated via SAMS notifications distribution system. To be notified when a user role change event (grant/revoke) has occurred, follow the instructions to configure your service to listen for UserRolesUpdated events.

Implement the UserRolesUpdatedData handler. It is important to note that by design the notification does not include if the role was granted/revoked. It is necessary to query SAMS for the user's roles and check for presence/absence of roles relevant to your service. This prevents issues with out of order notification delivery:

type UserRolesUpdatedData struct {
	// AccountID is the SAMS external ID of the user whose roles have been updated.
	AccountID string `json:"account_id"`
	// Service is the service that the user's roles have been updated in.
	Service services.Service `json:"service"`
	// RoleID is the  role that has been updated.
	RoleID roles.Role `json:"role"`
	// ResourceID is the ID of the resource the role has been updated on,
	// if applicable. When ResourceID is empty, the role is a service-level
	// role that does not apply to a specific resource.
	ResourceID string `json:"resource_id,omitempty"`
	// ResourceType is the type of the resource the role has been updated on,
	// if applicable. When ResourceType is empty, the role is a service-level
	// role that does not apply to a specific resource.
	ResourceType roles.ResourceType `json:"resource_type,omitempty"`
}