Create file in Xamarin Cross Platform App Development - xamarin.forms

I'm new to Xamarin. I'm trying to create a file using code
string path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string filename = Path.Combine(path, "myfile.txt");
using (var streamWriter = new StreamWriter(filename, true))
{
streamWriter.WriteLine(DateTime.UtcNow);
}
using (var streamReader = new StreamReader(filename))
{
string content = streamReader.ReadToEnd();
System.Diagnostics.Debug.WriteLine(content);
}
The above code sample is giving me exception
System.UnauthorizedAccessException: 'Access to the path 'C:\Users\hchittora\Documents\myfile.txt' is denied.'
Here is the full stacktrace
System.UnauthorizedAccessException
HResult=0x80070005
Message=Access to the path 'C:\Users\hchittora\Documents\myfile.txt' is denied.
Source=System.Private.CoreLib
StackTrace
at System.IO.FileStream.ValidateFileHandle(SafeFileHandle fileHandle)
at System.IO.FileStream.CreateFileOpenHandle(FileMode mode, FileShare share, FileOptions options)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encoding, Int32 bufferSize)
at System.IO.StreamWriter..ctor(String path, Boolean append)
at FileHandlingSampleApplication.MainPage..ctor() in C:\Users\hchittora\Desktop\FileHandlingSampleApplication\FileHandlingSampleApplication\FileHandlingSampleApplication\MainPage.xaml.cs:line 19
at FileHandlingSampleApplication.App..ctor() in C:\Users\hchittora\Desktop\FileHandlingSampleApplication\FileHandlingSampleApplication\FileHandlingSampleApplication\App.xaml.cs:line 16
at FileHandlingSampleApplication.UWP.MainPage..ctor() in C:\Users\hchittora\Desktop\FileHandlingSampleApplication\FileHandlingSampleApplication\FileHandlingSampleApplication.UWP\MainPage.xaml.cs:line 24
at FileHandlingSampleApplication.UWP.FileHandlingSampleApplication_UWP_XamlTypeInfo.XamlTypeInfoProvider.Activate_4_MainPage() in C:\Users\hchittora\Desktop\FileHandlingSampleApplication\FileHandlingSampleApplication\FileHandlingSampleApplication.UWP\obj\x86\Debug\XamlTypeInfo.g.cs:line 255
at FileHandlingSampleApplication.UWP.FileHandlingSampleApplication_UWP_XamlTypeInfo.XamlUserType.ActivateInstance() in C:\Users\hchittora\Desktop\FileHandlingSampleApplication\FileHandlingSampleApplication\FileHandlingSampleApplication.UWP\obj\x86\Debug\XamlTypeInfo.g.cs:line 476
When I looked at the above issue, I come to know that UWP cannot directly interact with the system files. What can be another alternative to do so?

Because Xamarin.Forms runs on multiple platforms, each with its own file system, there is no single approach for loading and saving files created by the user.
I can not provide code for all platform here because you need to understand the fundamental for it. Check this for more details.

I'm using SecureStorage library with dependency injection to save on each platform. Although this is mostly used for token caching, you might find it useful.

Related

Access denied error using asymmetric binding in web host server - .NET

I'm getting an Access denied error when I attempt to make a web service request. The web service requires an asymmetric security binding. The call is made via a webpage that references a .NET dll that references the web service. All of this works fine on our server but we loaded it up at a web hosting company we got hit with this error.
My theory is that whatever the GetKeyPairHelper method does it requires the ability to write a file to do it while we don't have access to the location it tries to write to. The support people at this company ran a trace for us and these were the last two lines:
06:03.1 w3wp.exe 5860 CreateFile C:\ProgramData NAME COLLISION >Desired Access: Read Data/List Directory, Synchronize, Disposition: Create, Options: >Directory, Synchronous IO Non-Alert, Open Reparse Point, Attributes: N, ShareMode: Read, >Write, AllocationSize: 0
06:03.1 w3wp.exe 5860 CreateFile C:\ProgramData ACCESS DENIED >Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, >Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a
Looks like something trying to write to C:\ProgramData but what? And why? And how do I get around it? I'm hoping there is some way to configure this so it doesn't have to write a file. Perhaps this job can be performed in memory or directed to an area we have access to.
Stacktrace:
Error: Access is denied. : Server stack trace:
at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle)
at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()
at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize)
at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()
at System.IdentityModel.Tokens.X509AsymmetricSecurityKey.get_PrivateKey()
at System.IdentityModel.Tokens.X509AsymmetricSecurityKey.GetSignatureFormatter(String algorithm)
at System.IdentityModel.SignedXml.ComputeSignature(SecurityKey signingKey)
at System.ServiceModel.Security.WSSecurityOneDotZeroSendSecurityHeader.CompletePrimarySignatureCore(SendSecurityHeaderElement[] signatureConfirmations, SecurityToken[] signedEndorsingTokens, SecurityToken[] signedTokens, SendSecurityHeaderElement[] basicTokens)
at System.ServiceModel.Security.SendSecurityHeader.CompleteSignature() at System.ServiceModel.Security.SendSecurityHeader.CompleteSecurityApplication()
at System.ServiceModel.Security.SecurityAppliedMessage.OnWriteMessage(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.Message.WriteMessage(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.Message.WriteMessage(XmlWriter writer)
at CustomMessageEncoder.CustomTextMessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset)
in C:\xxxxx\CustomEncoders\CustomTextMessageEncoder.cs:line 86
Code:
public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
{
MemoryStream stream = new MemoryStream();
XmlWriter writer = XmlWriter.Create(stream, this.writerSettings);
message.WriteMessage(writer); //'line 86' in stacktrace.
writer.Close();
byte[] messageBytes = stream.GetBuffer();
int messageLength = (int)stream.Position;
stream.Close();
int totalLength = messageLength + messageOffset;
byte[] totalBytes = bufferManager.TakeBuffer(totalLength);
Array.Copy(messageBytes, 0, totalBytes, messageOffset, messageLength);
//maybe parse message around here...
ArraySegment<byte> byteArray = new ArraySegment<byte>(totalBytes, messageOffset, messageLength);
return byteArray;
}
What happened here is that the hosting company was hosting our site on a shared server and so rightfully denied access to the C:\ProgramData folder which, further down the directory contains C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys which is the folder you need access to in order to use certificates based security. Their solution for us was to allow access to this folder for our app pool only. Works like a charm.

Can I use SQL Server CE in a medium trust environment by just deploying the DLLs

I've written an ASP.NET 4.0 application that is using a SQL Server CE 4.0 database. This is all working on my development environment using Medium Trust (same trust config as the hoster). On this machine, I've gone through the install process (downloaded and installed the MSI). I'm now trying to deploy this solution to my web hoster.
The code that is running is (this is a straight out copy of the Altairis Membership Provider:
using (HostingEnvironment.Impersonate())
using (var db = this.connectionString.CreateDbConnection())
using (var cmd = this.CreateDbCommand("SELECT PasswordHash, PasswordSalt FROM $Users WHERE UserName = #UserName AND IsApproved = 1", db))
{
cmd.AddParameterWithValue("#UserName", username);
db.Open();
// Validate password
using (var r = cmd.ExecuteReader()) { .. snip.. }
}
In the instance above, db is DbConnection, and cmd is DbCommand
I'm getting the following error:
[InvalidOperationException: Cannot perform CAS Asserts in Security Transparent methods]
System.Security.CodeAccessSecurityEngine.CheckNReturnSO(PermissionToken
permToken, CodeAccessPermission demand, StackCrawlMark& stackMark,
Int32 create) +0
System.Security.CodeAccessSecurityEngine.Assert(CodeAccessPermission
cap, StackCrawlMark& stackMark) +61
System.Security.CodeAccessPermission.Assert() +28
System.Data.SqlServerCe.KillBitHelper.GetKillBit() +231
System.Data.SqlServerCe.KillBitHelper..cctor() +56
[TypeInitializationException: The type initializer for 'System.Data.SqlServerCe.KillBitHelper' threw an exception.]
System.Data.SqlServerCe.KillBitHelper.ThrowIfKillBitIsSet() +0
System.Data.SqlServerCe.SqlCeProviderFactory..cctor() +41
[TypeInitializationException: The type initializer for 'System.Data.SqlServerCe.SqlCeProviderFactory' threw an exception.]
[TargetInvocationException: Exception has been thrown by the target of an invocation.]
System.RuntimeFieldHandle.GetValue(RtFieldInfo field, Object instance, RuntimeType fieldType, RuntimeType declaringType, Boolean&
domainInitialized) +0
System.Reflection.RtFieldInfo.InternalGetValue(Object obj, Boolean doVisibilityCheck, Boolean doCheckConsistency) +180
System.Reflection.RtFieldInfo.GetValue(Object obj) +8
System.Data.Common.DbProviderFactories.GetFactory(DataRow providerRow) +232
System.Data.Common.DbProviderFactories.GetFactory(String providerInvariantName) +88
Altairis.Web.Security.DatabaseExtensionMethods.CreateDbConnection(ConnectionStringSettings
settings) in DatabaseExtensionMethods.cs:19
Altairis.Web.Security.TableMembershipProvider.ValidateUser(String
username, String password) in TableMembershipProvider.cs:361
I've used a decompiler to look through the code, and it seems that the internals of SQL Server CE are looking for a registry entry and then trying to do a code Assert().
Is it possible to use SQL Server CE 4.0 in a partial trust environment, by just \bin deploying the DLL ??
Yes, provided your hoster allows it - it is allowed by default in ASP.NET 4.0. Make sure you use assembly version 4.0.0.0 (and not 4.0.0.1). Read more here: http://erikej.blogspot.com/2011/10/sql-server-compact-40-under-aspnet.html

If a ASMX WebService needs a DLL where should it be put?

I'm using some 3rd party software that's failing. Their API is wrapped in a ASP web service.
The call stack tells me that it needs maybe MySQL.Data.DLL or something (not sure exactly) in the correct directory. So I'm wondering where that directory would be. The Web Server is IIS.
Here is the call stack:
Unable to find the requested .Net Framework Data Provider. It may not be installed.
at System.Data.Common.DbProviderFactories.GetFactory(String providerInvariantName)
at WebReports.Api.Data.SqlObject.CreateConnectionObject(String dbType, String dataConnStr)
at WebReports.Api.Data.SqlObject..ctor(PageInfo pageInfo, Int32 dataSourceId)
at WebReports.Api.Data.DataObjectBase.GetDataObject(PageInfo pageInfo, Int32 dataSourceId, String objectType, Boolean isSqlSpecific)
at WebReports.Api.Reports.Entity.get_DataSource()
at WebReports.Api.Reports.Entity.GetColumnProcess(String colName, Boolean isActual)
at WebReports.Api.Reports.EntityColumnsCollection.GetColumnProcess(String colNameFull, Boolean isActual)
at WebReports.Api.Common.PageInfo.GetMnemonicFromId(String id)
at WebReports.Api.Reports.KeyColumnCollection.SetColumnMnemonics()
at WebReports.Api.Reports.ReportEntityCollection.SetColumnMnemonics()
at WebReports.Api.Reports.ReportEntityCollection.LoadData(DataSet ds, Boolean readSchema)
at WebReports.Api.Reports.Report.get_Entities()
at WebReports.Api.Common.PageInfo.GetMnemonicFromId(String id)
at WebReports.Api.Reports.Cell.set_SaveText(String value)
at WebReports.Api.Reports.ReportCellCollection.LoadData(DataSet ds)
at WebReports.Api.Reports.Report.get_Cells()
at WebReports.Api.Reports.Report.UpdateVersion()
at WebReports.Api.Reports.Report.Validate(Boolean validateJoins)
at WebReports.Api.Reports.Report.LoadData(Boolean validate)
at WebReports.Api.Reports.Report.Load(String reportName)
Above this line is their API, so I can't inspect it:
at eWebReportsLETG.ReportURLService.GenerateReportURL(Int32[] list, String m_szWebReportsVirtualDirectory, String m_szWebReportsUrl, String ReportDir, String ReportName, String PkSpecialName, Boolean& Failed) in C:\dev\eWebReports\eWebReportsLETG\ReportURLService.asmx.cs:line 51
I needed to copy MySQL.data.dll to the bin directory of my web service application directory. I also added the config key to the web.config.

ASP.NET MVC 3 AntiForgeryToken and custom MachineKey configuration

We've run into some issues with ASP.NET MVC 3 AntiForgeryToken HTML helper when having a custom configured MachineKey in Web.Config. The error is easy to reproduce if you change your MachineKey to the following (taken from Microsoft HowTo-guide on how to configure machine key).
<machineKey
validationKey="21F090935F6E49C2C797F69BBAAD8402ABD2EE0B667A8B44EA7DD4374267A75D7
AD972A119482D15A4127461DB1DC347C1A63AE5F1CCFAACFF1B72A7F0A281B"
decryptionKey="ABAA84D7EC4BB56D75D217CECFFB9628809BDB8BF91CFCD64568A145BE59719F"
validation="SHA1"
decryption="AES"/>
The exception thrown by AntiForgeryToken is as follow:
[IndexOutOfRangeException: Index was outside the bounds of the array.]
System.Web.Configuration.MachineKeySection.SetInnerOuterKeys(Byte[] validationKey, Byte[]& inner, Byte[]& outer) +11499173
System.Web.Configuration.MachineKeySection.ConfigureEncryptionObject() +228
System.Web.Configuration.MachineKeySection.EnsureConfig() +287
System.Web.Configuration.MachineKeySection.HashData(Byte[] buf, Byte[] modifier, Int32 start, Int32 length) +46
System.Web.Security.MachineKey.Encode(Byte[] data, MachineKeyProtection protectionOption) +58
System.Web.Helpers.AntiForgeryDataSerializer.<.ctor>b__2(Byte[] bytes) +13
System.Web.Helpers.AntiForgeryDataSerializer.Serialize(AntiForgeryData token) +365
System.Web.Helpers.AntiForgeryWorker.GetAntiForgeryTokenAndSetCookie(HttpContextBase httpContext, String salt, String domain, String path) +326
System.Web.Helpers.AntiForgeryWorker.GetHtml(HttpContextBase httpContext, String salt, String domain, String path) +28
System.Web.Helpers.AntiForgery.GetHtml(HttpContextBase httpContext, String salt, String domain, String path) +75
System.Web.Mvc.HtmlHelper.AntiForgeryToken(String salt, String domain, String path) +48
Is this a bug in the ASP.NET MVC 3 Html Helper to generate the AntiForgeryToken? Or am I missing something in regards to configuring machine keys?
It looks like I screwed up the keys - using http://aspnetresources.com/tools/machineKey I was able to generate a valid machineKey config section.

ASP.NET security exception with OpenWebConfiguration on shared host

After moving my web site from my local development environment to a shared host I get:
Security Exception
Description: The application attempted to perform an operation not allowed by
the security policy. To grant this application the required permission please
contact your system administrator or change the application's trust level in
the configuration file.
The problem occurs in my web application everywhere the following is called:
WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath)
Since my web application is only trying to open it's own web.config file, I don't know why this is flagged as a security exception. Maybe someone can explain... But more importantly I need a solution, the couple solutions I found via Google are painful.
One solution (from numerous posts) said to configure the trust level to Full, but I'm told that is not possible on my shared host.
Another solution (from https://web.archive.org/web/20210525032809/http://www.4guysfromrolla.com/articles/100307-1.aspx) says to not use OpenWebConfiguration(), but I need to use it to encrypt configuration sections (e.g. connectionStrings) using DPAPI (for more info see https://web.archive.org/web/20211020203213/https://www.4guysfromrolla.com/articles/021506-1.aspx).
Please advise on why IIS barfs on my web application trying to open it's own web.config, and a work-around to be able to encrypt parts of the web.config using DPAPI.
I have had experience of this issue in the past. The OpenWebConfiguration() method also reads the machine.config file. Under partial trust and without the correct permissions you can't use this method.
If you were to step into the .NET Framework assemblies with your debugger in Visual Studio 2008/2010 you can see exactly what is happening.
The following is a call stack captured when stepping into WebConfigurationManager.OpenWebConfiguration():
mscorlib.dll!System.IO.FileStream.Init(string path = "C:\\Windows\\Microsoft.NET\\Framework\\v2.0.50727\\Config\\machine.config", System.IO.FileMode mode = Open, System.IO.FileAccess access = Read, int rights = 0, bool useRights = false, System.IO.FileShare share = Read, int bufferSize = 4096, System.IO.FileOptions options = None, Microsoft.Win32.Win32Native.SECURITY_ATTRIBUTES secAttrs = null, string msgPath = "machine.config", bool bFromProxy = false) Line 326 C#
mscorlib.dll!System.IO.FileStream.FileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share) Line 259 C#
System.Configuration.dll!System.Configuration.Internal.InternalConfigHost.StaticOpenStreamForRead(string streamName) + 0x56 bytes
System.Configuration.dll!System.Configuration.Internal.InternalConfigHost.System.Configuration.Internal.IInternalConfigHost.OpenStreamForRead(string streamName, bool assertPermissions) + 0x7d bytes
System.Configuration.dll!System.Configuration.Internal.InternalConfigHost.System.Configuration.Internal.IInternalConfigHost.OpenStreamForRead(string streamName) + 0xb bytes
System.Configuration.dll!System.Configuration.Internal.DelegatingConfigHost.OpenStreamForRead(string streamName) + 0xe bytes
System.Configuration.dll!System.Configuration.UpdateConfigHost.OpenStreamForRead(string streamName) + 0x2f bytes
System.Configuration.dll!System.Configuration.BaseConfigurationRecord.InitConfigFromFile() + 0x126 bytes
System.Configuration.dll!System.Configuration.BaseConfigurationRecord.Init(System.Configuration.Internal.IInternalConfigRoot configRoot, System.Configuration.BaseConfigurationRecord parent, string configPath, string locationSubPath) + 0xaa5 bytes
System.Configuration.dll!System.Configuration.MgmtConfigurationRecord.Init(System.Configuration.Internal.IInternalConfigRoot configRoot, System.Configuration.Internal.IInternalConfigRecord parent, string configPath, string locationSubPath) + 0x39 bytes
System.Configuration.dll!System.Configuration.MgmtConfigurationRecord.Create(System.Configuration.Internal.IInternalConfigRoot configRoot, System.Configuration.Internal.IInternalConfigRecord parent, string configPath, string locationSubPath) + 0x2a bytes
System.Configuration.dll!System.Configuration.Internal.InternalConfigRoot.GetConfigRecord(string configPath) + 0x12d bytes
System.Configuration.dll!System.Configuration.Configuration.Configuration(string locationSubPath, System.Type typeConfigHost, object[] hostInitConfigurationParams) + 0xfd bytes
System.Configuration.dll!System.Configuration.Internal.InternalConfigConfigurationFactory.System.Configuration.Internal.IInternalConfigConfigurationFactory.Create(System.Type typeConfigHost, object[] hostInitConfigurationParams) + 0x1e bytes
System.Web.dll!System.Web.Configuration.WebConfigurationHost.OpenConfiguration(System.Web.Configuration.WebLevel webLevel, System.Configuration.ConfigurationFileMap fileMap, System.Web.VirtualPath path, string site, string locationSubPath, string server, string userName, string password, System.IntPtr tokenHandle) Line 862 C#
System.Web.dll!System.Web.Configuration.WebConfigurationManager.OpenWebConfigurationImpl(System.Web.Configuration.WebLevel webLevel, System.Configuration.ConfigurationFileMap fileMap, string path, string site, string locationSubPath, string server, string userName, string password, System.IntPtr userToken) Line 77 + 0x1c bytes C#
System.Web.dll!System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(string path) Line 140 + 0x25 bytes C#
Unfortunately your only alternative is to use WebConfigurationManager.GetSection() which isn't as feature rich.
With regard to encrypting your connection strings. Sadly this feature demands Full Trust, there's no other way around it.

Resources