Documents.Add fails on ASP.NET (VB.NET) - asp.net

I am having an issue with opening a document using Microsoft Word from ASP.NET MVC.
This works perfectly on my developer machine, but not when deployed to IIS.
Dim word = New Microsoft.Office.Interop.Word.Application
'This line is failing to return a document object
Dim letter = word.Documents.Add(letter_doc_path)
'This line then fails due to [letter] being null
letter.MailMerge.OpenDataSource(csvPath)
I have added permissions in "Component Services" (dcomcnfg) to the NETWORK SERVICE user which allows the creation of the Word object in the first place, but I am completely stuck as what to do with this one.
I have also tried suppressing Word dialogs with the following line just in case
word.DisplayAlerts = Microsoft.Office.Interop.Word.WdAlertLevel.wdAlertsNone
The issue isn't helped by not having an error (apart from the null object reference obviously) - maybe there's a way to query Word for a specific error message?

Word requires the normal.dot template file when opening any document, the problem was occurring because the IIS user didn't have anywhere to create the normal.dot so it was failing in the background.
This was fixed by setting the UserTemplate path for the newly created word instance (immediately after creating it).
The path must be writeable by the IIS user (NETWORK SERVICE in my case).
word.Options.DefaultFilePath(Microsoft.Office.Interop.Word.WdDefaultFilePath.wdUserTemplatesPath) = working_folder
So just for completeness, here's the original example with the winning line included:
Dim word = New Microsoft.Office.Interop.Word.Application
'this line fixed it
word.Options.DefaultFilePath(Microsoft.Office.Interop.Word.WdDefaultFilePath.wdUserTemplatesPath) = working_folder
Dim letter = word.Documents.Add(letter_doc_path)

I was having the same problem, and the settings that wheelibin suggested weren't enough to create documents using the NETWORK SERVICE account.
What I ended up doing is:
Create a user account for this
process to run under.
Login as the user and run Word (this
does various setup tasks in Word so
the application doesn't try putting
up modal dialogs when running as a
service).
Create a new application pool and set
the pool to run as the user account.
If you're using Windows
Authentication, and your server is
Windows 2003 (or 2000, presumably),
then this issue applies, and you
need to either change the SPN of the
server, which will break Windows
Authentication for any application
running under a different user
account, or you have to switch the
authentication provider over to NTLM
instead of Kerberos.
IIS 7 can use Kernel Mode Authentication to avoid the issue.

I am not sure how are you catching the errors.
Please take a look at the following pages if you find some clue from that.
Error while using Microsoft Office 2003 in web application
Error while calling MS-Word from ASP.NET
"There is insufficient memory or disk space. Save the document now" - Opening MS Word from ASP.NET

Related

Can I use ServerManager from Microsoft.Web.Administration without admin user as an application pool identity

I want to read some settings of the application pool using the ServerManager object from the Microsoft.Web.Administration.dll. The problem is that it works only if the identity of the application pool is a windows user with administrator privileges. Otherwise I am getting UnauthorizedAccessException - Filename: redirection.config; Error: Cannot read configuration file due to insufficient permissions.
Is there any workaround about this issue.
My code is the following:
ServerManager manager = new ServerManager();
string currentSiteName = System.Web.Hosting.HostingEnvironment.SiteName;
Site currentSite = manager.Sites[currentSiteName];
string appVirtaulPath = HttpRuntime.AppDomainAppVirtualPath;
string appPoolName = string.Empty;
foreach (Application app in currentSite.Applications)
{
string appPath = app.Path;
if (appPath == appVirtaulPath)
{
appPoolName = app.ApplicationPoolName;
}
}
ApplicationPool currentAppPool = manager.ApplicationPools[appPoolName];
Thanks!
No, there is no workaround to read the configuration file without causing a big security concern. What are you trying to accomplish?
If reading configuration settings, you can use an API in the same DLL that will give you read-only configuration access for that site settings, such as reading web.config or values in applicationHost.config for that site only, and not encrypted ones (such as passwords). The API is called WebConfigurationManager and has a static method called GetSection, such as WebConfigurationManager.GetSection("system.webServer/defaultDocument")
See: https://msdn.microsoft.com/en-us/library/microsoft.web.administration.webconfigurationmanager.getsection.aspx
However, several settings (namely all the ones used to start the process w3wp.exe) are not possible to be read through that API.
Short story: Unfortunately for security reasons many of those settings are not possible to be read from a worker process. There are some things you can read using server variables such as Request.ServerVariables["APP_POOL_ID"]), or Request.ServerVariables["APP_POOL_CONFIG"]). Of course bitness you could calculate the size of a pointer (4 or 8), or use environment variables (like PROCESSOR_ARCHITECTURE)
Longer story: In IIS for security reasons we take the applicationHost.config file and we split it into smaller application pool.config files (by default located at C:\inetpub\temp\appPools) which are isolated for security reasons so that even if untrusted code were to run in the process (w3wp.exe) to try to steal/read the settings of other sites it would be physically impossible. You can open the file and see which settings are there and you can read those. You'll notice the appPools section is missing entirely since that is only used by WAS to start w3wp.exe.

Difference between starting process from Console applciation and ASP.NET application

I have a Web API application that needs to run a Python script which in turn runs a Perl script:) does some otehr stuff and get the output results from it.
The way I do this is with starting a Process:
var start = new ProcessStartInfo()
{
FileName = _pythonPath, //#"C:\Python27\python.exe",
Arguments = arguments, //#"D:\apps\scripts\Process.py
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
var result = reader.ReadToEnd();
var err = process.StandardError.ReadToEnd();
process.WaitForExit();
return result;
}
}
The script inside tries to connect to Perforce server using P4 Python API and then Perl script call a P4 command as well. When running this code from Console application, everything goes fine. The program automatically gets the Perforce settings (I've got a P4V client with all the settings specified). But when running from ASP.NET Web API, it doesn't get the settigns and says that it cannot conenct to perforce:1666 server (I guess this is the standard value when no settign specified).
I do understand that not so many people use Perforce, especially in such way and can help here, but would like to know what is the difference between running this script from Console app and Web API app that mich cause this different behaviour.
One of the most obvious differences between running code from a console application and running code in IIS* is that, usually, the two pieces of code will be running under different user accounts.
Frequently, if you're experiencing issues where code works in one of those situations and not the other, it's a permissions or a per-user-settings issue. You can verify whether this is the case by either running the console application under the same user account that is configured for the appropriate IIS application pool, or vice verse, configure the application pool to use your own user account, and then see whether the problem persists.
If you've confirmed that it's a permissions issue and/or per-user-settings, then you need to decide how you want to fix it. I'd usually recommend not running IIS application pools under your own user account - if you cannot seem to get the correct settings configured for the existing application pool user, I'd usually recommend creating a separate user account (either locally on the machine or as part of your domain, depending on what's required) and giving it just the permissions/settings that it requires to do the job.
*IIS Express being the exception here because it doesn't do application pools and the code does end up running under your own user account.

Server.MapPath and running a command line utility from an ASP.NET application

I need to run an executable from an ASP.NET app using Process. The .exe file is located inside the ASP.NET project - 'ProjectRoot/Utilities/utility.exe'
Why does this code fail to run it:
string path = Server.MapPath("/Utilities/");
string args = " etc etc";
Process p = Process.Start(new ProcessStartInfo(path + "utility.exe", args));
p.WaitForExit(3000);
I've tried "\\utility.exe" too.
The answer depends on the type of the occuring exception. By the way, if the identity of application pool (which is the equal position to the process in a windows app) is set to a limited user (as it is by default) you can not execute a process on the server because of security issues.
If you know about the consequences and the server is your own, you can change the identity of the application pool to an authorized user, then your application can do such a thing without any hesitation.
Cheers
There's a few things to check here.
The first thing is to ensure that string path = Server.MapPath("/Utilities/"); is a valid path and that utility.exe is in the correct location.
You said that it does not throw an exception, so the invocation of utility.exe should at least be valid. However, utility.exe may swallow any exceptions it in turn encounters (depends on how it was coded), which may be why it does not appear to be working.
On top of that, your args variable may be specifying a file or some other resource that utility.exe accesses. If it does swallow exceptions, it could be masking a permissions error if the application pool identity does not have access to the resouce.

log4Net eventlog permissions issue using non-administrator account

This probably isnt an issue with SiteCore per se but I've included it for completeness. I have sitecore 6.3 running under IIS7 using a custom identity for the app pool. I cant get Sitecore to write its logging information (using the default log4net settings) to the eventlog. I've followed the advice here: http://logging.apache.org/log4net/release/faq.html#Why%20doesn%27t%20the%20EventLogAppender%20work? and although it works fine when I make the custom identity a member of the administrator's group I need to find a way to get it working in production without such a security hack.
The weird thing is that I have a MSI that installs it (running under an account which IS a member of the administrator's group) and creates the correct registry keys in the eventlog for me and yet despite that, I am still getting the following error when I run the application using the custom identity (without it being a member of administrators).
log4net:ERROR DOMConfigurator: Could not create Appender [EventLogAppender] of type [log4net.Appender.EventLogAppender]. Reported error follows.
System.Security.SecurityException: Requested registry access is not allowed.
at Microsoft.Win32.RegistryKey.OpenSubKey(String name, Boolean writable)
at System.Diagnostics.EventLog.GetEventLogRegKey(String machine, Boolean writable)
at System.Diagnostics.EventLog.FindSourceRegistration(String source, String machineName, Boolean readOnly)
at System.Diagnostics.EventLog.DeleteEventSource(String source, String machineName)
at log4net.Appender.EventLogAppender.ActivateOptions()
at log4net.Repository.Hierarchy.DOMHierarchyConfigurator.ParseAppender(XmlElement appenderElement)
The Zone of the assembly that failed was:
MyComputer
log4net:ERROR DOMConfigurator: Appender named [EventLogAppender] not found.
Thinking I could narrow it down to a registry permission issue I granted Everyone full permissions to the following registry key and subkeys but it didnt work either: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog
The custom identity is a member of the following groups:
Event Log Readers
IIS_USERS
Performance Monitor Users
I've also seen the following question which seems to ask the same thing. The Microsoft article seems to suggest it might be a problem with ACLs on an event log and gives examples on how you can change SSDLs but I'd rather avoid that if at all possible.
EDIT:
I have another server running where the log is being populated fine. The custom identity was a member of administrators so I revoked that and rebooted, trying to purposely break it but I cant. Config is identical on both boxes and same identity used to run the MSI which creates the registry keys. Have run procmon on both (after doing a IISReset and spinning up the app pool again) to examine registry activity. Strange thing is - on the box that works you get 477 name not found records for my event source in the wrong places (Application, and a different Custom EventLog "MyCompany"). No hits for the place where it is logging which is "MyCompany\MyCompany.SiteCore". Whilst on the box which is broken, it does appear to be requesting to read the right key (albeit only 6 times) but you then get the Log4Net registry access error.
As I understand it EventStores are stored in the registry, so you only need write permission to registry to create or delete an EventStore. This is usually only needed once and most applications create this as part of the install procedure so that the application does not need to be run as Administrator during normal execution.
However your error message (in the question) includes the method DeleteEventSource from which I would deduce/guess that the EventSource does exist but is wrong in some way. So perhaps this is currently registered as writing to the event log named MyCompany and you are now trying to change it to "MyCompany\MyCompany.SiteCore" which requires you to delete the old eventsource and create a new one.
So it sounds like your installation routine is creating a different EventSource from the one that your application is actually using.
If that doesn't help, then I would suggest enabling internal logging for Log4net (but obviously not to the eventlog) which will probably give you more information.
Giving full permission to the registry key is not enough.
According to Microsoft
To create an event source in Windows Vista and later or Windows Server 2003, you must have administrative privileges.
The reason for this requirement is that all event logs, including security, must be searched to determine whether the event source is unique. Starting with Windows Vista, users do not have permission to access the security log; therefore, a SecurityException is thrown.
Starting with Windows Vista, User Account Control (UAC) determines the privileges of a user. If you are a member of the Built-in Administrators group, you are assigned two run-time access tokens: a standard user access token and an administrator access token. By default, you are in the standard user role. To execute the code that accesses the security log, you must first elevate your privileges from standard user to administrator. You can do this when you start an application by right-clicking the application icon and indicating that you want to run as an administrator.
I think, contrary to the Apache documentation, log4net DOES need write access to the registry – or at least it does in my case. To prove this, I backed up the registry on the server where it wasnt working and granted IIS administrator privileges before spinning up sitecore. Sure enough it started logging away to the eventlog nicely and then when I exported the registry again to run a diff, there WAS a difference.
The value for the eventlogmessage file on my event source had been updated from:
C:\Windows\Microsoft.NET\Framework\v2.0.50727\EventLogMessages.dll
To
C:\Windows\Microsoft.NET\Framework64\v2.0.50727\EventLogMessages.dll
So I assumed that merely changing this value in the registry by hand would work.
But it didn’t.
So I ran procmon on the two servers I have: A=the working one, B=the failing one. Sure enough, on server B I have a line which says:
Operation: RegOpenKey, Path: HKLM\System\CurrentControlSet\Services\EventLog, Desired Access:Read/Write, Result: ACCESS DENIED.
I’ve traced through with Server A and in exactly the same place, the key is requested with Desired Access:Read.
Conclusion:
It seems unavoidable that I will need to grant my app pool identity administrator privileges in production for at least enough time to programatically do the necessary registry writes the first time from within log4net. I dont know why administrator; I have tried granting Full permissions to the entire eventlog node in the registry for my custom app to no avail. It seems to do something which I cannot identify or pin down. I will then revoke this privilege immediately after it starts to log and monitor whether subsequent installs knock out the functionality afterwards. (Hopefully not).
If anyone has any insight into this behaviour it would be greatly appreciated.

How to change data source in reportviewer control

I have a reportviewer (Microsoft.ReportViewer.WebForms) control on my page. All my reports use one data source. I want to be able to let my reports run on a different database when started from my UAT enviroment. So the location of the reports is the same, but the data comes from a different db. I cannot seem to find how this is done, is it even possible?
EDIT: They are server reports on SQL Server . I know you can set the dataset programmaticaly but I just want the reports to point to a different db and leave the rest of the report intact.
2005
TIA,
John
Did you want to pass a full connection string to the report as a parameter? You can do it but sometimes SSRS gets funny and make sure you delete the report off the server before you deploy a new copy when doing this...
1. Make a parameter - let's call ours connectionStr. Make it not null, not blank, single select and text as the data type. Eventually, you will want to hide this parameter but for testing please leave it visible.
2. So the value you will be using as the connection string... (for testing I set this as the default for the parameter, with nothing put under the available values section) Data Source=MySQLServerName;Initial Catalog=MyDatabaseName;Persist Security Info=True;User ID=MyUserNameForTheServer;Password=MyPasswordForTheServer;MultipleActiveResultSets=True
3. You need an unattended execution account on your report server or you get this: unattended execution account is not specified. (rsInvalidDataSourceCredentialSetting). http://msdn.microsoft.com/en-us/library/ms156302.aspx I can't provide more details because my boss had to do this part for me.
4. Under your datasource properties in SSRS... check Embedded Connection, select the type (mine is just a normal MS SQL Server), for the connection string, open the expression box and put: =Parameters!connectionStr.value and then click credentials and make sure the last option for no credentials is selected.
5. Your datasets for that datasource will no longer be happy when you try to edit them in design view but you can switch the datasource connection properties back to how they were, not using the parameter based connection string, for editing them.
My reports are on different servers, with different instances of the Report Server, too. On some servers, they need to get their data from various databases depending on whatever, stuff. This way, with the connection string as a parameter, I can use the same reports everywhere and just deploy them to the different servers. If you are having to pass this connection string around your app or to a report viewer, I suggest using encryption.
Like I said... SSRS get's funny when you start doing this, though. Your reports should always work in preview mode after doing this, if they don't even when provided with the correct connection strings, then you have an issue that won't be solved by just deploying to the server. Trouble shooting problems with this once they are on the server but not working include checking permissions, making sure the report receives the correct connection string and making all your stored procedures and functions within the SQL database are all the same.
If you want to just pass the database name and everything else is the same (server name, username, password) then just set the connection string parameter equal to your database name and for the datasource expression value use
="Data Source=MySQLServerName;Initial Catalog=" + Parameters!connectionString.value + ";Persist Security Info=True;User ID=MyUserNameForTheServer;Password=MyPasswordForTheServer;MultipleActiveResultSets=True"
I needed to pass the whole thing in, and you can play around with the credential settings - you might be able to save the server username/password info in there for each report so that the unattended execution account is not needed.

Resources