Using Security Methods in the PSI

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

The Security class methods in the Project Server Interface (PSI) enable developers to query the security system of Microsoft Office Project Server 2007 and to administer the security permissions for a Project Server instance. You can determine whether a user has specific permissions to control the functionality an application exposes. You can also create custom security categories, templates, or groups with specific permissions that are required for an application. (This article is adapted from an article by Phil Smail, Microsoft Corporation.)

Security System "Query" Methods

There are eight methods in the Security class that check permissions for the user of an application. The methods do not directly query the Published database, but return Boolean values that indicate whether the current user has the specified permissions.

The methods have a singular and a plural form. Because calling a plural method offers better performance than calling the singular method multiple times, we recommend that you call a plural method when you need to check multiple permissions. Following are the Security class methods that check permissions:

  • CheckUserGlobalPermission checks for one global permission.

  • CheckUserGlobalPermissions checks for multiple global permissions.

  • CheckUserObjectPermission checks the user's permission for a project or resource. The first parameter is the project or resource GUID, the second is the object type GUID, and the third is the category permission GUID. You can use CheckUserProjectPermission or CheckUserResourcePermission instead, to avoid specifying the object type GUID.

  • CheckUserObjectPermissions checks the user's permissions for multiple projects or resources. The first parameter is the project or resource GUID, the second is the object type GUID, and the third is the array of category permission GUIDs. We recommend that you use CheckUserProjectPermissions or CheckUserResourcePermissions instead, to avoid specifying the object type GUID.

  • CheckUserProjectPermission checks the user's category permission (the second GUID parameter) for a specified project (the first GUID parameter). CheckUserProjectPermission wraps a call to CheckUserObjectPermission.

  • CheckUserProjectPermissions checks the user's category permissions (the GUID array) for a specified project (the first GUID parameter). CheckUserProjectPermissions wraps a call to CheckUserObjectPermissions.

  • CheckUserResourcePermission checks the user's category permission (the second GUID parameter) for a specified project (the first GUID parameter). CheckUserResourcePermission wraps a call to CheckUserObjectPermission.

  • CheckUserResourcePermissions checks the user's category permissions (the GUID array) for a specified project (the first GUID parameter). CheckUserResourcePermissions wraps a call to CheckUserObjectPermissions.

NoteNote

The CheckUser methods simply inform the application whether the user who is currently logged on has the specified permissions. The methods do not allow explicit searching for permissions of other users.

You can find the permissions of other users only by using impersonation. That is, programmatically log on as another user and then use the CheckUser methods. For a sample application that uses impersonation, see Using the ProjTool Test Application.

The following code sample validates the current user's permissions using the CheckUserGlobalPermission and CheckUserResourcePermission methods. The code assumes that SecurityWebSvc is the namespace set for the Web reference to the Security Web service (http://ServerName/ProjectServerName/_vti_bin/psi/Security.asmx). You must also set a reference to the Microsoft.Office.Project.Server.Library.dll assembly.

using System;
using System.Net;
using PSLibrary = Microsoft.Office.Project.Server.Library;
. . .
CookieContainer cookiecontainer = new CookieContainer();
SecurityWebSvc.Security security = new SecurityWebSvc.Security();
security.Url = "http://ServerName/ProjectServerName/_vti_bin/psi/security.asmx";
security.CookieContainer = cookiecontainer;
security.Credentials = System.Net.CredentialCache.DefaultCredentials;

//Check if the user has the "About Project Server" permission.
bool hasAboutPermission = 
   security.CheckUserGlobalPermission(PSLibrary.PSSecurityGlobalPermission.AboutMicrosoftOfficeProjectServer);

//Check if the user has the "Assign Resource" permission for a specific resource. Set the GUID for an existing resource.
Guid resourceUid = new Guid("a1fcbf91-e91d-44e2-a4a7-3b4b698cb984")
Guid categoryPermission = PSLibrary.PSSecurityCategoryPermission.AssignResource;
bool hasAssignResourcePermission = security.CheckUserResourcePermission(resourceUid, categoryPermission);

For a complete sample application that includes the use of CheckUserGlobalPermissions, see the end of this article.

Security Administration Methods

The other methods in the Security class are for management of security groups, categories, templates, and organizational permissions. You can create, delete, and read security categories, templates, and groups. The only update method is UpdateOrganizationalPermissions; there are no other update methods. To change categories, templates, or groups, use SetCategories, SetTemplates, or SetGroups. You cannot simply send the DataSet delta to update a category, template, or group; instead, send the entire DataSet as a parameter in the method.

You can create custom Project Server permissions and set them for users, groups, and templates. To create custom permissions, you must modify the Published database. For more information, see Walkthrough: Creating and Using Custom Project Server Permissions.

We provide the following code example to explain the security methods. The code example primarily shows how to create a security category using a SecurityCategoriesDataSet. There are seven DataTable objects in a SecurityCatagoriesDataSet, in the following order:

  1. SecurityCategories   Each row specifies the category GUID, name, and description. Only the GUID and name (WSEC_CAT_UID and WSEC_CAT_NAME) are required to create a security category, and must be unique.

  2. UserRelations   Optional. Each row specifies the category GUID and the resource GUID.

  3. GroupRelations   Optional. Specifies the category GUID and the group GUID.

  4. UserPermissions   Optional. Each row specifies the category GUID, resource GUID, and the permission, and sets Allow or Deny for the permission.

  5. GroupPermissions   Optional. Each row specifies the category GUID, group GUID, and the permission, and sets Allow or Deny for the permission.

  6. SecurityCategoryObjects   Optional. Each row specifies the category GUID, object type (project or resource), and object GUID.

  7. SecurityCategoryRules   Optional. Each row specifies the category GUID, object type (project or resource), and rule type. For information about rule types, see ProjectSecurityRules and ResourceSecurityRules. For a code example that creates more than one security category, and an example that uses the SecurityCategoryRules table, see CreateCategories.

Following are the jobs that the code sample in this article performs:

  1. Uses the Resource Web service to do the following:

    • Check whether the current user has valid permissions, using the CheckUserGlobalPermissions method.

    • Create a resource, by using the CreateResources method.

    • Make the resource a Project Server user and specify the logon account, using the SetResourceAuthorization method.

  2. Uses the Security Web service to do the following:

    • Create a SecurityGroupsDataSet instance and add the user to the group, by creating a GroupMembersRow object in the GroupMembers table.

    • Create a security group, by using the CreateGroups method.

    • Create a SecurityCategoriesDataSet instance. Assign the group a specific permission, by creating a GroupPermissionsRow object in the GroupPermissions table.

    • Create a SecurityCategoriesDataSet instance.

    • Add the group to the category, by adding a GroupRelationsRow object to the GroupRelations table in the SecurityCategoriesDataSet object.

    • Give the group a specific permission on the category by adding a GroupPermissionsRow object to the GroupPermissions table in the SecurityCategoriesDataSet object.

    • Add an existing project to the category by adding a SecurityCategoryObjectsRow object to the SecurityCategoryObjects table in the SecurityCategoriesDataSet object.

    • Create a single security category defined in the SecurityCategoriesDataSet object, by using the CreateCategories method.

For a code example that creates more than one security category, see Security.CreateCategories.

The code example includes the utility classes ExceptionHandlers to handle different types of exceptions and ConsoleUtils to write datasets to the console. To compile the sample, set a Web reference to the Resource Web service and name it ResourceWebSvc. Set a Web reference to the Security Web service and name it SecurityWebSvc. For more information about compiling PSI sample code, see Prerequisites for Reference Code Samples.

After you run the sample, check the results on the Manage Users, Manage Groups, and Manage Categories pages in the Security section on the Server Settings page in Project Web Access.

using System;
using System.Net;
using System.Data;
using System.Text;
using System.Web.Services.Protocols;
using PSLibrary = Microsoft.Office.Project.Server.Library;

namespace Microsoft.SDK.Project.Samples.SecurityExample
{
   class Program
   {
      private static ResourceWebSvc.Resource resource =
         new ResourceWebSvc.Resource();
      private static SecurityWebSvc.Security security = 
         new SecurityWebSvc.Security();

      private static ExceptionHandlers exceptionHandlers = new ExceptionHandlers();
      private static ConsoleUtils consoleUtils = new ConsoleUtils();

      [STAThread]
      static void Main()
      {
         try
         {
            const string PROJECT_SERVER_URI = "http://ServerName/ProjectServerName/";
            const string SECURITY_SERVICE_PATH = "_vti_bin/psi/security.asmx";
            const string RESOURCE_SERVICE_PATH = "_vti_bin/psi/resource.asmx";

            System.Net.CookieContainer cookies = new CookieContainer();

            // Set up the Web service objects
            security.Url = PROJECT_SERVER_URI + SECURITY_SERVICE_PATH;
            security.Credentials = CredentialCache.DefaultCredentials;
            security.CookieContainer = cookies;

            resource.Url = PROJECT_SERVER_URI + RESOURCE_SERVICE_PATH;
            resource.Credentials = CredentialCache.DefaultCredentials;
            resource.CookieContainer = cookies;

            #region Check permissions for the application user
            Console.WriteLine("Checking current user permissions:");

            Guid[] permissionChecks = { 
                  // Permissions for creating resources and security groups:
               PSLibrary.PSSecurityGlobalPermission.NewResource, 
               PSLibrary.PSSecurityGlobalPermission.ManageUsersAndGroups, 
                  // Permission for creating security categories:
               PSLibrary.PSSecurityGlobalPermission.ManageSecurity }; 
            bool[] permissionResults = new bool[permissionChecks.Length];
            permissionResults = security.CheckUserGlobalPermissions(permissionChecks);

            bool permissionOk = true;

            for (int i = 0; i < permissionResults.Length; i++)
            {
               permissionOk = permissionOk && permissionResults[i];
            }
            string permissions = "\tNewResource: {0}" +
                                 "\n\tManageUsersAndGroups: {1}" +
                                 "\n\tManageSecurity: {2}";
            Console.WriteLine(string.Format(permissions,
               permissionResults[0], permissionResults[1], permissionResults[2]));
            #endregion

            if (permissionOk)
            {
               #region Create a user
               Console.WriteLine("\n\nCreating a Project Server user\n");

               // Create a GUID for a new user.
               Guid userGuid = Guid.NewGuid();

               // Create a ResourceDataSet and ResourcesRow to specify basic resource data.
               ResourceWebSvc.ResourceDataSet resourceDs =
                  new ResourceWebSvc.ResourceDataSet();
               ResourceWebSvc.ResourceDataSet.ResourcesRow resourceRow =
                  resourceDs.Resources.NewResourcesRow();

               resourceRow.RES_UID = userGuid;
               resourceRow.RES_NAME = "Juan-Carlos Rivas";
               resourceRow.RES_INITIALS = "JCR";
               resourceRow.RES_TYPE = (int)PSLibrary.Resource.Type.WorkResource;
               resourceRow.WRES_EMAIL = "juancarlos@adatum.com";
               resourceDs.Resources.AddResourcesRow(resourceRow);

               bool validateOnly = false;
               bool autoCheckIn = true;
               resource.CreateResources(resourceDs, validateOnly, autoCheckIn);

               consoleUtils.WriteTablesToConsole(resourceDs.Tables);

               // Create a ResourcesRow in a ResourceAuthorizationDataSet 
               // to specify authorization information.
               ResourceWebSvc.ResourceAuthorizationDataSet resourceAuthDs =
                  new ResourceWebSvc.ResourceAuthorizationDataSet();
               ResourceWebSvc.ResourceAuthorizationDataSet.ResourcesRow resourceAuthRow =
                  resourceAuthDs.Resources.NewResourcesRow();
               resourceAuthRow.RES_UID = userGuid;
               // Set Windows authentication.
               resourceAuthRow.RES_IS_WINDOWS_USER = true;
               resourceAuthRow.WRES_ACCOUNT = "adatum\\juancarlos";
               resourceAuthDs.Resources.AddResourcesRow(resourceAuthRow);
               resource.SetResourceAuthorization(resourceAuthDs);

               consoleUtils.WriteTablesToConsole(resourceAuthDs.Tables);
               #endregion
               
               #region Create a security group
               Console.WriteLine("\n\nCreating a security group\n");

               // Create a GUID for the new group.
               Guid groupGuid = Guid.NewGuid();

               // Create a SecurityGroupsRow in a SecurityGroupsDataSet 
               // to specify basic group information.
               SecurityWebSvc.SecurityGroupsDataSet groupDs =
                  new SecurityWebSvc.SecurityGroupsDataSet();
               SecurityWebSvc.SecurityGroupsDataSet.SecurityGroupsRow groupRow =
                  groupDs.SecurityGroups.NewSecurityGroupsRow();
               groupRow.WSEC_GRP_NAME = "SDK Test Group";
               groupRow.WSEC_GRP_UID = groupGuid;
               groupRow.WSEC_GRP_DESC = "Hello everyone from the Project Team!";
               groupDs.SecurityGroups.AddSecurityGroupsRow(groupRow);

               // Specify which users belong to the new group.
               SecurityWebSvc.SecurityGroupsDataSet.GroupMembersRow groupMembersRow =
                  groupDs.GroupMembers.NewGroupMembersRow();
               groupMembersRow.WSEC_GRP_UID = groupGuid;
               // Add the GUID of the resource to the group.
               groupMembersRow.RES_UID = userGuid;
               groupDs.GroupMembers.AddGroupMembersRow(groupMembersRow);

               // Specify a global permission for the group
               SecurityWebSvc.SecurityGroupsDataSet.GlobalPermissionsRow globalPermRow =
                  groupDs.GlobalPermissions.NewGlobalPermissionsRow();
               globalPermRow.WSEC_GRP_UID = groupGuid;
               // Add a permission that applies to the group. 
               // For example, add the "About Microsoft Office Project Server" permission.
               globalPermRow.WSEC_FEA_ACT_UID = 
                  PSLibrary.PSSecurityGlobalPermission.AboutMicrosoftOfficeProjectServer;
               globalPermRow.WSEC_ALLOW = true;
               groupDs.GlobalPermissions.AddGlobalPermissionsRow(globalPermRow);

               // Now that all the rows are added to the relevant tables,
               // create the group.
               security.CreateGroups(groupDs);

               consoleUtils.WriteTablesToConsole(groupDs.Tables);
               #endregion

               #region Create a security category
               Console.WriteLine("\n\nCreating a security category\n");

               // Create a GUID for the new category.
               Guid categoryGuid = Guid.NewGuid();

               SecurityWebSvc.SecurityCategoriesDataSet categoryDs =
                  new SecurityWebSvc.SecurityCategoriesDataSet();
               SecurityWebSvc.SecurityCategoriesDataSet.SecurityCategoriesRow categoryRow =
                  categoryDs.SecurityCategories.NewSecurityCategoriesRow();

               categoryRow.WSEC_CAT_UID = categoryGuid;
               categoryRow.WSEC_CAT_NAME = "SDK Test Category";
               categoryRow.WSEC_CAT_DESC = "Categorically, hello everyone!";
               categoryDs.SecurityCategories.AddSecurityCategoriesRow(categoryRow);

               // Add the group to the category.
               SecurityWebSvc.SecurityCategoriesDataSet.GroupRelationsRow groupRelationsRow =
                  categoryDs.GroupRelations.NewGroupRelationsRow();
               groupRelationsRow.WSEC_CAT_UID = categoryGuid;
               groupRelationsRow.WSEC_GRP_UID = groupGuid;
               categoryDs.GroupRelations.AddGroupRelationsRow(groupRelationsRow);

               // Specify the permissions for the group on the category.
               SecurityWebSvc.SecurityCategoriesDataSet.GroupPermissionsRow groupPermRow =
                  categoryDs.GroupPermissions.NewGroupPermissionsRow();
               groupPermRow.WSEC_CAT_UID = categoryGuid;
               groupPermRow.WSEC_GRP_UID = groupGuid;
               groupPermRow.WSEC_ALLOW = true;

               // For example, add the "Open Project" permission.
               groupPermRow.WSEC_FEA_ACT_UID = PSLibrary.PSSecurityCategoryPermission.OpenProject;
               categoryDs.GroupPermissions.AddGroupPermissionsRow(groupPermRow);

               // Add objects (projects or resources) to the category.
               SecurityWebSvc.SecurityCategoriesDataSet.SecurityCategoryObjectsRow categoryObjectRow =
                  categoryDs.SecurityCategoryObjects.NewSecurityCategoryObjectsRow();
               categoryObjectRow.WSEC_CAT_UID = categoryGuid;
               categoryObjectRow.WSEC_OBJ_TYPE_UID = PSLibrary.PSSecurityObjectType.Project;

               // Add an existing project to the category. 
               // Change the following GUID to a project on your system.
               categoryObjectRow.WSEC_OBJ_UID = new Guid("BC323C21-B7E4-4631-AF99-C44E5C52BA4E");
               categoryDs.SecurityCategoryObjects.AddSecurityCategoryObjectsRow(categoryObjectRow);

               security.CreateCategories(categoryDs);

               consoleUtils.WriteTablesToConsole(categoryDs.Tables);
               Console.WriteLine("Done!\n");
               #endregion
            }
            else
            {
               Console.WriteLine(
                  "You do not have the necessary Project Server permissions to run this application.");
            }
         }
         catch (SoapException ex)
         {
            exceptionHandlers.HandleSoapException(ex);
         }
         catch (WebException ex)
         {
            exceptionHandlers.HandleWebException(ex);
         }
         catch (Exception ex)
         {
            exceptionHandlers.HandleException(ex);
         }
         finally
         {
            exceptionHandlers.ResetConsole();
         }
      }
   }

   class ExceptionHandlers
   {
      public ExceptionHandlers()
      {
      }

      public void HandleSoapException(SoapException ex)
      {
         PSLibrary.PSClientError error = new PSLibrary.PSClientError(ex);
         PSLibrary.PSErrorInfo[] errors = error.GetAllErrors();
         string errMess = "==============================\r\nError: \r\n";
         for (int i = 0; i < errors.Length; i++)
         {
            errMess += "\n" + ex.Message.ToString() + "\r\n";
            errMess += "".PadRight(30, '=') + "\r\nPSCLientError Output:\r\n \r\n";
            errMess += errors[i].ErrId.ToString() + "\n";

            for (int j = 0; j < errors[i].ErrorAttributes.Length; j++)
            {
               errMess += "\r\n\t" + errors[i].ErrorAttributeNames()[j] + ": "
                  + errors[i].ErrorAttributes[j];
            }
            errMess += "\r\n".PadRight(30, '=');
         }
         Console.ForegroundColor = ConsoleColor.Red;
         Console.WriteLine(errMess);
      }

      public void HandleWebException(WebException ex)
      {
         string errMess = ex.Message.ToString() +
            "\n\nLog on, or check the Project Server Queuing Service";
         Console.ForegroundColor = ConsoleColor.Red;
         Console.WriteLine("Error: " + errMess);
      }

      public void HandleException(Exception ex)
      {
         Console.ForegroundColor = ConsoleColor.Red;
         Console.WriteLine("Error: " + ex.Message);
      }

      public void ResetConsole()
      {
         Console.ResetColor();
         Console.WriteLine("\r\n\r\nPress any key...");
         Console.ReadKey();
      }
   }

   class ConsoleUtils
   {
      public ConsoleUtils()
      {
      }

      // Write all contents of a table collection to the console.
      public void WriteTablesToConsole(System.Data.DataTableCollection theTables)
      {
         Console.ForegroundColor = ConsoleColor.DarkGreen;
         foreach (System.Data.DataTable table in theTables)
         {
           
            int[] columnWidths = new int[table.Columns.Count];
            int tableWidth = 0;
            string dataString;
            Console.WriteLine("Table: " + table.TableName);
           
            // Write out the column names and get their spacing.
            StringBuilder tableRow = new StringBuilder();
            for (int i = 0; i < table.Columns.Count; i++)
            {
               columnWidths[i] = GetColumnWidth(table.Columns[i]);
               tableRow.Append(table.Columns[i].ColumnName.PadRight(columnWidths[i]));

               tableWidth += columnWidths[i];
            }
            // Add a space so it won't wrap.
            tableWidth += 1;
            // Make the console as wide as the widest table.
            Console.BufferWidth = (Console.BufferWidth > tableWidth 
               ? Console.BufferWidth : tableWidth);
            tableRow.Append("\r\n");
            Console.Write(tableRow.ToString());

            // Write out the data.
            foreach (DataRow row in table.Rows)
            {
               tableRow = new StringBuilder();
               for (int i = 0; i < table.Columns.Count; i++)
               {
                  dataString = row[i].ToString();
                  // Truncate output if it is wider than the desired column width.
                  if (dataString.Length >= columnWidths[i])
                  {
                     dataString = dataString.Substring(0, columnWidths[i] - 1);
                  }
                  // Add the output to the stringbuilder and pad right to fill
                  // up to the column width.
                  tableRow.Append(dataString.PadRight(columnWidths[i]));
               }
               tableRow.Append("\r\n");
               Console.Write(tableRow.ToString());
            }
            Console.Write("\r\n".PadLeft(tableWidth, '-'));
         }
         Console.ResetColor();
      }
      // Helper function for WriteTablesToConsole
      private static int GetColumnWidth(DataColumn column)
      {
         // Note: May not handle byte[]data types well.
         const int MAX_COL_WIDTH = 40;
         int dataWidth = 0;

         // Return 12 for numbers, 30 for dates, and string width for strings.
         switch (column.DataType.UnderlyingSystemType.ToString())
         {
            case "System.Boolean":
            case "System.Byte":
            case "System.Byte[]":
            case "System.Char":
            case "System.Decimal":
            case "System.Double":
            case "System.Int16":
            case "System.Int32":
            case "System.Int64":
            case "System.SByte":
            case "System.Single":
            case "System.UInt16":
            case "System.UInt32":
            case "System.UInt64":
               dataWidth = 12;
               break;
            case "System.DateTime":
            case "System.TimeSpan":
               dataWidth = 30;
               break;
            case "System.Guid":
               dataWidth = 37;
               break;
            case "System.String":
               // If the column has a maximum length, use it.
               if (column.MaxLength > 0)
               {
                  dataWidth = column.MaxLength;
               }
               else
               {
                  // Otherwise use the maximum column width.
                  dataWidth = MAX_COL_WIDTH;
               }
               break;
            default:
               dataWidth = column.ColumnName.Length;
               break;
         }
         // Truncate if over the maximum length.
         if (dataWidth > MAX_COL_WIDTH)
         {
            dataWidth = MAX_COL_WIDTH;
         }
         // Always be at least as wide as the colum name.
         return (column.ColumnName.Length > (dataWidth) 
            ? column.ColumnName.Length + 1 : dataWidth);
      }
   }
}

See Also

Tasks

Walkthrough: Creating and Using Custom Project Server Permissions

Concepts

Using Project Server Security

Prerequisites for Reference Code Samples