在当前的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 && 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;
}
答案 0 :(得分:0)
我的猜测是_connectionstring永远不会获得值。 SSRS中的大多数安全功能将查看machine.config或 rsreportserver.config,而不是web.config。
答案 1 :(得分:0)
/ facepalm
服务器的域用户无权访问数据库。