Prevent Unwanted Updates
To block specific users from viewing or changing the values in a custom field, you have the option to make the field private. However, there are a few limitations to this solution. Users can either read and/or update all private custom fields in a project or none of them.
With workflows, you can prevent unwanted updates to changes in public fields. You can support a wide variety of update restrictions. For example:
Prohibit anyone but members of the QA team from changing the state for reported bugs from Fixed to Verified.
Mark an issue as Fixed only when the Spent time field is not empty.
Block users from changing the value for the Assignee field once an issue is resolved.
Restrict Updates to a Specific Group of Users
Let's assume that you have a group of accountants who process payment requests. Your company doesn't want the accounting team to accept payment requests without the approval from a manager.
To support this restriction, you need two custom fields:
Field | Description |
---|---|
Authorization status | Stores two enumerated values: Required (the default value) and Authorized. |
Authorizer | Stores a user type. It contains a list of managers who can approve payment requests. |
You want to apply a restriction to the Authorization status field so that it can only be changed to Authorized by the user who is selected as the Authorizer. This means that:
The Authorizer must have permission to read the issue.
The Authorizer must have permission to update the Authorization status field.
No other user should be able to update the value for the Authorization status field.
To support the first and the second points, either use the permission scheme in the project or use a workflow that adds the Authorizer to the visibility list. For details, see Restrict Issue Visibility with Workflows. You should also perform the following setup:
Create a separate Authorizers group and add all of the managers as members.
Grant the same level of access that is available to users who have permission to create issues in the project.
Set this group as the source for values in the Authorizer field.
Now you can restrict who can set the value for the Authorization status field. This rule is triggered when someone tries to set the Authorization status to Authorized. It verifies that the current user is the same person who is set as the Authorizer.
var entities = require('@jetbrains/youtrack-scripting-api/entities');
var workflow = require('@jetbrains/youtrack-scripting-api/workflow');
exports.rule = entities.Issue.onChange({
title: 'Only Authorizers can update value for Authorization status field',
guard: function(ctx) {
return ctx.issue.fields.becomes(ctx.AuthStatus,
ctx.AuthStatus.Authorized);
},
action: function(ctx) {
var authBy = ctx.issue.fields.AuthBy;
workflow.check(authBy && authBy.login === ctx.currentUser.login,
'Only Authorizers can update this field!');
},
requirements: {
AuthBy: {
type: entities.User.fieldType,
name: 'Authorizer'
},
AuthStatus: {
type: entities.EnumField.fieldType,
name: 'Authorization status',
Authorized: {}
}
}
});
This limitation is very strict. However, you can write the rule differently to make the policy more lenient. The rule that follows lets members of the accounting team choose another value for the Authorizer field, even after the Authorization status field has been set to Authorized. When a new authorizer is selected, the value of the Authorization status field switches back to Required. This lets you handle cases where, for example, an authorizer leaves the company or is no longer the manager for the person who submitted the payment request.
var entities = require('@jetbrains/youtrack-scripting-api/entities');
var workflow = require('@jetbrains/youtrack-scripting-api/workflow');
exports.rule = entities.Issue.onChange({
title: 'Only Executors can change Authorizer in authorized requests',
guard: function(ctx) {
var fs = ctx.issue.fields;
return fs.isChanged(ctx.AuthBy) &&
fs.AuthStatus.name === ctx.AuthStatus.Authorized.name;
},
action: function(ctx) {
workflow.check(ctx.currentUser.isInGroup(ctx.executors.name),
'Only Executors can change the Authorizer ' +
'after the request has been authorized!');
ctx.issue.fields.AuthStatus = ctx.AuthStatus.Required;
},
requirements: {
AuthBy: {
type: entities.User.fieldType,
name: 'Authorizer'
},
AuthStatus: {
type: entities.EnumField.fieldType,
name: 'Authorization status',
Authorized: {},
Required: {}
},
executors: {
type: entities.UserGroup
}
}
});
Add Restrictions Based on Field Combinations
The restrictions described in the previous section determine which users can update different fields. You can also add restrictions that are based on field and value combinations. For example, here’s a rule that won’t let anyone change the value for the State field to Paid unless the Authorization status is Authorized.
var entities = require('@jetbrains/youtrack-scripting-api/entities');
var workflow = require('@jetbrains/youtrack-scripting-api/workflow');
exports.rule = entities.Issue.onChange({
title: 'Request must be authorized to be marked as paid',
guard: function(ctx) {
return ctx.issue.fields.becomes(ctx.State, ctx.State.Paid);
},
action: function(ctx) {
var fs = ctx.issue.fields;
workflow.check(fs.AuthStatus &&
fs.AuthStatus.name === ctx.AuthStatus.Authorized.name,
'Only authorized requests can be marked as paid!');
},
requirements: {
AuthStatus: {
type: entities.EnumField.fieldType,
name: 'Authorization status',
Authorized: {},
},
State: {
type: entities.State.fieldType,
Paid: {}
}
}
});
Here’s another variant. When the State has been set to Paid, the value that is stored in the Amount field cannot be modified.
var entities = require('@jetbrains/youtrack-scripting-api/entities');
var workflow = require('@jetbrains/youtrack-scripting-api/workflow');
exports.rule = entities.Issue.onChange({
title: 'Block changes to Amount for paid requests',
guard: function(ctx) {
return ctx.issue.fields.isChanged(ctx.Amount);
},
action: function(ctx) {
var fs = ctx.issue.fields;
workflow.check(fs.State && fs.State.name !== ctx.State.Paid.name,
'You cannot change the Amount for paid requests!');
},
requirements: {
Amount: {
type: entities.Field.floatType
},
State: {
type: entities.State.fieldType,
Paid: {}
}
}
});