Skip to main content

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 TypeInterfaceTypical Use
PostConnect TriggerIPostConnectTriggerProgramAsyncGrant access when a user is linked to an instance
PostCreate TriggerIPostCreateTriggerProgramAsyncGrant access to newly created instances
PromoteActionCommandIPromoteActionCommandProgramAsyncModify access on state transition
PreDelete TriggerIPreDeleteTriggerProgramAsyncRemove access before deletion

Pattern Structure

  1. Fetch the instance and its User-type attributes.
  2. For each user to be granted (or revoked) access, call InstanceService.AddInstanceUser(instanceId, userId, isOwner) or InstanceService.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

ServiceMethodSignaturePurpose
IInstanceServiceAddInstanceUserAsync(Guid instanceId, Guid userId, bool recursiveAccess)Grant a user access to an instance
IInstanceServiceRemoveInstanceUserAsync(Guid instanceId, Guid userId)Revoke a user's access to an instance
IInstanceServiceGetInstanceUsersAsync(Guid instanceId, ...)List users who have access to an instance
IUserServiceGetUser(Guid instanceId)Returns UserLook up user details by UserProfile instance ID
IUserServiceCreateUser(User user)Returns Guid (new user ID)Create a new Protrak login user
IUserServiceActivateUser(Guid instanceId)Activate a user account
IUserServiceDeactivateUser(Guid instanceId)Deactivate a user account
IUserServiceAddUserRoles(Guid userId, string[] roles)Assign roles to a user
IUserServiceRemoveUserRoles(Guid userId, string[] roles)Remove roles from a user

recursiveAccess parameter

ValueEffect
falseGrants access to instance
trueGrants access to instance and its child instances

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) and DeactivateUser(instanceId) take the instanceId of 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:

PropertyTypeDescription
UserNamestringDisplay name for the new user
UserEmailstringEmail address (used as login identity)
Rolesstring[]Initial roles to assign at creation

CreateUser creates the login account. The platform automatically creates a corresponding UserProfile instance. To assign additional roles later, use UserService.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" });