How can I find the role required when PrincipalPermissionAttribute demand fails? - c#

Within a web site I'm using the PrincipalPermission attribute to restrict access to certain methods.
Here's a canoncial example:
class Program
{
static void Main(string[] args)
{
Foo();
}
[System.Security.Permissions.PrincipalPermission(System.Security.Permissions.SecurityAction.Demand, Role = "Winners")]
static void Foo()
{ }
}
If the principal isn't in the role specified then the .net infrastructure throws a System.Security.SecurityException with the generic message "Request for principal permission failed".
If the permission check fails I'd like to log what role the PrincipalPermission required. This will be really useful for our support staff who can then either assign the role to the user and/or monitor the logs to see if anything suspicious is happening. Clearly for security purposes the end user will still see a generic unauthorised message. I've trawled through the SecurityException itself but it doesn't have the "Role" anywhere in it.
Is there anyway to get this information?

If your code is fully trusted, you can extract the required information by parsing the XML representation of the SecurityException's FirstPermissionThatFailed property. However, for your second purpose of detecting "suspicious" activity, it might be preferable to simply log the return value of the SecurityException's ToString() method. This will include both the details of the denied PrincipalPermission and the stack trace where the exception occurred. The context given by the stack trace is likely to be very useful, assuming that the support staff in question understand how to relate it to your application structure.

Related

Microsoft Bot Framework Multi-Tenant Credentials C#

Background
I've created a working bot in C# but I'm failing to expand it to be a multi-tenant bot. I have created multiple bots in the Microsoft portal using this technique to identify themselves from the messaging endpoint:
https://example.com/api/messages/bot1
https://example.com/api/messages/bot2
https://example.com/api/messages/bot3
I can grab the LastSegment from the URL while in the MessagesController and store it in PrivateConversationData so I know which bot is talking in the current conversation. I intended use this stored 'bot id' in order to retrieve the Microsoft AppId & Password from the web.config (the bot's credentials are stored as a series of custom entries and not the standard appSettings as that only works for a single bot).
Credentials Problem
The authentication works well (nearly) as described here except when using async code with .ConfigureAwait(false) I can't get the HttpContext.Current as it becomes null when running on a different thread. This means I can't get the authenticated user's credentials either by looking them up in the web.config or by calling GetCredentialsFromClaims() since I've lost the authenticated user. If I use .ConfigureAwait(true) I just get deadlocks all over the place.
I have the credentials in the web.config but they are stored per bot and I need the 'bot id' from the URL above in order to get the credentials.
Question
The crux of the problem is: I need the URL to get the 'bot id' and I need the 'bot id' to get the credentials from the web.config but I can never reliably get access to the URL once I've passed a .ConfigureAwait(false) in the code. On the flip side, I can't get the 'bot id' from the PrivateConversationData since I need the bot's credentials in order to load it. A bit chicken and egg :-(
If anyone has any ideas of what I may be doing wrong or has an alternative approach to know which 'bot id' is currently executing I'd very much appreciate it.
Thanks
Please find below given the sample code.
public class StartUp {
public void Configuration(IAppBuilder app) {
var builder = new ContainerBuilder();
//Note: Initialize / register the Metadata Service that can bring the tenant details from the corresponding store
builder.RegisterType<TenantMetadataService>().As<ITenantMetadataService>();
//Note: This helps you in accessing the TenantMetadata from any constructor going forward after the below registry
builder.Register(ti => TenantMetadata.GetTenantMetadataFromRequest()).InstancePerRequest();
//TODO: Register the various services / controllers etc which may require the tenant details here
}
}
public class TenantMetadata {
public Guid TenantId { get;set; }
public Uri TenantUrl { get;set; }
public string TenantName { get;set; }
public static TenantMetadata GetTenantMetadataFromRequest() {
var context = HttpContext.Current;
//TODO: If you have any header like TenantId coming from the request, you can read and use it
var tenantIdFromRequestHeader = "";
//TODO: There will be a lazy cache that keeps building the data as new tenant's login or use the application
if(TenantCache.Contains(...))return TenantCache[Key];
//TODO: Do a look-up from the above step and then construct the metadata
var tenantMetadata = metadataSvc.GetTenantMetadata(...);
//TODO: If the data match does not happen from the Step2, build the cache and then return the value.
TenantCache.Add(key,tenantMetadata);
return tenantMetadata;
}
}
Note
The above code snippet uses the various service placeholders, cache and the other methods which will require to be used based on the designed application services. If you wish not to cache the tenant metadata, if it may contain some sensitive data, you can remove the caching implementation parts.
This implementation can be spread across all your web facing portals like your Web UI, Web Api and WebJobs etc so that it is same across all apps and it is easy to test and consume.
HTH.

Abp Tenant creation not working. AbpAuthorizationException at CreateStaticRoles

After update to Abp 2.3 my tenant creation is not working anymore.
The exception is been throwing when the roles are created:
//TenantAppService.cs
//We are working entities of new tenant, so changing tenant filter
using (CurrentUnitOfWork.SetTenantId(tenant.Id))
{
//Create static roles for new tenant
CheckErrors(await _roleManager.CreateStaticRoles(tenant.Id)); <-- Here
The exception:
Exception thrown: 'Abp.Authorization.AbpAuthorizationException' in
mscorlib.dll
Additional information: [At least one of these permissions must be granted]
I didn't change the TenantAppService that came from Module Zero, but if I remove the AbpAuthorize from class it works.
[AbpAuthorize(PermissionNames.Pages_Tenants)] //If removed works
public class TenantAppService : SeducaAppServiceBase, ITenantAppService
{
...
Tks.
The problem was AbpCache.
What I need to do is restart the app (cleaning everything) or call the api method (api/AbpCache/ClearAll)
reference: https://github.com/aspnetboilerplate/aspnetboilerplate/issues/573
It's solves all my problems with permissions
The user who is calling this method has to be granted permission with PermissionNames.Pages_Tenants.

How to pass current context to an object c#

I am creating an application where I first login with my user account. This user account could be windows or self managed account in my own application database.
Now I want to authorize the logged in user before accessing any business objects of my application. Objects are mapped with database tables so eventually I want to authorize user first, whether to give data back to user or not.
After logging in I store user credentials globally as an object of UserCredential class. But I don't want to pass this credentials to each object when I am creating it.
Is there any way to check/reach the application context (including UserCredential object I stored globally) for each business objects automatically which I am creating further?
I want to achieve this in C#. Code example is much appreciated.
You should take a look at the PrincipalPermissionAttribute class, here is the MSDN documentation:
PrincipalPermissionAttribute class MSDN documentation
The PrincipalPermissionAttribute throws a SecurityException when the Thread.CurrentPrincipal does not match the security assertion.
Examples:
User's name is GDroid:
[PrincipalPermission(SecurityAction.Demand, Name = "GDroid")]
public void YourBusinessMethod()
{
// Do something
}
User belongs to Admin role:
[PrincipalPermission(SecurityAction.Demand, Role = "Admin")]
public void YourBusinessMethod()
{
// Do something
}
User is authenticated:
[PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
public void YourBusinessMethod()
{
// Do something
}

Inmutable values mutating in IIS?

So, I've been trying to figure out the following problem for the past few weeks, and at this point I'm almost exhausting my options because how contradictory the situation seems.
I have an application which is developed to work under SharePoint but it's basically ASP.NET code. I have an encrypted connection string which I decrypt it in memory and store it in a configuration object to access the database. My configuration object is static (accesible through a Service Locator pattern), which I later use to seed a LINQ-to-SQL data context.
My internal key for decryption is stored, privately in a class as private static readonly string myPassword = "MyPassword"; (just an example, the actual password is more complex and valid). There's no single statement, anywhere, referencing that field, except one on a static method using it as a parameter for another decryption method (instance method), which instantiates a new DESCryptoServiceProvider with it.
And still, I get the following exception from time to time in my production server logs:
Exception type: CryptographicException
Exception message: Specified key is a known weak key for 'DES' and cannot be used.
As such, the connection string decryption fails and, of course, the database is not accessed anymore. Poof, application down.
How is this even possible?
Disclaimer: This is an old application I am maintaining. The description I provide here is to help troubleshoot, but I cannot change the way it works internally. Some will agree that this is not the best approach but the application has been running without a problem for more than 2 years and suddenly these exceptions are taking it down.
Update: I've been requested to clarify with a stack trace of the exception, but I cannot provide one full stack trace for NDA reasons. What I can tell is the following:
The object throwing the exception is the System.Security.DESCryptoServiceProvider.CreateDecryptor(Byte[] rgbKey, Byte[] rgbIV) method
The original key (the one we actually use) does validate and does not generate an exception. Still, we get this exception from time to time (not always), without knowing which is the current value which does not validate
The instance of the DESCryptoServiceProvider is stored statically, privately, in a helper class
This is all triggered by System.Web.HttpApplication.InitModulesCommon(), to initialize the application internal parts
Also, here is an obscured stack trace:
at System.Security.Cryptography.DESCryptoServiceProvider.CreateDecryptor(Byte[] rgbKey, Byte[] rgbIV)
at SymmetricEncryption.Decrypt(String contents, String key)
// our helper, just a wrapper, based from this class: http://www.codeproject.com/Articles/1967/Encryption-Decryption-with-NET
at EncryptedConnectionStringHelper.DecryptUserAndPass(String connectionString)\
// our container for parsing the connection string and decrypting the user and password, not the full connstring is encrypted
at OurModule.Init(OurConfigurationSection config)
at OurModule.Boot(OurConfigurationSection config)
at OurModule.Boot()
at OurModule.Init(HttpApplication context)
at System.Web.HttpApplication.InitModulesCommon()
at System.Web.HttpApplication.InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers)
at System.Web.HttpApplicationFactory.GetNormalApplicationInstance(HttpContext context)
at System.Web.HttpApplicationFactory.GetApplicationInstance(HttpContext context)
at System.Web.HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)
Our application registers this module in the following way:
public class OurModule : IHttpModule
{
public static bool initialized = false;
public void Init(HttpApplication context)
{
if (!initialized) {
subscribe(context);
OurModule.Boot();
initialized = true;
}
}
Have a look at your wrapper SymmetricEncryption.Decrypt. My guess would be that the issue is in there. How it creates the Key from your password. Does it use PasswordDeriveBytes or some other half baked solution?
Failing that maybe you could try get a better key than "MyPassword".
Failing that maybe you could use web.config encryption. Scott Gu wrote about it here.
It doesn't sound like anything's mutating the object. It sounds like "something" (if you'd posted the stack trace it would be clearer) is validating the DES key... and complaining that it's a known weak key.
Ideally, you should change your password to be more secure, of course - but if you can't, you should look at exactly where that exception's coming from, and see if there are settings somewhere controlling how and when it's validated.
If you're not already logging the full stack trace (instead of just the exception message) that's the first thing you should do.

Authorization in a more purely OOP style

I've never seen this done but I had an idea of doing authorization in a more purely OO way. For each method that requires authorization we associate a delegate. During initialization of the class we wire up the delegates so that they point to the appropriate method (based on the user's rights). For example:
class User
{
private deleteMemberDelegate deleteMember;
public StatusMessage DeleteMember(Member member)
{
if(deleteMember != null) //in practice every delegate will point to some method, even if it's an innocuous one that just reports 'Access Denied'
{
deleteMember(member);
}
}
//other methods defined similarly...
User(string name, string password) //cstor.
{
//wire up delegates based on user's rights.
//Thus we handle authentication and authorization in the same method.
}
}
This way the client code never has to explictly check whether or not a user is in a role, it just calls the method. Of course each method should return a status message so that we know if and why it failed.
Thoughts?
This is basically the null object pattern for authorization. It's an interesting idea if you can figure out a way to design StatusMessage such that the calling code doesn't need special cases. For instance, for certain actions, you'll want to indicate "You can't do that as a guest, but would you like to login or sign up for an account?" So certain StatusMessages might need to redirect to a login/sign up page.

Categories