Manage Instance Access Pattern
Overview
Protrak supports per-instance user access control. Beyond role-based permissions, specific users can be granted explicit access to individual instances using IInstanceService.AddInstanceUser and IInstanceService.RemoveInstanceUser. This pattern is used to programmatically manage fine-grained access when it must follow business rules (e.g., grant team members access to a project's tasks when they are assigned to the project).
When to Use
- Grant a user access to an instance when they are assigned to a role on a related instance (e.g., project architect gets access to all tasks under the project).
- Revoke access from a user when they are removed from a project or their role changes.
- Provide access to a newly created child instance to all users who have access to the parent.
Applicable Program Types
| Program Type | Interface | Typical Use |
|---|---|---|
| PostConnect Trigger | IPostConnectTriggerProgramAsync | Grant access when a user is linked to an instance |
| PostCreate Trigger | IPostCreateTriggerProgramAsync | Grant access to newly created instances |
| PromoteActionCommand | IPromoteActionCommandProgramAsync | Modify access on state transition |
| PreDelete Trigger | IPreDeleteTriggerProgramAsync | Remove access before deletion |
Pattern Structure
- Fetch the instance and its User-type attributes.
- For each user to be granted (or revoked) access, call
InstanceService.AddInstanceUser(instanceId, userId, isOwner)orInstanceService.RemoveInstanceUser(instanceId, userId).
Code Example — Grant Access via PostConnect
When a task is linked to a project, key team members from the project are given access to the task.
using Prorigo.Protrak.API.Contracts;
using Prorigo.Protrak.API.Services;
using Prorigo.Protrak.Programs;
using System;
using System.Threading.Tasks;
namespace YourNamespace.Programs
{
/// <summary>
/// Type: Post Connect Trigger
/// Configured for: Relation Type ProjectToTask
/// Trigger: After a task is linked to a project
/// Summary: Grants the project team (Architect, Lead, CSM) access to the newly linked task.
/// </summary>
public class ProvideAccessToProjectTeamOnTasks : IPostConnectTriggerProgramAsync
{
public IInstanceService InstanceService { get; set; }
private readonly string ATTRIBUTE_TECH_LEAD = "TechLead";
private readonly string ATTRIBUTE_PROJECT_LEAD = "ProjectLead";
private readonly string ATTRIBUTE_ACCOUNT_MGMT = "AccountManagerUser";
public async Task RunAsync(Relation relation)
{
// relation.SourceInstanceId = project, relation.DestinationInstanceId = task
var project = await InstanceService.GetInstanceAsync(relation.SourceInstanceId, new[]
{
ATTRIBUTE_TECH_LEAD, ATTRIBUTE_PROJECT_LEAD, ATTRIBUTE_ACCOUNT_MGMT
});
await GrantUsersAccessAsync(relation.DestinationInstanceId, project, ATTRIBUTE_TECH_LEAD);
await GrantUsersAccessAsync(relation.DestinationInstanceId, project, ATTRIBUTE_PROJECT_LEAD);
await GrantUsersAccessAsync(relation.DestinationInstanceId, project, ATTRIBUTE_ACCOUNT_MGMT);
}
private async Task GrantUsersAccessAsync(Guid instanceId, Instance source, string attributeName)
{
var users = source.GetUserAttributeValue(attributeName);
if (users == null) return;
foreach (var user in users)
await InstanceService.AddInstanceUserAsync(instanceId, user.UserId, false);
}
}
}
Code Example — Grant Access to All Children
When a user is linked to a project, grant them access to all child items under that project.
private async Task GrantAccessOnChildItems(Guid projectId, Guid userId)
{
var query = new RelatedInstanceQuery
{
RelationFilters = new[]
{
new RelationFilter
{
InstanceId = projectId,
RelationTypeName = "ProjectToSubItem",
TypeName = "SubItem",
RelationDirection = RelationDirection.To
}
},
Skip = 0,
Take = int.MaxValue
};
var deliverables = await InstanceService.GetRelatedInstancesAsync(projectId, query);
if (deliverables?.Items == null) return;
foreach (var item in deliverables.Items)
{
await InstanceService.AddInstanceUserAsync(item.RelatedInstanceId, userId, false);
}
}
Code Example — Revoke Access
Removes a user's access from all related instances when their association is revoked.
private async Task RevokeAccessFromRelatedInstances(Guid parentId, Guid userId, string relationType, string typeName)
{
var query = new RelatedInstanceQuery
{
RelationFilters = new[]
{
new RelationFilter
{
InstanceId = parentId,
RelationTypeName = relationType,
TypeName = typeName,
RelationDirection = RelationDirection.To
}
},
Skip = 0,
Take = int.MaxValue
};
var related = await InstanceService.GetRelatedInstancesAsync(parentId, query);
if (related?.Items == null) return;
foreach (var item in related.Items)
{
await InstanceService.RemoveInstanceUserAsync(item.RelatedInstanceId, userId);
}
}
API Reference
| Service | Method | Signature | Purpose |
|---|---|---|---|
IInstanceService | AddInstanceUserAsync | (Guid instanceId, Guid userId, bool recursiveAccess) | Grant a user access to an instance |
IInstanceService | RemoveInstanceUserAsync | (Guid instanceId, Guid userId) | Revoke a user's access to an instance |
IInstanceService | GetInstanceUsersAsync | (Guid instanceId, ...) | List users who have access to an instance |
IUserService | GetUser(Guid instanceId) | Returns User | Look up user details by UserProfile instance ID |
IUserService | CreateUser(User user) | Returns Guid (new user ID) | Create a new Protrak login user |
IUserService | ActivateUser(Guid instanceId) | — | Activate a user account |
IUserService | DeactivateUser(Guid instanceId) | — | Deactivate a user account |
IUserService | AddUserRoles(Guid userId, string[] roles) | — | Assign roles to a user |
IUserService | RemoveUserRoles(Guid userId, string[] roles) | — | Remove roles from a user |
recursiveAccess parameter
| Value | Effect |
|---|---|
false | Grants access to instance |
true | Grants access to instance and its child instances |
Related: User Account Management
Activate / Deactivate a User Account
For activating or deactivating a Protrak user account on lifecycle transitions (e.g., on a "UserProfile" type), use IUserService:
public class ActivateUserAccount : IPromoteActionCommandProgramAsync
{
public IUserService UserService { get; set; }
public async Task<ProgramResult> RunAsync(Instance instance, string fromState, string toState, string actionName)
{
if (actionName == "Activate")
UserService.ActivateUser(instance.Id);
return new ProgramResult { IsSuccess = true };
}
}
ActivateUser(instanceId)andDeactivateUser(instanceId)take theinstanceIdof the UserProfile instance.
Create a New Platform User
UserService.CreateUser creates a Protrak login user (not just a UserProfile data record). Use this when a business event should result in a new user being able to log into the platform — for example, after a candidate passes onboarding.
using Prorigo.Protrak.API.Contracts;
using Prorigo.Protrak.API.Services;
using Prorigo.Protrak.Programs;
using System;
using System.Threading.Tasks;
namespace YourNamespace.Programs
{
/// <summary>
/// Type: Promote Action Command
/// Configured for: Type Candidate
/// Trigger: On "Onboard" action
/// Summary: Creates a Protrak login user for the candidate and assigns the Employee role.
/// </summary>
public class CreateUserOnOnboarding : IPromoteActionCommandProgramAsync
{
public IInstanceService InstanceService { get; set; }
public IUserService UserService { get; set; }
private readonly string ATTRIBUTE_FULL_NAME = "FullName";
private readonly string ATTRIBUTE_EMAIL = "WorkEmail";
public async Task<ProgramResult> RunAsync(Instance instance, string fromState, string toState, string actionName)
{
var candidate = await InstanceService.GetInstanceAsync(instance.Id,
new[] { ATTRIBUTE_FULL_NAME, ATTRIBUTE_EMAIL });
var fullName = candidate.GetTextAttributeValue(ATTRIBUTE_FULL_NAME);
var email = candidate.GetTextAttributeValue(ATTRIBUTE_EMAIL);
if (string.IsNullOrWhiteSpace(fullName))
return new ProgramResult { IsSuccess = false, Errors = new[] { "Full name is required to create user." } };
if (string.IsNullOrWhiteSpace(email))
return new ProgramResult { IsSuccess = false, Errors = new[] { "Email is required to create user." } };
// CreateUser returns the new user's ID (Guid)
var newUser = new User
{
UserName = fullName,
UserEmail = email,
Roles = new[] { "Employee" } // Assign initial roles at creation
};
var newUserId = UserService.CreateUser(newUser);
if (newUserId == Guid.Empty)
throw new Exception("Failed to create system user.");
return new ProgramResult { IsSuccess = true };
}
}
}
User object properties used with CreateUser:
| Property | Type | Description |
|---|---|---|
UserName | string | Display name for the new user |
UserEmail | string | Email address (used as login identity) |
Roles | string[] | Initial roles to assign at creation |
CreateUsercreates the login account. The platform automatically creates a corresponding UserProfile instance. To assign additional roles later, useUserService.AddUserRoles(userId, roles[]).
Assign Roles to an Existing User
// Add one or more roles to an existing user
UserService.AddUserRoles(userId, new[] { "Manager", "Reviewer" });
// Remove roles from a user
UserService.RemoveUserRoles(userId, new[] { "Reviewer" });