Converting Word to PDF using Interop requires admin rights - c#

First off, I know that this falls into the category of "not recommended practice", but I have a requirement to convert Word documents to PDF, via an ASP.Net site, and I have been using the Word Interop as it is free, simple to implement and Word is already installed on the server.
When I have been testing, it is working for me but after publishing users are reporting "Unauthorised access" errors. I have admin rights to the server and I found that adding the users as admin on the server works but I do not want to grant admin rights to every user.
So one of a number of things needs to happen, is there an alternative, free, library for converting a Word document to PDF that I could be using? Is there an easy way to get my users access to the Interop library without giving admin rights? Is there a way to impersonate an admin user for this part of the web application, seeing as the application requires Windows Authentication?
Is there anything else I have not thought of that could benefit me?

You can use impersonation to run code as a different user. There is a good impersonation class available on CodeProject here, http://www.codeproject.com/Articles/10090/A-small-C-Class-for-impersonating-a-User.
Just create a new admin account on the machine with access to do what you want, store the credentials in the web.config in app settings, (encrypt them if you want to with rijandael and decrypt them with code, etc.
But long store short, you use it like this,
using ( new Impersonator( "myUsername", "myDomainname", "myPassword" ) )
{
//This code is running elevated as the specified user
}
//This code is reverted back to the previous user.
I actually wrote my own web.config ConfigurationElement for storing credentials. It looks like this,
public class CredentialConfigurationElement : ConfigurationElement
{
#region Properties
[ConfigurationProperty("userName", DefaultValue="", IsRequired=true)]
public string UserName
{
get { return (string)this["userName"];}
set { this["userName"] = value; }
}
[ConfigurationProperty("password", DefaultValue = "", IsRequired = true)]
public string Password
{
get { return (string)this["password"]; }
set { this["password"] = value; }
}
#endregion
#region Explicit Operators
public static implicit operator NetworkCredential(CredentialConfigurationElement value)
{
return new NetworkCredential(value.UserName, value.Password);
}
public static implicit operator CredentialConfigurationElement(NetworkCredential value)
{
return new CredentialConfigurationElement() { UserName = value.UserName, Password = value.Password };
}
#endregion
}
However, to use it, you need to make a Custom ConfigurationSection, a class that inherits from ConfigurationSection and exposes the CredentialConfigurationElement as a property.
E.g. you could make a new section called CodeImpersonatorSection : ConfigurationSection
And in there expose the CredentialConfigurationElement as a property called ImpersonationCredentials.
Then use (CodeImpersonatorSection)WebConfigurationManager.GetSection("/yoursectionnamehere"); to get an instance to the configuration.
Optionally modify the Impersonator class to do that automatically, and change it to have a static method like
Impersonator.RunUnderImpersonation(p => {
//This code is impersonating the configured user.
});

Related

ASP.NET Combining Windows Authentication with Custom Application Groups/Roles

Full disclosure, I do not fully understand the world of windows auth, active directory and LDAP and have had little experience with individual user accounts via sql server. Additionally, I have found that most documentation on the web, especially those put forth by Microsoft, assume that you develop in a pure Microsoft world and have the ability to implement the most current and any which solution, framework, or service they provide.
I am developing an intranet application. And for various reasons I cannot take advantage of Active Directory groups/roles but would like to mimic this functionality as much as possible. Thus I need to be able to manage users/roles/groups internally within the application. However, I would like the ability to be able to detect a users Windows Auth credentials in the process. In other words I do not want the user to have to register nor do I want them to have to log in but rather use the windows account they are signed in as.
The application managed roles would then determine various functionality the user will have within the application.
This is an asp.net MVC application. I will ultimately need to satisfy the following requirements in regards to authorization.
1) Compare current windows user with application user store and roles. Can use SQL server this.
2) Manipulate functionality based on a users role
3) allow for an admin to search AD and add a domain\User to the store as well as assign groups
4) Create Groups and register with application components
Any information as to how I could address on or all of these would be vastly beneficial.
What you are looking for is a custom role provider.
It is extremely easy and simple to do. Simply create a class that inherits from System.Web.Security.RoleProvider. The only methods you need to implement are IsUserInRole and GetRolesForUser.
You may just throw a NotImplementedException on all the other methods.
Then, tie it to your application in Web.Config by setting the roleManager element under System.Web.
public class CustomRoleProvider : RoleProvider
{
private mydatabase db;
public override string ApplicationName { get; set; }
public CustomRoleProvider()
{
db = new mydatabase();
}
public override bool IsUserInRole(string username, string roleName)
{
//This will return the user object.
//To get the username of the logged on user, you can use User.Identity.Name
//To remove the domain name from the username: User.Identity.Name.Split('\\').Last();
var user = db.CurrentUser();
return user.Roles != null
&& user.Roles.Count > 0
&& (user.Roles.Exists(x => x.Roles.RoleNm == roleName));
}
public override string[] GetRolesForUser(string username)
{
var user = db.CurrentUser();
return user.Roles.Select(x => x.Roles.RoleNm).ToArray();
}
#region not implemented
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override void CreateRole(string roleName)
{
throw new NotImplementedException();
}
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
{
throw new NotImplementedException();
}
public override string[] FindUsersInRole(string roleName, string usernameToMatch)
{
throw new NotImplementedException();
}
public override string[] GetAllRoles()
{
throw new NotImplementedException();
}
public override string[] GetUsersInRole(string roleName)
{
throw new NotImplementedException();
}
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override bool RoleExists(string roleName)
{
throw new NotImplementedException();
}
#endregion
}
and then, in Web.Config
<system.web>
<roleManager defaultProvider="CustomRoleProvider" enabled="true">
<providers>
<clear />
<add name="CustomRoleProvider" type="ThisProject.CustomRoleProvider, ThisProject" />
</providers>
</roleManager>
</system.web>
DISCLAIMER: There are likely typos in this code but you should be able to get the gyst
There is a membership provider created specifically for ActiveDirectory:
https://msdn.microsoft.com/en-us/library/system.web.security.activedirectorymembershipprovider(v=vs.110).aspx.
You can implement that provider in your app. In addition, you can create your own membership provider if you need additional functionality that the ActiveDirectoryMembershipProvider does not provide:
https://msdn.microsoft.com/en-us/library/f1kyba5e.aspx
Asp.Net Identity separates Identity and Authorization as two distinct components.
By design you can choose to use the AD identity piece with the Asp.Net Authorization piece. Such that you can use the local AD token to identify WHO the user is and then use that token to assign them privileges (roles and/or claims) based on that identity. Similar to how you can also use Google, Facebook or Twitter identities. Obviously, if your AD authorities won't allow you to query AD for "who is the user behind token X" then this answer is moot.
I haven't time to go further with this right now, but I think this should start you in the right direction.
(caveat: you MAY become limited to using a Microsoft browser. Last I looked only IE would send the Active Directory Token with the HttpRequest IF the request was being sent to a local domain server (aka the 'intranet' zone). I have heard that Chrome will allow you to configure it do this as well, but have never actually done it.)

Isolate users in signalR hub by domain

I have a web application that is a single IIS installation (This isn't changing), but has a dynamic collection of subdomains. Each subdomain has its own user accounts.
The problem I am running into is that when I run signalR on it, it treats all sub-domains as the same domain, so users who just so happen to have the same user name will get each others messages.
This is causing me an security violation issue between domain accounts.
So far my best guess solutions for this have different levels of risks and problems.
Each user gets their own group, build the group name with the sub-domain name + user name.
List item this minimizes the risk of collision but doesn't remove it.
Using a Guid for the domain name, and reserving the first n-characters for the guid reduces the risk even further, but now for each user online I now have a group formed.
On the owin start, spin up a new hub that represents each domain.
Each time I add a subdomain, I will have to restart the application to add the new hub. Right now, I don't have to do anything to add subdomains the DNS is supporting the wildcard, and the host header in IIS is blank. All works except for the lack of subdomain awareness in SignalR.
Build a custom hub class, that makes the client collection domain aware, like the rest of the application.
This seems to be the cleanest, but by far, most time consuming. It also poses the highest risk of bugs, since I will have to compose a larger collection of QA tests beyond the TDD unit testing.
Last option, don't use SignalR, build my own long poll API.
This is the hardest one to accept, since it is the highest bandwidth and most exposed process. A basic survey of our target users shows that they are using websocket supporting browsers, so why would we purposely increase bandwidth or create new latency.
To see this failure, just grab the simple chat demo at ASP.NET/SignalR, and run it on your local computer under two different browsers (FF and IE for my core tests), and have one call http:\localhost and the other call http:\yourcomputername. You will need IIS not IIS Express for a proper test.
My 2 cents: build your own implementation of IUserIdProvider, from there it should be easy to inspect each request and generate a unique user id across multiple domains which you would return, this way SignalR would know to whom to associate each request correctly. It'd be a simple and not invasive solution. You can check here for more detail.
I know this is a bit late, however I've also come across this issue and I've since solved it using groups, however they way I did it was to implement IHub myself and then when set Clients is called wrap the value in my own implementation of IHubCallerConnectionContext<dynamic> and then use a key to isolate all the calls made using the already available methods. Here's an example of what that class looked like:
internal class ClientsDatabaseIsolator : IHubCallerConnectionContext<object>
{
private readonly string _database;
private readonly IHubCallerConnectionContext<dynamic> _clients;
public ClientsDatabaseIsolator(string database, IHubCallerConnectionContext<dynamic> clients)
{
if (database == null) throw new ArgumentNullException(nameof(database));
this._database = database;
this._clients = clients;
}
private string PrefixDatabase(string group)
{
return string.Concat(_database, ".", group);
}
public dynamic AllExcept(params string[] excludeConnectionIds)
{
return _clients.Group(_database, excludeConnectionIds);
}
public dynamic Client(string connectionId)
{
return _clients.Client(connectionId);
}
public dynamic Clients(IList<string> connectionIds)
{
return _clients.Clients(connectionIds);
}
public dynamic Group(string groupName, params string[] excludeConnectionIds)
{
return _clients.Group(PrefixDatabase(groupName), excludeConnectionIds);
}
public dynamic Groups(IList<string> groupNames, params string[] excludeConnectionIds)
{
return _clients.Groups(groupNames.Select(PrefixDatabase).ToList(), excludeConnectionIds);
}
public dynamic User(string userId)
{
return _clients.User(userId);
}
public dynamic Users(IList<string> userIds)
{
return _clients.Users(userIds);
}
public dynamic All
{
get { return _clients.Group(_database); }
}
public dynamic OthersInGroup(string groupName)
{
return _clients.OthersInGroup(PrefixDatabase(groupName));
}
public dynamic OthersInGroups(IList<string> groupNames)
{
return _clients.OthersInGroups(groupNames.Select(PrefixDatabase).ToList());
}
public dynamic Caller
{
get { return _clients.Caller; }
}
public dynamic CallerState
{
get { return _clients.CallerState; }
}
public dynamic Others
{
get { return _clients.OthersInGroup(_database); }
}
}
then in OnConnected I add the connection to the _database group
now in my hub when I call Clients.All.Send("message") that will really just send messages to the group specified when the ClientsDatabaseIsolator was created, it would be like calling Clients.Group(database).Send("message") that way you don't have to think about it. I'm not sure if this is the best solution but it worked for us.

Custom membership provider + custom CodeAccessSecurityAttribute

I've been tasked to create a custommembership provider for our methods for our MVC 4.0 project.
Based on a attribute ([Authorize]?) it has to spot whether the attempted user is allowed to use the method or not.
Currently i've got this:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class TestAuthorize : CodeAccessSecurityAttribute
{
public TestAuthorize(SecurityAction action)
: base(action)
{
}
public override IPermission CreatePermission()
{
throw new NotImplementedException();
}
}
When I add
return new PrincipalPermission(null,null,true) I expect the permission to be valid and the user has access to the method.
When I add
return new PrincipalPermission(null.null,false) I expect the permission to be invalid and the user will be denied access to the method.
However, It only stops from continuing when I use a throw new SecurityException("You are denied access") which also forces the MVC application to stop unless this exception is handled at client side (using a try catch).
Is it possible for us to handle this exception in our MVC project?
as an example of what we wish to have done by use of attributes:
[TestAuthorize(SecurityAction.Demand)]
public List<string> GetList()
{
//if access, return new List();
//if no access, return null;
}
Pretty sure you want to be inheriting from AuthorizeAttribute here, not CodeAccessSecurityAttribute. In your attribute, you override AuthorizeCore and simply return true if the user should be allowed to continue, and false if they're not authorised to do whatever it is that method does. A false result will trigger a HTTP-401 Unauthorised response, which ASP.NET automatically handles by redirecting the user to the login page so they can log in as someone with the right access, although you can change this behaviour if you wish.
In fact, you might not even need to create your own attribute; if you're using the existing ASP.NET MVC mempership provider, or you can get whatever you're using to play nice with it, then the existing AuthorizeAttribute will work for you.

How do I secure a web app using active directory groups?

Sorry for the basic question, first time with Web MVC4 in C#...
I'm creating a web interface for an application I've written in C#/SQL. I've been able to attach the MVC4 framework to the SQL DB. Now I want to secure what people can do based on group membership in AD. I have the authentication in my web.config set to "Windows" and it properly displays the User.Identity.Name that i'm logged in with. So I know it's pulling up the current logged in user. More over, I need to be able to authenticate a user outside of the active directory domain in the case of an android or ipad device. I haven't gotten that far yet though... for the most part, I'd like to auto authenticate the logged in user if possible then prompt for a username/password if none exists.
Ok, also I already know how to pull group membership for a user in AD. But I need to run that AD query and store that information somewhere that can be accessed on each page. Then on each page how do I access that variable?
For example, I don't want to display a menu option if they don't have access to it so that variable needs to be used to either display or not display the menu option that's being secured. Also, I assume I need to add that security on the webpage as well so that if someone tries to go there manually they cannot.
I assume I don't want to use session variables for security reasons..
In the past with Adobe Flex I used a singleton to manage the session state. I did a search out there and people are saying that it's probably not a good idea in C#. Not many examples of this anyway...
What are you doing to do this?
Here is what I would recommend. Start looking for examples of the ActiveDirectoryMembershipProvider Class. This MembershipProvider combined with Forms Authentication will provide you with a secure system to authenticate users.
Once authenticated, you need to authorize your users to access resources by combining the Active Directory Role Provider(ADRP) (to determine User Groups) with the standard way of Securing your MVC Application.
To get you started I created these simple extension methods when you can extend to use the ADRP (as I haven't used the ADRP).
public static class IPrincipalExtensions
{
private static _adminName = "Administrator";
public static bool IsAnonymous(this IPrincipal instance)
{
return (instance == null);
}
public static bool IsAdminOrInRole(this IPrincipal instance, string role)
{
if (instance == null
|| instance.Identity == null
|| !instance.Identity.IsAuthenticated)
{
return false;
}
bool result = instance.IsInRole(role)
|| instance.IsInRole(IPrincipalExtensions._adminName));
return result;
}
}
Then I also extended the default AuthorizeAttibute to give me an attribute I can use solely for Administrators:
public class AuthorizeAdministratorAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
bool result = false;
IPrincipal user = httpContext.User;
if (user.Identity.IsAuthenticated)
{
result = user.IsAdmin();
}
return result;
}
}
This uses the same extension methods provided in my IPrincipalExtensions so I don't repeat myself. Some might find this overkill as the following two lines are equal:
[Authorize("Administrator")]
[AuthorizeAdministrator]
However, since the first example is using a string, a simple mistype denies access, and if I decided to change the role/group name to "Admins" it becomes more difficult. So using the second one (which I could argue is strongly typed) if the group changes, I only have to change the name in one location.

.net profile provider and custom profiles. The settings property '' was not found.

I've run into a bit of a pickle here. I have a custom profile that I used in two applications in the same solution. The first was a web application that I built to import a custom set of user profiles and attributes from an old .net application into the .net membership membership, roles, profile tables. I built a profile common class that inherits from profilebase. Both applications have a copy of the same class within their namespaces.
using System;
using System.Web.Security;
using System.Web.Profile;
using System.Collections.Specialized;
namespace WebProject
{
public class ProfileCommon : ProfileBase
{
public static ProfileCommon GetUserProfile(string username)
{
return Create(username) as ProfileCommon;
}
public static ProfileCommon GetUserProfile()
{
return Create(Membership.GetUser().UserName) as ProfileCommon;
}
[SettingsAllowAnonymous(false)]
public string FirstName
{
get
{
return base["FirstName"] as string;
}
set
{
base["FirstName"] = value;
}
}
[SettingsAllowAnonymous(false)]
public string LastName
{
get
{
return base["LastName"] as string;
}
set
{
base["LastName"] = value;
}
}
[SettingsAllowAnonymous(false)]
public string Email
{
get
{
return base["Email"] as string;
}
set
{
base["Email"] = value;
}
}
[SettingsAllowAnonymous(false)]
public StringCollection Sites
{
get
{
return base["Sites"] as StringCollection;
}
set
{
base["Sites"] = value;
}
}
}
}
My profile provider section in my web config file looks like this.
<profile defaultProvider="WebProjectProfileProvider" inherits="WebProject.ProfileCommon">
<providers>
<clear />
<add name="WebProjectProfileProvider" applicationName="/" type="System.Web.Profile.SqlProfileProvider" connectionStringName="Test"/>
</providers>
</profile>
If I use one application to perform the user import and another that uses the membership, roles and profiles that I created would this cause the "The settings property '' was not found." error? I can't seem to pinpoint where the error is being caused and some of the most common causes I've already checked. This is the first time I've used this feature in .net on such a large scale. Any help is greatly appreciated.
Thanks.
I found my problem. The issue was in the calling code. I had run into so many issues regarding the profile that I forgot to change the calling code back to the static method
ProfileCommon.GetUserProfile();
The other issues I had run into as well was declaring the profile's properties in the web config and declaring them in a profile common class. This was causing me to get flip flopping errors such as "the property has already been defined." and "The settings property '' was not found."
In short, declare the ProfileCommon proxy class within code and not in the web.config IF you are using a "Web Application" solution. Declare the properties in web.config if you are using a "web site" solution.
The best example I've come accross on the web was from this site.
ASP.NET Profiles in Web Application Projects
It describes how to use custom profiles in a nice concise summary and gives a full explanation of why this method is performed for web applications and why it's done differently for web sites. Hope this saves many head aches.

Categories

Resources