#c# #asp.net #.net #reporting-services #sql-server-2016
#c# #asp.net #.net #службы отчетов #sql-server-2016
Вопрос:
В нашей текущей реализации SSRS все пользователи, кроме администраторов, имеют одну и ту же роль пользователя, поскольку нам так и не удалось выяснить, как получить роли пользователей из базы данных.
Независимо от того, как мы пытаемся выполнить вызов базы данных для access_rules, расширение перестает работать, в результате чего пользователи не могут получить доступ ни к чему. Нет даже ресурса ‘mids’.
Возможно ли вообще получить доступ к внешнему ресурсу из IAuthorizationExtension? Если да, кто-нибудь знает, как это сделать?
Вот что мы пытаемся в настоящее время:
public class AuthorizationExtension : IAuthorizationExtension
{
private static readonly IList<string> AdminUserNames = new List<string>();
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<string>(sql, new { userName }).ToArray();
userRoles = userRoles.Concat(namespaces).ToArray();
}
// Admin role check
if (userRoles.Contains("*"))
return true;
return userRoles.Contains(principalName, StringComparer.CurrentCultureIgnoreCase);
}
/// <summary>
/// Returns a security descriptor that is stored with an individual item in the report server database.
/// </summary>
/// <param name="acl">The access code list (ACL) created by the report server for the item. It contains a collection of access code entry (ACE) structures.</param>
/// <param name="itemType">The type of item for which the security descriptor is created.</param>
/// <param name="stringSecDesc">Optional. A user-friendly description of the security descriptor, used for debugging. This is not stored by the report server.</param>
/// <returns>Should be implemented to return a serialized access code list for the item.</returns>
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}");
}
/// <summary>
/// Indicates whether a given user is authorized to access the item for a given catalog operation.
/// </summary>
/// <param name="userName">The name of the user as returned by the GetUserInfo method.</param>
/// <param name="userToken">Pointer to the user ID returned by GetUserInfo.</param>
/// <param name="secDesc">The security descriptor returned by CreateSecurityDescriptor.</param>
/// <param name="operation">The operation being requested by the report server for a given user.</param>
/// <returns>True if the user is authorized.</returns>
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;
}
/// <summary>
/// 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().
/// </summary>
/// <param name="userName">The name of the user as returned by the GetUserInfo method.</param>
/// <param name="userToken">Pointer to the user ID returned by GetUserInfo.</param>
/// <param name="itemType">The type of item for which the permissions are returned.</param>
/// <param name="secDesc">The security descriptor associated with the item.</param>
/// <returns></returns>
public StringCollection GetPermissions(string userName, IntPtr userToken, SecurityItemType itemType, byte[] secDesc)
{
var permissions = new List<string>();
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;
}
/// <summary>
/// Used to deserialize the ACL that is stored by the report server.
/// </summary>
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;
}
/// <summary>
/// Utility method used to create mappings to the various operations in Reporting Services.
/// These mappings support the implementation of the GetPermissions method.
/// </summary>
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.");
}
/// <summary>
/// You must implement SetConfiguration as required by IExtension
/// </summary>
/// <param name="configuration">Configuration data as an XML string that is stored along with the Extension element in the configuration file.</param>
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 => null;
}
Ответ №1:
Я предполагаю, что _connectionstring никогда не получает значения. Большинство функций безопасности в SSRS будут просматривать machine.config или rsreportserver.config, но не web.config.
Комментарии:
1. К сожалению, это не так. У меня строка подключения жестко запрограммирована в моих тестовых файлах. Я просто заменил это на _connectionString здесь, чтобы скрыть это.
Ответ №2:
/facepalm
Пользователь домена сервера не имел доступа к базе данных.