title: SSRS IAuthorizationExtension get user roles from external source
body: <p>In our current implementation of SSRS, all users except admins have the same user-role, as we've never managed to figure out how to get user-roles from the db.</p>
  
  <p>No matter how we try to do the call to the db for the access_rules, the extension stops working, resulting in users not being able to access anything. Not even the 'mids' resource.</p>
  
  <p>Is it at all possible to access an external resource from the IAuthorizationExtension? If so, does anyone know how?</p>
  
  <p>Here's what we're trying currently:</p>
  
  <pre><code>public class AuthorizationExtension : IAuthorizationExtension
  {
      private static readonly IList&lt;string&gt; AdminUserNames = new List&lt;string&gt;();
  
      private static Hashtable _modelItemOperNames;
      private static Hashtable _modelOperNames;
      private static Hashtable _catOperNames;
      private static Hashtable _fldOperNames;
      private static Hashtable _rptOperNames;
      private static Hashtable _resOperNames;
      private static Hashtable _dsOperNames;
  
      private const int NrRptOperations = 27;
      private const int NrFldOperations = 10;
      private const int NrResOperations = 7;
      private const int NrDSOperations = 7;
      private const int NrCatOperations = 16;
      private const int NrModelOperations = 11;
      private const int NrModelItemOperations = 1;
  
      static AuthorizationExtension()
      {
          InitializeMaps();
      }
  
      private bool UserIsAdmin(string userName)
      {
          return AdminUserNames.Contains(userName, StringComparer.CurrentCultureIgnoreCase);
      }
  
      private bool UserHasAccessToPrincipalName(string userName, string principalName)
      {
          var userRoles = new[] { "mids" }; //Temp role for now
  
          //Our attempt to get external roles
          var sql = @"SELECT DISTINCT ca.namespace
                      FROM Users u
                      inner join Access_rules r on u.user_id = r.user_id
                      inner join Client_accounts ca on r.ca_id = ca.ca_id
                      WHERE email = @userName";
  
          using (IDbConnection c = new SqlConnection(_connectionString))
          {
              string[] namespaces = c.Query&lt;string&gt;(sql, new { userName }).ToArray();
              userRoles = userRoles.Concat(namespaces).ToArray();
          }
  
  
          // Admin role check
          if (userRoles.Contains("*"))
              return true;
  
          return userRoles.Contains(principalName, StringComparer.CurrentCultureIgnoreCase);
      }
  
      /// &lt;summary&gt;
      /// Returns a security descriptor that is stored with an individual item in the report server database. 
      /// &lt;/summary&gt;
      /// &lt;param name="acl"&gt;The access code list (ACL) created by the report server for the item. It contains a collection of access code entry (ACE) structures.&lt;/param&gt;
      /// &lt;param name="itemType"&gt;The type of item for which the security descriptor is created.&lt;/param&gt;
      /// &lt;param name="stringSecDesc"&gt;Optional. A user-friendly description of the security descriptor, used for debugging. This is not stored by the report server.&lt;/param&gt;
      /// &lt;returns&gt;Should be implemented to return a serialized access code list for the item.&lt;/returns&gt;
      public byte[] CreateSecurityDescriptor(AceCollection acl, SecurityItemType itemType, out string stringSecDesc)
      {
          // Creates a memory stream and serializes the ACL for storage.
          BinaryFormatter bf = new BinaryFormatter();
          using (MemoryStream result = new MemoryStream())
          {
              bf.Serialize(result, acl);
              stringSecDesc = null;
              return result.GetBuffer();
          }
      }
  
      private void LogAccessCheck(string userName, string operation, bool authorized)
      {
          var auth = authorized ? "AUTHORIZED" : "DENIED";
          Logger.Debug($"Access check: {auth} - user: {userName}, operation: {operation}");
      }
  
      /// &lt;summary&gt;
      /// Indicates whether a given user is authorized to access the item for a given catalog operation.
      /// &lt;/summary&gt;
      /// &lt;param name="userName"&gt;The name of the user as returned by the GetUserInfo method.&lt;/param&gt;
      /// &lt;param name="userToken"&gt;Pointer to the user ID returned by GetUserInfo.&lt;/param&gt;
      /// &lt;param name="secDesc"&gt;The security descriptor returned by CreateSecurityDescriptor.&lt;/param&gt;
      /// &lt;param name="operation"&gt;The operation being requested by the report server for a given user.&lt;/param&gt;
      /// &lt;returns&gt;True if the user is authorized.&lt;/returns&gt;
      public bool CheckAccess(string userName, IntPtr userToken, byte[] secDesc, CatalogOperation operation)
      {
          // If the user is the administrator, allow unrestricted access.
          if (UserIsAdmin(userName))
          {
              LogAccessCheck(userName, operation.ToString(), true);
              return true;
          }
  
          var acl = DeserializeAcl(secDesc);
          var isAuthorized = false;
  
          // First check to see if the user or group has an access control entry for the item
          // If an entry is found, return true if the given required operation is contained in the ACE structure
          foreach (AceStruct ace in acl)
              if (UserHasAccessToPrincipalName(userName, ace.PrincipalName))
                  foreach (CatalogOperation aclOperation in ace.CatalogOperations)
                      if (aclOperation == operation)
                          isAuthorized = true;
  
          LogAccessCheck(userName, operation.ToString(), true);
          return isAuthorized;
      }
  
      public bool CheckAccess(string userName, IntPtr userToken, byte[] secDesc, ModelItemOperation operation)
      {
          if (UserIsAdmin(userName))
          {
              LogAccessCheck(userName, operation.ToString(), true);
              return true;
          }
  
          var acl = DeserializeAcl(secDesc);
          var isAuthorized = false;
  
          foreach (AceStruct ace in acl)
              if (UserHasAccessToPrincipalName(userName, ace.PrincipalName))
                  foreach (ModelItemOperation aclOperation in ace.ModelItemOperations)
                      if (aclOperation == operation)
                          isAuthorized = true;
  
          LogAccessCheck(userName, operation.ToString(), true);
          return isAuthorized;
      }
  
      public bool CheckAccess(string userName, IntPtr userToken, byte[] secDesc, ModelOperation operation)
      {
          if (UserIsAdmin(userName))
          {
              LogAccessCheck(userName, operation.ToString(), true);
              return true;
          }
  
          var acl = DeserializeAcl(secDesc);
          var isAuthorized = false;
  
          foreach (AceStruct ace in acl)
              if (UserHasAccessToPrincipalName(userName, ace.PrincipalName))
                  foreach (ModelOperation aclOperation in ace.ModelOperations)
                      if (aclOperation == operation)
                          isAuthorized = true;
  
          LogAccessCheck(userName, operation.ToString(), true);
          return isAuthorized;
      }
  
      public bool CheckAccess(string userName, IntPtr userToken, byte[] secDesc, ReportOperation operation)
      {
          if (UserIsAdmin(userName))
          {
              LogAccessCheck(userName, operation.ToString(), true);
              return true;
          }
  
          var acl = DeserializeAcl(secDesc);
          var isAuthorized = false;
  
          foreach (AceStruct ace in acl)
              if (UserHasAccessToPrincipalName(userName, ace.PrincipalName))
                  foreach (ReportOperation aclOperation in ace.ReportOperations)
                      if (aclOperation == operation)
                          isAuthorized = true;
  
          LogAccessCheck(userName, operation.ToString(), true);
          return isAuthorized;
      }
  
      public bool CheckAccess(string userName, IntPtr userToken, byte[] secDesc, FolderOperation operation)
      {
          if (UserIsAdmin(userName))
          {
              LogAccessCheck(userName, operation.ToString(), true);
              return true;
          }
  
          var acl = DeserializeAcl(secDesc);
          var isAuthorized = false;
  
          foreach (AceStruct ace in acl)
              if (UserHasAccessToPrincipalName(userName, ace.PrincipalName))
              {
                  foreach (FolderOperation aclOperation in ace.FolderOperations)
                      if (aclOperation == operation)
                          isAuthorized = true;
  
              }
  
          LogAccessCheck(userName, operation.ToString(), true);
          return isAuthorized;
      }
  
      public bool CheckAccess(string userName, IntPtr userToken, byte[] secDesc, ResourceOperation operation)
      {
          if (UserIsAdmin(userName))
          {
              LogAccessCheck(userName, operation.ToString(), true);
              return true;
          }
  
          var acl = DeserializeAcl(secDesc);
          var isAuthorized = false;
  
          foreach (AceStruct ace in acl)
              if (UserHasAccessToPrincipalName(userName, ace.PrincipalName))
                  foreach (ResourceOperation aclOperation in ace.ResourceOperations)
                      if (aclOperation == operation)
                          isAuthorized = true;
  
          LogAccessCheck(userName, operation.ToString(), true);
          return isAuthorized;
      }
  
      public bool CheckAccess(string userName, IntPtr userToken, byte[] secDesc, DatasourceOperation operation)
      {
          if (UserIsAdmin(userName))
          {
              LogAccessCheck(userName, operation.ToString(), true);
              return true;
          }
  
          var acl = DeserializeAcl(secDesc);
          var isAuthorized = false;
  
          foreach (AceStruct ace in acl)
              if (UserHasAccessToPrincipalName(userName, ace.PrincipalName))
                  foreach (DatasourceOperation aclOperation in ace.DatasourceOperations)
                      if (aclOperation == operation)
                          isAuthorized = true;
  
          LogAccessCheck(userName, operation.ToString(), true);
          return isAuthorized;
      }
  
      public bool CheckAccess(string userName, IntPtr userToken, byte[] secDesc, CatalogOperation[] operations)
      {
          Logger.Debug($"UserName: {userName}, {string.Join(",", operations)}");
  
          foreach (CatalogOperation operation in operations)
              if (!CheckAccess(userName, userToken, secDesc, operation))
                  return false;
  
          return true;
      }
  
      public bool CheckAccess(string userName, IntPtr userToken, byte[] secDesc, FolderOperation[] operations)
      {
          Logger.Debug($"UserName: {userName}, {string.Join(",", operations)}");
  
          foreach (FolderOperation operation in operations)
              if (!CheckAccess(userName, userToken, secDesc, operation))
                  return false;
  
          return true;
      }
  
      public bool CheckAccess(string userName, IntPtr userToken, byte[] secDesc, ResourceOperation[] operations)
      {
          Logger.Debug($"UserName: {userName}, {string.Join(",", operations)}");
  
          if (UserIsAdmin(userName))
              return true;
  
          foreach (ResourceOperation operation in operations)
              if (!CheckAccess(userName, userToken, secDesc, operation))
                  return false;
  
          return true;
      }
  
      /// &lt;summary&gt;
      /// Returns the set of permissions a specific user has for a specific item managed in the report 
      /// server database. This provides underlying support for the Web service method GetPermissions().
      /// &lt;/summary&gt;
      /// &lt;param name="userName"&gt;The name of the user as returned by the GetUserInfo method.&lt;/param&gt;
      /// &lt;param name="userToken"&gt;Pointer to the user ID returned by GetUserInfo.&lt;/param&gt;
      /// &lt;param name="itemType"&gt;The type of item for which the permissions are returned.&lt;/param&gt;
      /// &lt;param name="secDesc"&gt;The security descriptor associated with the item.&lt;/param&gt;
      /// &lt;returns&gt;&lt;/returns&gt;
      public StringCollection GetPermissions(string userName, IntPtr userToken, SecurityItemType itemType, byte[] secDesc)
      {
          var permissions = new List&lt;string&gt;();
  
          if (UserIsAdmin(userName))
          {
              foreach (CatalogOperation oper in _catOperNames.Keys)
                  if (!permissions.Contains((string)_catOperNames[oper]))
                      permissions.Add((string)_catOperNames[oper]);
  
              foreach (ModelItemOperation oper in _modelItemOperNames.Keys)
                  if (!permissions.Contains((string)_modelItemOperNames[oper]))
                      permissions.Add((string)_modelItemOperNames[oper]);
  
              foreach (ModelOperation oper in _modelOperNames.Keys)
                  if (!permissions.Contains((string)_modelOperNames[oper]))
                      permissions.Add((string)_modelOperNames[oper]);
  
              foreach (CatalogOperation oper in _catOperNames.Keys)
                  if (!permissions.Contains((string)_catOperNames[oper]))
                      permissions.Add((string)_catOperNames[oper]);
  
              foreach (ReportOperation oper in _rptOperNames.Keys)
                  if (!permissions.Contains((string)_rptOperNames[oper]))
                      permissions.Add((string)_rptOperNames[oper]);
  
              foreach (FolderOperation oper in _fldOperNames.Keys)
                  if (!permissions.Contains((string)_fldOperNames[oper]))
                      permissions.Add((string)_fldOperNames[oper]);
  
              foreach (ResourceOperation oper in _resOperNames.Keys)
                  if (!permissions.Contains((string)_resOperNames[oper]))
                      permissions.Add((string)_resOperNames[oper]);
  
              foreach (DatasourceOperation oper in _dsOperNames.Keys)
                  if (!permissions.Contains((string)_dsOperNames[oper]))
                      permissions.Add((string)_dsOperNames[oper]);
  
              Logger.Debug($"Permissions for administrator {userName}:\n{string.Join("\n", permissions)}");
          }
          else
          {
              var msg = $"Deserialized security descriptor for username {userName}\nSecurityItemType: {itemType}\n";
  
              AceCollection acl = DeserializeAcl(secDesc);
              foreach (AceStruct ace in acl)
              {
                  msg += $"\nPrincipal name: {ace.PrincipalName}\n";
  
                  if (UserHasAccessToPrincipalName(userName, ace.PrincipalName))
                  {
                      foreach (ModelItemOperation aclOperation in ace.ModelItemOperations)
                      {
                          msg += $" - ModelItemOperation {aclOperation}\n";
                          if (!permissions.Contains((string)_modelItemOperNames[aclOperation]))
                              permissions.Add((string)_modelItemOperNames[aclOperation]);
                      }
  
                      foreach (ModelOperation aclOperation in ace.ModelOperations)
                      {
                          msg += $" - ModelOperation {aclOperation}\n";
                          if (!permissions.Contains((string)_modelOperNames[aclOperation]))
                              permissions.Add((string)_modelOperNames[aclOperation]);
                      }
  
                      foreach (CatalogOperation aclOperation in ace.CatalogOperations)
                      {
                          msg += $" - CatalogOperation {aclOperation}\n";
                          if (!permissions.Contains((string)_catOperNames[aclOperation]))
                              permissions.Add((string)_catOperNames[aclOperation]);
                      }
  
                      foreach (ReportOperation aclOperation in ace.ReportOperations)
                      {
                          msg += $" - ReportOperation {aclOperation}\n";
                          if (!permissions.Contains((string)_rptOperNames[aclOperation]))
                              permissions.Add((string)_rptOperNames[aclOperation]);
                      }
  
                      foreach (FolderOperation aclOperation in ace.FolderOperations)
                      {
                          msg += $" - FolderOperation {aclOperation}\n";
                          if (!permissions.Contains((string)_fldOperNames[aclOperation]))
                              permissions.Add((string)_fldOperNames[aclOperation]);
                      }
  
                      foreach (ResourceOperation aclOperation in ace.ResourceOperations)
                      {
                          msg += $" - ResourceOperation {aclOperation}\n";
                          if (!permissions.Contains((string)_resOperNames[aclOperation]))
                              permissions.Add((string)_resOperNames[aclOperation]);
                      }
  
                      foreach (DatasourceOperation aclOperation in ace.DatasourceOperations)
                      {
                          msg += $" - DatasourceOperation {aclOperation}\n";
                          if (!permissions.Contains((string)_dsOperNames[aclOperation]))
                              permissions.Add((string)_dsOperNames[aclOperation]);
                      }
                  }
              }
  
              Logger.Debug(msg);
          }
  
          var sc = new StringCollection();
          sc.AddRange(permissions.ToArray());
          return sc;
      }
  
      /// &lt;summary&gt;
      /// Used to deserialize the ACL that is stored by the report server.
      /// &lt;/summary&gt;
      private AceCollection DeserializeAcl(byte[] secDesc)
      {
          AceCollection acl = new AceCollection();
          if (secDesc != null)
          {
              var bf = new BinaryFormatter();
              using (var sdStream = new MemoryStream(secDesc))
                  acl = (AceCollection)bf.Deserialize(sdStream);
          }
          return acl;
      }
  
      /// &lt;summary&gt;
      /// Utility method used to create mappings to the various operations in Reporting Services.
      /// These mappings support the implementation of the GetPermissions method.
      /// &lt;/summary&gt;
      private static void InitializeMaps()
      {
          // Create model operation names data
          _modelItemOperNames = new Hashtable
          {
              {ModelItemOperation.ReadProperties, OperationNames.OperReadProperties}
          };
  
          // Model item name mismatch
          if (_modelItemOperNames.Count != NrModelItemOperations)
              throw new Exception("Number of operation names don't match.");
  
          // Create model operation names data
          _modelOperNames = new Hashtable
          {
              {ModelOperation.Delete, OperationNames.OperDelete},
              {ModelOperation.ReadAuthorizationPolicy, OperationNames.OperReadAuthorizationPolicy},
              {ModelOperation.ReadContent, OperationNames.OperReadContent},
              {ModelOperation.ReadDatasource, OperationNames.OperReadDatasources},
              {ModelOperation.ReadModelItemAuthorizationPolicies, OperationNames.OperReadModelItemSecurityPolicies},
              {ModelOperation.ReadProperties, OperationNames.OperReadProperties},
              {ModelOperation.UpdateContent, OperationNames.OperUpdateContent},
              {ModelOperation.UpdateDatasource, OperationNames.OperUpdateDatasources},
              {ModelOperation.UpdateDeleteAuthorizationPolicy, OperationNames.OperUpdateDeleteAuthorizationPolicy},
              {
                  ModelOperation.UpdateModelItemAuthorizationPolicies,
                  OperationNames.OperUpdateModelItemSecurityPolicies
              },
              {ModelOperation.UpdateProperties, OperationNames.OperUpdatePolicy}
          };
  
          // Model name mismatch
          if (_modelOperNames.Count != NrModelOperations)
              throw new Exception("Number of operation names don't match.");
  
          // Create operation names data
          _catOperNames = new Hashtable
          {
              {CatalogOperation.CreateRoles, OperationNames.OperCreateRoles},
              {CatalogOperation.DeleteRoles, OperationNames.OperDeleteRoles},
              {CatalogOperation.ReadRoleProperties, OperationNames.OperReadRoleProperties},
              {CatalogOperation.UpdateRoleProperties, OperationNames.OperUpdateRoleProperties},
              {CatalogOperation.ReadSystemProperties, OperationNames.OperReadSystemProperties},
              {CatalogOperation.UpdateSystemProperties, OperationNames.OperUpdateSystemProperties},
              {CatalogOperation.GenerateEvents, OperationNames.OperGenerateEvents},
              {CatalogOperation.ReadSystemSecurityPolicy, OperationNames.OperReadSystemSecurityPolicy},
              {CatalogOperation.UpdateSystemSecurityPolicy, OperationNames.OperUpdateSystemSecurityPolicy},
              {CatalogOperation.CreateSchedules, OperationNames.OperCreateSchedules},
              {CatalogOperation.DeleteSchedules, OperationNames.OperDeleteSchedules},
              {CatalogOperation.ReadSchedules, OperationNames.OperReadSchedules},
              {CatalogOperation.UpdateSchedules, OperationNames.OperUpdateSchedules},
              {CatalogOperation.ListJobs, OperationNames.OperListJobs},
              {CatalogOperation.CancelJobs, OperationNames.OperCancelJobs},
              {CatalogOperation.ExecuteReportDefinition, OperationNames.ExecuteReportDefinition}
          };
  
          // Catalog name mismatch
          if (_catOperNames.Count != NrCatOperations)
              throw new Exception("Number of operation names don't match.");
  
          _fldOperNames = new Hashtable
          {
              {FolderOperation.CreateFolder, OperationNames.OperCreateFolder},
              {FolderOperation.Delete, OperationNames.OperDelete},
              {FolderOperation.ReadProperties, OperationNames.OperReadProperties},
              {FolderOperation.UpdateProperties, OperationNames.OperUpdateProperties},
              {FolderOperation.CreateReport, OperationNames.OperCreateReport},
              {FolderOperation.CreateResource, OperationNames.OperCreateResource},
              {FolderOperation.ReadAuthorizationPolicy, OperationNames.OperReadAuthorizationPolicy},
              {FolderOperation.UpdateDeleteAuthorizationPolicy, OperationNames.OperUpdateDeleteAuthorizationPolicy},
              {FolderOperation.CreateDatasource, OperationNames.OperCreateDatasource},
              {FolderOperation.CreateModel, OperationNames.OperCreateModel}
          };
  
          // Folder name mismatch
          if (_fldOperNames.Count != NrFldOperations)
              throw new Exception("Number of operation names don't match.");
  
          _rptOperNames = new Hashtable
          {
              {ReportOperation.Delete, OperationNames.OperDelete},
              {ReportOperation.ReadProperties, OperationNames.OperReadProperties},
              {ReportOperation.UpdateProperties, OperationNames.OperUpdateProperties},
              {ReportOperation.UpdateParameters, OperationNames.OperUpdateParameters},
              {ReportOperation.ReadDatasource, OperationNames.OperReadDatasources},
              {ReportOperation.UpdateDatasource, OperationNames.OperUpdateDatasources},
              {ReportOperation.ReadReportDefinition, OperationNames.OperReadReportDefinition},
              {ReportOperation.UpdateReportDefinition, OperationNames.OperUpdateReportDefinition},
              {ReportOperation.CreateSubscription, OperationNames.OperCreateSubscription},
              {ReportOperation.DeleteSubscription, OperationNames.OperDeleteSubscription},
              {ReportOperation.ReadSubscription, OperationNames.OperReadSubscription},
              {ReportOperation.UpdateSubscription, OperationNames.OperUpdateSubscription},
              {ReportOperation.CreateAnySubscription, OperationNames.OperCreateAnySubscription},
              {ReportOperation.DeleteAnySubscription, OperationNames.OperDeleteAnySubscription},
              {ReportOperation.ReadAnySubscription, OperationNames.OperReadAnySubscription},
              {ReportOperation.UpdateAnySubscription, OperationNames.OperUpdateAnySubscription},
              {ReportOperation.UpdatePolicy, OperationNames.OperUpdatePolicy},
              {ReportOperation.ReadPolicy, OperationNames.OperReadPolicy},
              {ReportOperation.DeleteHistory, OperationNames.OperDeleteHistory},
              {ReportOperation.ListHistory, OperationNames.OperListHistory},
              {ReportOperation.ExecuteAndView, OperationNames.OperExecuteAndView},
              {ReportOperation.CreateResource, OperationNames.OperCreateResource},
              {ReportOperation.CreateSnapshot, OperationNames.OperCreateSnapshot},
              {ReportOperation.ReadAuthorizationPolicy, OperationNames.OperReadAuthorizationPolicy},
              {ReportOperation.UpdateDeleteAuthorizationPolicy, OperationNames.OperUpdateDeleteAuthorizationPolicy},
              {ReportOperation.Execute, OperationNames.OperExecute},
              {ReportOperation.CreateLink, OperationNames.OperCreateLink}
          };
  
          // Report name mismatch
          if (_rptOperNames.Count != NrRptOperations)
              throw new Exception("Number of operation names don't match.");
  
          _resOperNames = new Hashtable
          {
              {ResourceOperation.Delete, OperationNames.OperDelete},
              {ResourceOperation.ReadProperties, OperationNames.OperReadProperties},
              {ResourceOperation.UpdateProperties, OperationNames.OperUpdateProperties},
              {ResourceOperation.ReadContent, OperationNames.OperReadContent},
              {ResourceOperation.UpdateContent, OperationNames.OperUpdateContent},
              {ResourceOperation.ReadAuthorizationPolicy, OperationNames.OperReadAuthorizationPolicy},
              {ResourceOperation.UpdateDeleteAuthorizationPolicy, OperationNames.OperUpdateDeleteAuthorizationPolicy}
          };
  
          // Resource name mismatch
          if (_resOperNames.Count != NrResOperations)
              throw new Exception("Number of operation names don't match.");
  
          _dsOperNames = new Hashtable
          {
              {DatasourceOperation.Delete, OperationNames.OperDelete},
              {DatasourceOperation.ReadProperties, OperationNames.OperReadProperties},
              {DatasourceOperation.UpdateProperties, OperationNames.OperUpdateProperties},
              {DatasourceOperation.ReadContent, OperationNames.OperReadContent},
              {DatasourceOperation.UpdateContent, OperationNames.OperUpdateContent},
              {DatasourceOperation.ReadAuthorizationPolicy, OperationNames.OperReadAuthorizationPolicy},
              {
                  DatasourceOperation.UpdateDeleteAuthorizationPolicy,
                  OperationNames.OperUpdateDeleteAuthorizationPolicy
              }
          };
  
          // Datasource name mismatch
          if (_dsOperNames.Count != NrDSOperations)
              throw new Exception("Number of operation names don't match.");
      }
  
      /// &lt;summary&gt;
      /// You must implement SetConfiguration as required by IExtension
      /// &lt;/summary&gt;
      /// &lt;param name="configuration"&gt;Configuration data as an XML string that is stored along with the Extension element in the configuration file.&lt;/param&gt;
      public void SetConfiguration(string configuration)
      {
          // Retrieve admin user and password from the config settings and verify
          var doc = new XmlDocument();
  
          doc.LoadXml(configuration);
  
          if (doc.DocumentElement != null &amp;&amp; doc.DocumentElement.Name == "AdminConfiguration")
          {
              foreach (XmlNode child in doc.DocumentElement.ChildNodes)
              {
                  if (child.Name == "UserName")
                      AdminUserNames.Add(child.InnerText);
                  else
                      throw new Exception("Unrecognized configuration element.");
              }
          }
          else
              throw new Exception("Root element is not 'AdminConfiguration' in config data.");
      }
  
      public string LocalizedName =&gt; null;
  }
  </code></pre>