I have developed a custom form for users with limited rights that should only be able to approve a single XY-record and not change any other field. I have created a custom MVC-Form and XY-PageController action for this.
For this I have created a new service operation and repository method. Internally I reuse the other repository methods Retrieve and Update in order to perform the operation.
Simplified Code-Snippet of Repository Method:
public class XYApprovalRequest: ServiceRequest
{
public int Id { get; set; }
public string Notes { get; set; }
}
public XYApprovalResponse SetApproved(IUnitOfWork uow, XYApprovalRequest request)
{
var entity = Retrieve(uow.Connection, new RetrieveRequest()
{
EntityId = request.Id
}).Entity;
entity.Notes = request.Notes ;
entity.Status = XYStatus.Approved
entity.Date = DateTime.Today;
var updateResponse = Update(uow, new SaveRequest<MyRow>()
{
EntityId = entity.Id,
Entity = entity
});
...
}
Now my problem is if I set more restrictive permissions on the service operations or attributes, like ModifyPermission[] on XYRow, my custom operation fails, because it reuses the Retrieve / Update methods internally, which check the user authorization again.
How can I modify this in order to guarantee, that:
Surely I could implement the update method via custom SQL, however I'd loose the advantages of the typed objects provided by the serenity entities.
If users with permission B should be able to retrieve a record, then set B as read permission. Or use a generic basic permission and grant both group of users that one.
If that's not possible, you need to either add a second parameter, e.g. ignorePermissions to your Retrieve method in repository, or, use connection.ById < > instead of calling the repository.
Another possibility:
Find this in SiteInitialization.cs:
registrar.RegisterInstance<IPermissionService>(new Administration.PermissionService());
Replace with:
registrar.RegisterInstance<IPermissionService>(
new Serenity.Web.TransientGrantingPermissionService(new Administration.PermissionService()));
Then in your SetApproved method:
public XYApprovalResponse SetApproved(IUnitOfWork uow, XYApprovalRequest request)
{
var grantor = (ITransientGrantor)Dependency.Resolve<IPermissionService>();
grantor.GrantAll();
try
{
var entity = Retrieve(uow.Connection, new RetrieveRequest()
{
EntityId = request.Id
}).Entity;
}
finally
{
grantor.UndoGrant();
}
This grants all permissions to current user temporarily.
You may also use grantor.Grant("B") instead of grantor.GrantAll() to grant only that permission temporarily.
I have just implemented the second solution and it worked perfectly.
Thank you for the great template and assistance!
Most helpful comment
Another possibility:
Find this in SiteInitialization.cs:
Replace with:
Then in your SetApproved method:
This grants all permissions to current user temporarily.
You may also use grantor.Grant("B") instead of grantor.GrantAll() to grant only that permission temporarily.