Access denied when saving to file path - c#

I have an upload method that looks like this
public ActionResult UploadImage(int? id)
{
if (id == null)
return HttpNotFound();
Component c = db.Components.Find((int)id);
HttpPostedFileBase photo = Request.Files["image"];
if (photo != null && photo.ContentLength > 0)
{
var file = IGT.imagePath + "\\Components\\";
//bool exists = System.IO.Directory.Exists(Server.MapPath(file));
//if (!exists)
// System.IO.Directory.CreateDirectory(Server.MapPath(file));
var filename = file + id.ToString() + ".jpg";
if (!System.IO.Directory.Exists(file))
{
System.IO.Directory.CreateDirectory(file);
}
photo.SaveAs(filename);
c.Image_Url = IGT.baseUrl + "/Content/images/Components/" + id.ToString() +".jpg";
db.SaveChanges();
}
But I receive the error at photo.SaveAs(filename); saying
System.UnauthorizedAccessException: 'Access to the path 'C:\Users\chris\Source\Repos\inventory2.0\PIC_Program_1.0\Content\images\Components\498.jpg'
is denied.'
Why is this and how can I fix it?

Whatever user is executing that code, does not have permission to write to that file path. If you go to C:\Users\chris\Source\Repos\inventory2.0\PIC_Program_1.0\Content\images\Components, right click, properties, Security tab, you will see the users that have permissions and what those permissions are. You can add or edit your users permissions there.

I think the problem is your application user don't have permission to access your the folder. If you are testing this in VS IIS express, then you should grant permission for your current user.
However, if you are receiving this error message from IIS Server. Then you should grant permission for application pool identity(IIS Apppool\apppoolname).
Process monitor can help you fix access denied error all the time. You just need to create a filter for Result ="access is denied". Then it will tell you who and what permission are required.
https://learn.microsoft.com/en-us/sysinternals/downloads/procmon

Related

How can i save the XML file elements to ExternalStorageDirectory in xamarin android? [duplicate]

So I'm trying to create a file and I'm getting
System.UnauthorizedAccessException: Access to the path "/DownloadJitters" is denied. I'm not sure if it's a permissions thing (I've tried a write to external storage in case but that didn't work) or something else. Also I'm trying to figure out a good place to write these files as I would like them not to be easily found. Any ideas? Here's the code as well :
public void favouriteList(MainActivity av, Ordering o, string favouriteName, string totalCost, JittersListView jlv)
{
//Checks Directory exists
if (File.Exists(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt") == false)
{
Directory.CreateDirectory(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/");
File.Create(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt");
}
if (File.Exists(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/" + favouriteName + ".txt") == false)
{
var fav = File.Create(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/" + favouriteName + ".txt");
fav.Close();
string file = Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/" + favouriteName + ".txt";
string added = null;
int current = 0;
while (true)
{
if (current < jlv.Count)
{
JittersListItem jli = jlv[current];
added += jli.Top + "|" + jli.Bottom + "|" + jli.itemPic + "|" + jli.itemDes + System.Environment.NewLine;
current++;
}
else
{
break;
}
}
File.AppendAllText(file, favouriteName + "|" + totalCost + added);
}
else
{
new AlertDialog.Builder(av)
.SetMessage("Please use a different name, this one has been taken.")
.Show();
}
}
First of all add this permissions to you Manifest:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Since Android 6.0 (API 23) you need also to request the permissions manually, add this code on your MainActivity.cs on your Xamarin.Android project:
if ((ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != (int)Permission.Granted)
|| (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) != (int)Permission.Granted))
{
ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage }, REQUEST);
}
Since Android 10 you may also need to add android:requestLegacyExternalStorage attribute to your Manifest like this:
<application android:requestLegacyExternalStorage="true" />
UPDATE
After doing all this maybe you still get the exception on Android 11 or greather If you try save a file on any path of the SDCARD, you can only use the EXTERNAL STORAGE and INTERNAL STORAGE paths of your App by default.
PRIVATE EXTERNAL STORAGE PATH OF YOUR APP:
Android.App.Application.Context.GetExternalFilesDir(null).AbsolutePath
PRIVATE INTERNAL STORAGE PATH OF YOUR APP:
System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData)
If you need to access to any path of the SDCARD on Android 11 or greather, you should ask for manage all files access permission.
private void RequestStorageAccess()
{
if (!Environment.IsExternalStorageManager)
{
StartActivityForResult(new Intent(Android.Provider.Settings.ActionManageAllFilesAccessPermission), 3);
}
}
This will pop up an Activity listing all the Apps that have access to all file access permission, the user should tap your App and enable the permission.
But in order to make your App appear there, you should add this permission to your manifest:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
And you also need to make sure that you are compiling your App with Android 11 or greather.
Here you can see the permission Activity:
For some reason I was able to save files to documents folder without this permission, but after deleting the files I was not able to create again the same files with the same name, also I was not able to list all the files, this is why this pop-up is needed even to use the documents directory.
Use manage all files permission only in case that you really need
If you want to publish your App in Google Play with the "Manage All Files Permission", you will have to Fill a "Permissions Declaration Form" on Google Play, because it is considered a
"high-risk or sensitive permissions" here is more information, and Google should approve it:
https://support.google.com/googleplay/android-developer/answer/9214102?hl=en#zippy=
When to ask for this permission:
https://support.google.com/googleplay/android-developer/answer/10467955?hl=en
Ok I Fixed it by changing the saving location to
System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal)
Don't ask me why that worked when they need the same permissions but it did.
Xamarin.Forms (Android solution)
MainActivity.cs
For apps that target Android 5.1(API level 22) or lower, there is nothing more that needs to be done.
Apps that will run on Android 6.0(API 23 level 23) or higher should ask Run time permission checks.
protected override void OnCreate(Bundle bundle)
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
{
if (!(CheckPermissionGranted(Manifest.Permission.ReadExternalStorage) && !CheckPermissionGranted(Manifest.Permission.WriteExternalStorage)))
{
RequestPermission();
}
}
LoadApplication(new App());
}
private void RequestPermission()
{
ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage }, 0);
}
public bool CheckPermissionGranted(string Permissions)
{
// Check if the permission is already available.
if (ActivityCompat.CheckSelfPermission(this, Permissions) != Permission.Granted)
{
return false;
}
else
{
return true;
}
}
This looks like a copy and paste error - you should learn to refactor common code and expressions into one value and reuse it.
//Checks Directory exists
if (File.Exists(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt") == false)
{
Directory.CreateDirectory(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/");
File.Create(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt");
}
Let's assume Android.OS.Environment.DirectoryDownloads has the value /Downloads. Now go through the code line by line (you should really do this with a debugger):
File.Exists(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt")
The parameter value here will be "/Downloads/Jitters/FavouritesListAdded.txt" - OK
Directory.CreateDirectory(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/");
There's no leading slash on the literal string here, so the value will be: /DownloadsJitters/FavouriteList - I'm guessing you probably meant it to be /Downloads/Jitters/FavouriteList.
Rather than making sure slashes are added to all 6 path expressions in your code - just create one variable with the path value and reuse it.
If you are still getting UnauthorizedAccessException for write or read file in Xamarin Android. I just written article to resolve it
http://bsubramanyamraju.blogspot.com/2019/12/resolved-unauthorizedaccessexception.html

c# service: how to get user profile folder path

I need to get the user directory from within a C# windows service...
...like C:\Users\myusername\
Ideally, I'd like to have the roaming path...
...like C:\Users\myusername\AppData\Roaming\
When I used the following in a console program I got the correct user directory...
System.Environment.GetEnvironmentVariable("USERPROFILE");
...but when I use that same variable in a service, I get...
C:\WINDOWS\system32\config\systemprofile
How can I get the user folder and maybe even the roaming folder location from a service?
Thanks in advance.
I have searched for getting the profile path of user from Windows service. I have found this question, which does not include a way to do it. As I have found the solution, partly based on a comment by Xavier J on his answer, I have decided to post it here for others.
Following is a piece of code to do that. I have tested it on few systems, and it should work on different OSes ranging from Windows XP to Windows 10 1903.
//You can either provide User name or SID
public string GetUserProfilePath(string userName, string userSID = null)
{
try
{
if (userSID == null)
{
userSID = GetUserSID(userName);
}
var keyPath = #"SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\" + userSID;
var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(keyPath);
if (key == null)
{
//handle error
return null;
}
var profilePath = key.GetValue("ProfileImagePath") as string;
return profilePath;
}
catch
{
//handle exception
return null;
}
}
public string GetUserSID(string userName)
{
try
{
NTAccount f = new NTAccount(userName);
SecurityIdentifier s = (SecurityIdentifier)f.Translate(typeof(SecurityIdentifier));
return s.ToString();
}
catch
{
return null;
}
}
First, you'll want to use Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)
Environment.SpecialFolder.ApplicationData is for roaming profiles.
Find all SpecialFolder enumeration values here: https://msdn.microsoft.com/en-us/library/system.environment.specialfolder(v=vs.110).aspx
As others have noted, the Service will run under the account LocalSystem/LocalService/NetworkService, depending on configuration: https://msdn.microsoft.com/en-us/library/windows/desktop/ms686005(v=vs.85).aspx
A service doesn't log on like a user, unless the service is configured to use a specific user's profile. So it's not going to point to "user" folders.

C# Nested impersonation - impersonate a user whilst already impersonating another

I have a fairly odd requirement to be able to impersonate a user, when I'm already impersonating another, using C#.
I'm writing an app to allow the management of Active Directory users. This app will provide the ability for anyone in the company to view and maintain certain details about themselves (some of which will not actually be saved to Active Directory, but some of which will), for managers to be able to view and maintain details about their team, and for HR to be able to view and maintain details about anyone.
For obvious reasons I don't want to develop or test this against the live domain. We have recently ported all users over to this domain from another domain, which means I can actually test against the old domain without affecting anything. However, to enable me to do this I have to impersonate my old account on the old domain, which I do on loading the application.
Although for me everything will work fine as I'm setup as a domain admin, going forward obviously not all users will be domain admins, and won't be able to write to AD under their own account, and therefore we have another domain admin user setup specifically for this application, whenever data needs to be saved to AD that user is impersonated. This was working great before when I was testing against an Active Directory I'd setup on a virtual machine because I was logging onto the local domain, however that didn't allow me to step through the code in Visual Studio so debugging was slow, and hence I've stopped using that virtual machine and am using this old domain. Now I'm already impersonating another user (i.e. my old domain account), when it then tries to impersonate the domain admin user it fails with an "System.Security.SecurityException: Access is denied." exception. The line this fails on is just writing out some debugging information using "WindowsIdentity.GetCurrent().Name".
If I change my code so I'm actually logging in using the new domain admin rather than my old account, the first time it goes through it logs in successfully (so the credentials are correct), however when it then goes through and tries to do the same again to write to AD it fails with the above exception. Therefore I think it must be a problem with trying to do a nested impersonate.
Is it possible to do a nested impersonate?
Below is the code I'm using:
private static WindowsImpersonationContext ImpersonateUser(out string result, string sUsername,
string sDomain, string sPassword)
{
// initialize tokens
var pExistingTokenHandle = new IntPtr(0);
var pDuplicateTokenHandle = new IntPtr(0);
// if domain name was blank, assume local machine
if (sDomain == "")
{
sDomain = Environment.MachineName;
}
try
{
result = null;
const int logon32ProviderDefault = 0;
// create token
const int logon32LogonInteractive = 2;
// get handle to token
var bImpersonated = LogonUser(sUsername, sDomain, sPassword,
logon32LogonInteractive,
logon32ProviderDefault,
ref pExistingTokenHandle);
// did impersonation fail?
if (!bImpersonated)
{
var nErrorCode = Marshal.GetLastWin32Error();
result = "LogonUser() failed with error code: " + nErrorCode + "\r\n";
}
// Get identity before impersonation
result += string.Format("Before impersonation: {0}\r\n", WindowsIdentity.GetCurrent().Name);
var bRetVal = DuplicateToken(pExistingTokenHandle, (int)SecurityImpersonationLevel.SecurityImpersonation,
ref pDuplicateTokenHandle);
// did DuplicateToken fail?
if (bRetVal)
{
// create new identity using new primary token
var newId = new WindowsIdentity(pDuplicateTokenHandle);
var impersonatedUser = newId.Impersonate();
// check the identity after impersonation
result += "After impersonation: " + WindowsIdentity.GetCurrent().Name + "\r\n";
return impersonatedUser;
}
else
{
var nErrorCode = Marshal.GetLastWin32Error();
CloseHandle(pExistingTokenHandle); // close existing handle
result += "DuplicateToken() failed with error code: " + nErrorCode + "\r\n";
return null;
}
}
finally
{
// close handle(s)
if (pExistingTokenHandle != IntPtr.Zero)
{
CloseHandle(pExistingTokenHandle);
}
if (pDuplicateTokenHandle != IntPtr.Zero)
{
CloseHandle(pDuplicateTokenHandle);
}
}
}
When this is called for the nested impersonation which fails, "bImpersonated" is actually "true", as is bRetVal, which suggests its worked, however when it gets to "WindowsIdentity.GetCurrent().Name" it fails with the exception above.
I hope this makes sense, and would appreciate any assistance.

ASP.NET ascx control hosted in Sharepoint gets System.UnauthorizedAccessException when trying to save file

We have an ascx custom control (not a web part) hosted in a special Sharepoint page. This page allows users to upload files to our server. Unfortunately permission issues are preventing Sharepoint from saving files to the network location.
The network account attributed to the application pool for the Sharepoint 2007 based site has "modify" and "read" access granted to the location.
We've logged in to a different machine using the credentials used by the application pool account and can create directories and files without any issue at the specified network location.
Is it possible Sharepoint is trying to use some other account to save these files rather than the one set on it's Application Pool in IIS7?
The error we're getting:
Message: Access to the path '\opal\gwl\pictures\L36' is denied.
Stack Trace: at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.Directory.InternalCreateDirectory(String fullPath, String path, DirectorySecurity dirSecurity) at System.IO.Directory.CreateDirectory(String path, DirectorySecurity directorySecurity) at ECan.SharePoint.Web.Applications.MyECan_WaterMeterFormDatalogger.SavePhotos()
Exception Type: System.UnauthorizedAccessException
User: System Account
The code for the SavePhotos function in the ascx code behind file:
protected void SavePhotos()
{
string wellNo = WellNo.Value;
string epoWaterMeterID = EPO_WaterMeterID.Value;
string dirRoot = ConfigurationManager.AppSettings["PhotoDir"];
string map = wellNo.Substring(0, wellNo.IndexOf('/'));
int photoSaveCount = 1;
foreach (string filePath in Request.Files)
{
HttpPostedFile file = (HttpPostedFile)Request.Files[filePath];
if (file.InputStream.Length > 0)
{
try
{
// Create dir if does not exist
string dir = dirRoot + map;
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
// Save file
file.SaveAs(dir + #"\" + wellNo.Replace('/', '_') + "-" + epoWaterMeterID.ToString() + "-" + photoSaveCount.ToString() + ".jpg");
photoSaveCount++;
}
catch (Exception ex)
{
Logger.Write(ex);
}
}
}
}
Anyone have any ideas what the issue might be?
I think you have to call the SavePhotos with elevated privildges.
Running the code with elevated priviledges will executes the specified method with Full Control rights even if the user does not otherwise have Full Control.
See link:
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spsecurity.runwithelevatedprivileges(v=office.12).aspx
Please try the below code:
protected void Button1_Click(object sender, EventArgs e)
{
SPSecurity.CodeToRunElevated elevatedGetSitesAndGroups = new SPSecurity.CodeToRunElevated(SavePhotos);
SPSecurity.RunWithElevatedPrivileges(elevatedGetSitesAndGroups);
}
Have you tried to set the permission of the newly created directory or folder? You can do so by using the DirectorySecurity class within the System.Security.AccessControl Namespace, and specifically the SetAccessControl Method of that class.

Accessing mapped folder from a Windows Service written in C#

I have a windows service which polls for a specific folder for creation of new files. This works fine when the folder is in one of the local drives such as C: or D:
The service fails to find a folder on a mapped drive.
Here is the code which does the checking for folder exist before polling:
System.Security.Principal.WindowsIdentity userIdentity =
System.Security.Principal.WindowsIdentity.GetCurrent();
System.Security.Principal.WindowsPrincipal principal =
new System.Security.Principal.WindowsPrincipal(userIdentity);
MappedDriveResolver mdr = new MappedDriveResolver();
if (mdr.isNetworkDrive(folderPath))
{
LoggingAppWrapper.LogDeveloperMessage(folderPath + " is on a Mapped drive", 1, TraceEventType.Information, string.Empty);
}
MappedDriveResolver is a class that I found hereHow do I determine a mapped drive's actual path?
The code in that link works fine from a simple console application, but fails when it is part of windows service.
Any suggestions as to what has to be done for the code to work for a windows service?
Regards.
I would recommend you configure your service to use UNC paths for folders not on the server running the service.
Mapped drives are a usability feature for users and as such they are specific to that users profile/environment. Meaning, when you login you may have a drive X: that is mapped to \\server1\share1 but when I login my drive X: could be mapped to \\server2\share2 instead. The actual mapping process is either saved as part of your profile with the "Reconnect at logon" or is handled by a logon script.
You need to check what account the service is running under and make sure that mapped drive exists for that user environment (This might help How to map a network drive to be used by a service).
Edit:
The reason your console application works and the service doesn't is because of the differences between the environment they are running in.
To illustrate this, take this console application, compile it and then run it as a Schedule Task. Set the "path" variable to be a mapped drive that your user can access.
static void Main(string[] args) {
MappedDriveResolver mdr = new MappedDriveResolver();
string logfile;
string path = #"I:\";
string[] files;
// Write out "log" file to where this is running from
logfile = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
logfile = Path.Combine(logfile, "log.txt");
using (StreamWriter sw = new StreamWriter(logfile, true)) {
try {
sw.WriteLine("Checking path " + path);
if (mdr.isNetworkDrive(path)) {
sw.WriteLine("Network Drive: Yes");
} else {
sw.WriteLine("Network Drive: No");
}
} catch (Exception ex) {
sw.WriteLine("Exception: " + ex.Message);
}
try {
sw.WriteLine("Resolve path " + path);
string newpath = mdr.ResolveToUNC(path);
sw.WriteLine("Resolved path " + newpath);
} catch (Exception ex) {
sw.WriteLine("Exception: " + ex.Message);
}
try {
sw.WriteLine("Get file list from " + path);
files = Directory.GetFiles(path);
if (files == null || files.Length == 0) {
sw.WriteLine("No files found");
} else {
sw.WriteLine(string.Format("Found {0} files.", files.Length));
}
} catch (Exception ex) {
sw.WriteLine("Exception: " + ex.Message);
}
sw.Flush();
sw.Close();
}
}
Note: This is with the Windows 7 Task Scheduler
Test 1: Just run the app by double-clicking on it.
Result: Success
Test 2: Configure scheduled task to run as your user account with "Run only when user is logged on"
Result: Success
Test 3: Configure scheduled task to run as your user account with "Run whether user is logged on or not"
Result: Exceptions
Test 4: Configure schedule task to run as "Local Service" account.
Result: Exceptions
Test 1 & 2 work because they are using the currently logged in user environment including the mapped drives that are part of it.
Test 3 & 4 fail because they have their own user environment created for them, which does not have any mapped drives configured. It escapes me at the moment what the differences there are, but an "interactive" and "non-interactive" environment are different in some significant ways.

Categories

Resources