Second Life of a Hungarian SharePoint Geek

May 16, 2016

Project Server Displays Incorrect Effective Rights for Resources

Filed under: Bugs, PS 2013, Security — Tags: , , — Peter Holpar @ 15:55

We observed the following – in my opinion buggy – behavior in case of Project Server 2013 (patch state: 2016 March CU):

In our project web sites we have a web page that should display the name of the project and the title of the project owner. The name is displayed using client-side technologies, that means JavaScript and the Project Server JavaScript object model. See the code snippets below. Note, that these are parts of an AngularJS applications and cannot be used alone, but only part of the whole application. I show the code only to provide you an overview, about what I’m writing here.

The “business logic” from the controller:

  1. var promise = OurCustomService.getProjInfo($scope);
  2. promise.then(function (pi) {
  3.     var projName = pi.project.get_name();
  4.     // for some users the get_owner() mehtod returns null
  5.     var projManName = pi.project.get_owner().get_title();            
  6. }, function (errorMsg) {
  7.     console.log("Error: " + errorMsg);
  8. });
  9.  
  10.     }, function (errorMsg) {
  11.         console.log("Error: " + errorMsg);
  12.     });
  13. });

…and the service code:

  1. this.getProjInfo = function ($scope) {
  2.     var deferred = $q.defer();
  3.  
  4.     var ctx = new SP.ClientContext.get_current();
  5.  
  6.     var projContext = PS.ProjectContext.get_current();
  7.     projContext.set_isPageUrl(ctx.get_isPageUrl);
  8.     var proj = projContext.get_projects().getById($scope.projectId);
  9.     projContext.load(proj, "Name", "Owner.Title");
  10.  
  11.     projContext.executeQueryAsync(
  12.         function () {
  13.             deferred.resolve(
  14.                 {
  15.                     project: proj
  16.                 });
  17.         },
  18.         function (sender, args) {
  19.             deferred.reject('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
  20.         }
  21.     );
  22.  
  23.     return deferred.promise;
  24. };

We found that this solution does not work for a lot of our users. Having a look via Internet Explorer (F12) Developer Tools in the code running with their credentials I found that the object returned by the pi.project.get_owner() expression is null, causing an exception as I want to access the get_title() method of this null object. It was obviously a security issue. To be able to access the title of the project owner (that is a resource as well), the user should have the View Enterprise Resource Data category permission in relation to the project owner resource.

When checking the Owner property of the Project via REST (the Guid in the URL is the ID of the given project):

http://YourProjectServer/PWA/_api/ProjectServer/Projects(‘1EF03FA9-2F7A-E411-80D4-005056B47337’)/Owner

the users having the problem received null as result, however, other users having more permissions (including the required one) received the full info of the project owner as expected.

Similarly, we have checked the resources available for the user via the REST query:

http://YourProjectServer/PWA/_api/ProjectServer/EnterpriseResources

The result for the “problematic” users did not contained the resource that is the project owner, however for the other users (the ones who had no problem with the AngularJS application mentioned above) the result included this resource as well.

No problem, it sounds OK up to this point.

However, when we selected any of  “problematic” these users on the Manage Users page in PWA Settings, clicked Check Effective Rights, change the Permission Type to Category Permission – Resource, and selected the selected the project owner (the one, the user has in practice no permission at all) in the Security Object – Resource list, the report shows, that the user has View Enterprise Resource Data permission via a group (let’s say All Users) and a category (let’s say My Project Team). Then we clicked other resources in the Security Object – Resource list as well, and found, that based on the report, the user should have View Enterprise Resource Data permission to almost all of these resources either, although based on the REST query above (http://YourProjectServer/PWA/_api/ProjectServer/EnterpriseResources) he has permission only a very few of them.

That is pretty strange. The users are really member of the All Users group, and the My Project Team category is really assigned to the All Users group.

The resources affected by the My Project Team category are selected by the “They are members of a Project Team on a project owned by the User” rule:

image

Members of the All Users group have View Enterprise Resource Data permission on resources included in the My Project Team category:

image

The resources displayed by the Effective Rights page as ones the “problematic” users have permission to are however no team members of the users at all!

How is it possible? In this post I don’t want to bore you with very deep technical details (I plan to post these details in a follow-up post later), the most important facts are, that the objects and stored procedures used to check the permissions when you want to access a resource differ from the ones used to display the effective rights.

For example, when checking the “They are members of a Project Team on a project owned by the User” rule, the pub.MSP_WEB_FN_SEC_ResourcesInCategoryRule3 table-valued function is used, where @res_uid parameter is the resource ID of the current user. It should return the ID of all of the resources that are affected by this category rule:

SELECT RES_UID AS OBJ_UID
FROM pub.MSP_ASSIGNMENTS
WHERE WRES_UID_MANAGER = @res_uid
  AND WASSN_DELETED_IN_PROJ = 0

UNION

SELECT PR.RES_UID AS OBJ_UID
FROM pub.MSP_PROJECTS P
INNER JOIN pub.MSP_PROJECT_RESOURCES PR ON PR.PROJ_UID = P.PROJ_UID
WHERE P.WRES_UID = @res_uid

That means, resources returned by the query if the resource that belongs to the current user (the one that wants to access another resource) is either an assignment owner of  a non-deleted project task assignment where the target resource (the one the current user want to access) is the assignment resource (first part of the UNION query), or there is a project that has the current user as project manager and the target resource as project resource (second part of the UNION query). That sounds logically.

On the contrary, when displaying the effective rights, the pub.MSP_WEB_FN_SEC_GetEffectiveCategories_NONCLAIMSCOMPLIANT tabled-value function is called by the pub.MSP_WEB_SP_SEC_ReadUserEffectiveRightsWithCategoryPermissions_NONCLAIMSCOMPLIANT stored procedure. This function uses the following condition to check the “They are members of a Project Team on a project owned by the User” rule, where @res_uid parameter is the resource ID of the current user, and the @wsec_obj_uid parameter is the ID of the target resource. It should insert the value 3 into the temporary @rule_table is the target resource is affected by the category rule:

IF EXISTS (SELECT TOP 1 RES_UID FROM MSP_ASSIGNMENTS WHERE WRES_UID_MANAGER = @res_uid AND RES_UID = @wsec_obj_uid)
    OR EXISTS (SELECT TOP 1 RES_UID FROM MSP_PROJECT_RESOURCES WHERE RES_UID = @wsec_obj_uid)
    OR EXISTS (SELECT TOP 1 WRES_UID as RES_UID FROM MSP_PROJECTS WHERE WRES_UID = @res_uid)
BEGIN
    INSERT INTO @rule_table(WSEC_OBJ_RULE_TYPE) VALUES (3)
END

As far as I see, this condition is wrong. It says that there should be an assignment having the current user as an assignment owner and the target resource as assignment resource (see first part of the UNION in the first SQL query above, differs in checking the WASSN_DELETED_IN_PROJ flag), or there is any project, where the target resource is a resource, or there is any project where the current user is the project manager (compare with the second part of the UNION query above, condition this time is total wrong). It means we may receive a false positive on the Effective Rights page for each resources, that are resources on any project, and for all resources if the current user (the one we check the effective rights for) is a project manager of any project. In fact, we should receive a positive value in all of these cases (as long as there is no explicit deny), it is a false positive, when there is no other, valid positive value via other categories.

I think one should re-arrange the condition like this:

IF EXISTS (SELECT TOP 1 RES_UID FROM MSP_ASSIGNMENTS WHERE WRES_UID_MANAGER = @res_uid AND RES_UID = @wsec_obj_uid AND WASSN_DELETED_IN_PROJ = 0)
    OR EXISTS (SELECT TOP 1 PR.RES_UID FROM MSP_PROJECTS P INNER JOIN MSP_PROJECT_RESOURCES PR ON PR.PROJ_UID = P.PROJ_UID
    WHERE PR.RES_UID = @wsec_obj_uid AND P.WRES_UID = @res_uid)
BEGIN
    INSERT INTO @rule_table(WSEC_OBJ_RULE_TYPE) VALUES (3)
END

or even better, one could simply re-use the logic implemented in the pub.MSP_WEB_FN_SEC_ResourcesInCategoryRule3 table-valued function:

IF EXISTS (SELECT TOP 1 OBJ_UID FROM pub.MSP_WEB_FN_SEC_ResourcesInCategoryRule3(@res_uid) WHERE OBJ_UID = @wsec_obj_uid)
BEGIN
    INSERT INTO @rule_table(WSEC_OBJ_RULE_TYPE) VALUES (3)
END

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: