safe and secure HTTPCookie storage in ASP.NET MVC C# application - c#

I'm using the below class to handle cookies and use them to store/read values in my ASP.NET MVC application (such as shopping cart items, etc.)
1.I want to know if values are stored without any security in the browser and anyone can look inside its content (using the below implementation)? I checked that values are stored as some hexadecimal values but I doubt that any specific encryption/security exists in this implementation.
2.How can I modify this class to store cookie values as encrypted information?
using System;
using System.Web;
namespace My.Application.Sample
{
public class CookieStore
{
public static void SetCookie(string key, string value)
{
SetCookie(key, value, TimeSpan.FromDays(14));
}
public static void SetCookie(string key, string value, TimeSpan expires)
{
string encodedValue = HttpUtility.UrlEncode(value);
HttpCookie encodedCookie = new HttpCookie(key, encodedValue);
if (HttpContext.Current.Request.Cookies[key] != null)
{
var cookieOld = HttpContext.Current.Request.Cookies[key];
cookieOld.Expires = DateTime.Now.Add(expires);
cookieOld.Value = encodedCookie.Value;
HttpContext.Current.Response.Cookies.Add(cookieOld);
}
else
{
encodedCookie.Expires = DateTime.Now.Add(expires);
HttpContext.Current.Response.Cookies.Add(encodedCookie);
}
}
/// <summary>
/// Return value stored in a cookie by defined key, if not found returns empty string
/// </summary>
/// <param name="key"></param>
/// <returns> never returns null! :) </returns>
public static string GetCookie(string key)
{
string value = string.Empty;
try
{
HttpCookie cookie = HttpContext.Current.Request.Cookies[key];
//if (cookie != null)
//{
// // For security purpose, we need to encrypt the value.
// HttpCookie decodedCookie = HttpSecureCookie.Decode(cookie);
// value = decodedCookie.Value;
//}
if (cookie != null)
{
string encodedValue = cookie.Value;
value = HttpUtility.UrlDecode(encodedValue);
}
}
catch (Exception)
{
}
return value;
}
}
}

You can use the Protect and Unprotect methods to encrypt cookies. Note that both bytes have the same key value. Data encrypted with Protect can only be decrypted with Unprotect.
encrypted method
public string encryptedCookie(string value)
{
var cookieText = Encoding.UTF8.GetBytes(value);
var encryptedValue = Convert.ToBase64String(MachineKey.Protect(cookieText, "ProtectCookie"));
return encryptedValue;
}
decrypted method
public string decryptedCookie(string value)
{
var bytes = Convert.FromBase64String(value);
var output = MachineKey.Unprotect(bytes, "ProtectCookie");
string result = Encoding.UTF8.GetString(output);
return result;
}
Instead of "ProtectCookie", you can use your unique key.

Related

How can i modify registry via c# on win7?

I modify my WIN7 computer's registry via c#,but it dosen't work.
my code likes below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32; //添加针对操作注册表的应用
namespace operateToolWPF.Utils
{
class RegisterHelper
{
public static string GetRegistryData(RegistryKey root, string subKey,
string name)
{
string registData = string.Empty;
RegistryKey myKey = root.OpenSubKey(subKey, true);
if (myKey != null)
{
registData = myKey.GetValue(name).ToString();
}
return registData;
}
/// <summary>
/// 向注册表中写数据
/// </summary>
/// <param name="root"></param>
/// <param name="subKey"></param>
/// <param name="keyName"></param>
/// <param name="keyValue"></param>
public static void SetRegistryData(RegistryKey root, string subKey, string keyName, Int32 keyValue)
{
RegistryKey aimDir = root.CreateSubKey(subKey);
aimDir.SetValue(keyName, keyValue, RegistryValueKind.DWord);
}
/// <summary>
/// 删除注册表中指定的注册项
/// </summary>
/// <param name="root"></param>
/// <param name="subKey"></param>
/// <param name="keyName"></param>
public static void DeleteRegist(RegistryKey root, string subKey, string keyName)
{
string[] subkeyNames;
RegistryKey myKey = root.OpenSubKey(subKey, true);
subkeyNames = myKey.GetSubKeyNames();
foreach (string aimKey in subkeyNames)
{
if (aimKey == keyName)
myKey.DeleteSubKeyTree(keyName);
}
}
/// <summary>
/// 判断指定注册表项是否存在
/// </summary>
/// <param name="root"></param>
/// <param name="subKey"></param>
/// <param name="keyName"></param>
/// <returns></returns>
public static bool IsRegistryExits(RegistryKey root, string subKey, string keyName)
{
bool result = false;
string[] subKeyNames;
RegistryKey myKey = root.OpenSubKey(subKey, true);
subKeyNames = myKey.GetValueNames();
foreach (string name in subKeyNames)
{
if (name == keyName)
{
result = true;
return result;
}
}
return result;
}
}
}
and then call it like this:
//获取当前Windows用户
WindowsIdentity curUser = WindowsIdentity.GetCurrent();
//用户SID
SecurityIdentifier sid = curUser.User;
//用户全称
NTAccount ntacc = (NTAccount)sid.Translate(typeof(NTAccount));
Console.WriteLine("Windows SID:" + sid.Value);
Console.WriteLine("用户全称:" + ntacc.Value);
Int32 tempInt = 0; //预先定义一个有符号32位数
//unchecked语句块内的转换,不做溢出检查
unchecked
{
tempInt = Convert.ToInt32("00000000", 16); //强制转换成有符号32位数
}
//读取Display Inline Images
string displayImgPath = sid.Value + #"\Software\Microsoft\Windows\CurrentVersion\Internet Settings";
string ProxyEnable = RegisterHelper.GetRegistryData(Registry.Users, displayImgPath, "ProxyEnable");
//此时的tempInt已经是有符号32位数,可以直接写入注册表
RegisterHelper.SetRegistryData(Registry.Users, displayImgPath, "ProxyEnable", tempInt);
Thread.Sleep(3000);
RegisterHelper.DeleteRegist(Registry.Users, displayImgPath, "ProxyServer");
RegisterHelper.DeleteRegist(Registry.Users, displayImgPath, "ProxyOverride");
Registry.Users.Close();
Process[] MyProcess = Process.GetProcessesByName("explorer");
MyProcess[0].Kill();
by all this ,i want modify ProxyEnable and delete ProxyOverride,ProxyServer,which was cancel IE proxy setting.
I have tried several methodes,but have no one can cancel IE proxy setting.
Can you help me? Thanks!
Here is an example of how I would implement registry IO as you have tried to do. Depending on which part of the registry you are trying to read/write from may use different keys:
public class MyReg{
public RegistryKey Foriegnkey {
get => forignKey;
set => forignKey = value;
}
private RegistryKey forignKey;
public object Read(string Path, string Name) => (Registry.CurrentUser.OpenSubKey(Path, false).GetValue(Name));
public void Write(string Path, string Name, object Data) {
Foriegnkey = Registry.CurrentUser.CreateSubKey(Path, RegistryKeyPermissionCheck.Default);
Foriegnkey.SetValue(Name, Data);
Foriegnkey.Close();
}
}
The example above will read / write at Current User level, but there are other levels which can be used, and you will see these as available options within IntelliSense
You can use this in your application by assigning an instance of your registry class to an object and just calling registry.read/write etc …
This can be checked for nulls using :
if (Registry.GetValue(#"HKEY_CURRENT_USER\Software\MyApp", "SomeValue", null) == null)
And when you come to write data you can use:
myregobject.Write(#"\software\MyApp", "SomeValue", "hello world!");
In your case this enables you to do the following:
if (!Registry.GetValue(#"\Software\Microsoft\Windows\CurrentVersion\Internet Settings", "ProxyEnable", null) == null) {
myregobject.Write(#"\Software\Microsoft\Windows\CurrentVersion\Internet Settings", "ProxyEnable", "your data here")
}
I cant tell if your delete method works or not from looking at it so i'll throw in my input there as well:
public void RemoveKey(string FolderName) {
Registry.CurrentUser.DeleteSubKeyTree(FolderName);
}
Hope this helps!

Identity Framework and Custom Password Hasher

I have added identity framework to my WebApi and followed the steps outlined here:
http://bitoftech.net/2015/01/21/asp-net-identity-2-with-asp-net-web-api-2-accounts-management/
All of this is working fine.
The problem I have, is that my client has another system of which the API integrates with (to collect data) and that has it's own login methods. So, with that in mind, my client has asked me to use a CustomPasswordHasher to encrypt and decrypt passwords.
What they would like to do is be able to get a password hash and convert it into the actual password so that they can use it to log into the old system (both passwords / accounts will be the same).
I know this is very unothadox but I have no choice in the matter.
My question is, how easy is this to do?
I have found a few topics on how to create a custom password hasher, but none show me how to actually get the password from the hashed password, they only show how to compare.
Currently I have this:
public class PasswordHasher : IPasswordHasher
{
private readonly int _saltSize;
private readonly int _bytesRequired;
private readonly int _iterations;
public PasswordHasher()
{
this._saltSize = 128 / 8;
this._bytesRequired = 32;
this._iterations = 1000;
}
public string HashPassword(string password)
{
// Create our defaults
var array = new byte[1 + this._saltSize + this._bytesRequired];
// Try to hash our password
using (var pbkdf2 = new Rfc2898DeriveBytes(password, this._saltSize, this._iterations))
{
var salt = pbkdf2.Salt;
Buffer.BlockCopy(salt, 0, array, 1, this._saltSize);
var bytes = pbkdf2.GetBytes(this._bytesRequired);
Buffer.BlockCopy(bytes, 0, array, this._saltSize + 1, this._bytesRequired);
}
// Return the password base64 encoded
return Convert.ToBase64String(array);
}
public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
// Throw an error if any of our passwords are null
ThrowIf.ArgumentIsNull(() => hashedPassword, () => providedPassword);
// Get our decoded hash
var decodedHashedPassword = Convert.FromBase64String(hashedPassword);
// If our password length is 0, return an error
if (decodedHashedPassword.Length == 0)
return PasswordVerificationResult.Failed;
var t = decodedHashedPassword[0];
// Do a switch
switch (decodedHashedPassword[0])
{
case 0x00:
return PasswordVerificationResult.Success;
default:
return PasswordVerificationResult.Failed;
}
}
private bool VerifyHashedPassword(byte[] hashedPassword, string password)
{
// If we are not matching the original byte length, then we do not match
if (hashedPassword.Length != 1 + this._saltSize + this._bytesRequired)
return false;
//// Get our salt
//var salt = pbkdf2.Salt;
//Buffer.BlockCopy(salt, 0, array, 1, this._saltSize);
//var bytes = pbkdf2.GetBytes(this._bytesRequired);
//Buffer.BlockCopy(bytes, 0, array, this._saltSize + 1, this._bytesRequired);
return true;
}
}
If I really wanted to I could just do this:
public class PasswordHasher : IPasswordHasher
{
public string HashPassword(string password)
{
// Do no hashing
return password;
}
public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
// Throw an error if any of our passwords are null
ThrowIf.ArgumentIsNull(() => hashedPassword, () => providedPassword);
// Just check if the two values are the same
if (hashedPassword.Equals(providedPassword))
return PasswordVerificationResult.Success;
// Fallback
return PasswordVerificationResult.Failed;
}
}
but that would be crazy, because all the passwords would be stored as plain text. Surely there is a way to "encrypt" the password and "decrypt" it when I make a call?
So, I have tried to be as secure as possible. This is what I have done.
I created a new provider:
public class AdvancedEncryptionStandardProvider
{
// Private properties
private readonly ICryptoTransform _encryptor, _decryptor;
private UTF8Encoding _encoder;
/// <summary>
/// Default constructor
/// </summary>
/// <param name="key">Our shared key</param>
/// <param name="secret">Our secret</param>
public AdvancedEncryptionStandardProvider(string key, string secret)
{
// Create our encoder
this._encoder = new UTF8Encoding();
// Get our bytes
var _key = _encoder.GetBytes(key);
var _secret = _encoder.GetBytes(secret);
// Create our encryptor and decryptor
var managedAlgorithm = new RijndaelManaged();
managedAlgorithm.BlockSize = 128;
managedAlgorithm.KeySize = 128;
this._encryptor = managedAlgorithm.CreateEncryptor(_key, _secret);
this._decryptor = managedAlgorithm.CreateDecryptor(_key, _secret);
}
/// <summary>
/// Encrypt a string
/// </summary>
/// <param name="unencrypted">The un-encrypted string</param>
/// <returns></returns>
public string Encrypt(string unencrypted)
{
return Convert.ToBase64String(Encrypt(this._encoder.GetBytes(unencrypted)));
}
/// <summary>
/// Decrypt a string
/// </summary>
/// <param name="encrypted">The encrypted string</param>
/// <returns></returns>
public string Decrypt(string encrypted)
{
return this._encoder.GetString(Decrypt(Convert.FromBase64String(encrypted)));
}
/// <summary>
/// Encrypt some bytes
/// </summary>
/// <param name="buffer">The bytes to encrypt</param>
/// <returns></returns>
public byte[] Encrypt(byte[] buffer)
{
return Transform(buffer, this._encryptor);
}
/// <summary>
/// Decrypt some bytes
/// </summary>
/// <param name="buffer">The bytes to decrypt</param>
/// <returns></returns>
public byte[] Decrypt(byte[] buffer)
{
return Transform(buffer, this._decryptor);
}
/// <summary>
/// Writes bytes to memory
/// </summary>
/// <param name="buffer">The bytes</param>
/// <param name="transform"></param>
/// <returns></returns>
protected byte[] Transform(byte[] buffer, ICryptoTransform transform)
{
// Create our memory stream
var stream = new MemoryStream();
// Write our bytes to the stream
using (var cs = new CryptoStream(stream, transform, CryptoStreamMode.Write))
{
cs.Write(buffer, 0, buffer.Length);
}
// Retrun the stream as an array
return stream.ToArray();
}
}
Then my PasswordHasher, I changed to this:
public class PasswordHasher : IPasswordHasher
{
// Private properties
private readonly AdvancedEncryptionStandardProvider _provider;
public PasswordHasher(AdvancedEncryptionStandardProvider provider)
{
this._provider = provider;
}
public string HashPassword(string password)
{
// Do no hashing
return this._provider.Encrypt(password);
}
public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
// Throw an error if any of our passwords are null
ThrowIf.ArgumentIsNull(() => hashedPassword, () => providedPassword);
// Just check if the two values are the same
if (hashedPassword.Equals(this.HashPassword(providedPassword)))
return PasswordVerificationResult.Success;
// Fallback
return PasswordVerificationResult.Failed;
}
}
To use this PasswordHasher, you invoke it like this:
var passwordHasher = new PasswordHasher(new AdvancedEncryptionStandardProvider(ConfigurationManager.AppSettings["as:key"], ConfigurationManager.AppSettings["as:secret"]));
This seems to satisfy my conditions. Let me know if there are security risks please!

If there are Int32[] properties in user.config, I get an invalid cast exception when I use my CustomSettingsProvider

I followed this article and I created my CustomSettingsProvider in order to get rid of the "_url_somehash" part of the path where the user.config file is stored. Now my settings are stored in <LocalApplicationData>\CompanyName\ProductName\Version\user.config as I wanted.
My user.config file (written by my application before creating my CustomSettingsProvider) contains one Int32[] property that was stored and loaded correctly by the default SettingsProvider. When I use my CustomSettingsProvider I get the following exception:
Exception InvalidCastException
Source = mscorlib
Message = Invalid cast from 'System.String' to 'System.Int32[]'.
TargetSite = System.Object DefaultToType(System.IConvertible, System.Type, System.IFormatProvider)
Stack =
System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)
System.String.System.IConvertible.ToType(Type type, IFormatProvider provider)
System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
System.Convert.ChangeType(Object value, Type conversionType)
MyApp.Interface.CustomSettingsProvider.GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection) in d:\Users\angelo\Documents\Visual Studio 2013\Projects\MyApp\MyApp\Interface\CustomSettingsProvider.cs:line 112
System.Configuration.SettingsBase.GetPropertiesFromProvider(SettingsProvider provider)
System.Configuration.SettingsBase.GetPropertyValueByName(String propertyName)
System.Configuration.SettingsBase.get_Item(String propertyName)
System.Configuration.ApplicationSettingsBase.GetPropertyValue(String propertyName)
System.Configuration.ApplicationSettingsBase.get_Item(String propertyName)
MyApp.Properties.Settings.get_UpgradeRequired() in d:\Users\angelo\Documents\Visual Studio 2013\Projects\MyApp\MyApp\Properties\Settings.Designer.cs:line 31
MyApp.Interface.Program.Run() in d:\Users\angelo\Documents\Visual Studio 2013\Projects\MyApp\MyApp\Interface\Program.cs:line 51
MyApp.Interface.Program.Main() in d:\Users\angelo\Documents\Visual Studio 2013\Projects\MyApp\MyApp\Interface\Program.cs:line 34
How can I fix this problem? In a more general way, how can I store collections and classes in the same way I can do it with the default SettingsProvider?
This is the full code of my CustomSettingsProvider class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Reflection;
using System.Xml.Linq;
using System.IO;
// ==>>>> https://stackoverflow.com/questions/2265271/custom-path-of-the-user-config
// https://stackoverflow.com/questions/1947185/c-sharp-get-special-folder
namespace MyApp.Interface
{
class CustomSettingsProvider : SettingsProvider
{
#region Helper struct
/// <summary>
/// Helper struct.
/// </summary>
internal struct SettingStruct
{
internal string name;
internal string serializeAs;
internal string value;
}
#endregion
#region Constants
const string NAME = "name";
const string SERIALIZE_AS = "serializeAs";
const string CONFIG = "configuration";
const string USER_SETTINGS = "userSettings";
const string SETTING = "setting";
#endregion
#region Fields
bool _loaded;
#endregion
#region Properties
/// <summary>
/// Override.
/// </summary>
public override string ApplicationName { get { return System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name; } set { /*do nothing*/ } }
/// <summary>
/// The setting key this is returning must set before the settings are used.
/// e.g. <c>Properties.Settings.Default.SettingsKey = #"C:\temp\user.config";</c>
/// </summary>
private string UserConfigPath
{
get
{
System.Diagnostics.FileVersionInfo versionInfo;
string strUserConfigPath, strUserConfigFolder;
strUserConfigPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create);
versionInfo = System.Diagnostics.FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location);
strUserConfigPath = Path.Combine(strUserConfigPath, versionInfo.CompanyName, versionInfo.ProductName, versionInfo.ProductVersion, "user.config");
strUserConfigFolder = Path.GetDirectoryName(strUserConfigPath);
if(!Directory.Exists(strUserConfigFolder))
Directory.CreateDirectory(strUserConfigFolder);
return strUserConfigPath;
}
}
/// <summary>
/// In memory storage of the settings values
/// </summary>
private Dictionary<string, SettingStruct> SettingsDictionary { get; set; }
#endregion
#region Constructor
/// <summary>
/// Loads the file into memory.
/// </summary>
public CustomSettingsProvider()
{
SettingsDictionary = new Dictionary<string, SettingStruct>();
}
/// <summary>
/// Override.
/// </summary>
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(ApplicationName, config);
}
#endregion
/// <summary>
/// Must override this, this is the bit that matches up the designer properties to the dictionary values
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
/// <returns></returns>
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
{
//load the file
if(!_loaded)
{
_loaded = true;
LoadValuesFromFile();
}
//collection that will be returned.
SettingsPropertyValueCollection values = new SettingsPropertyValueCollection();
//iterate thought the properties we get from the designer, checking to see if the setting is in the dictionary
foreach(SettingsProperty setting in collection)
{
SettingsPropertyValue value = new SettingsPropertyValue(setting);
value.IsDirty = false;
//need the type of the value for the strong typing
var t = Type.GetType(setting.PropertyType.FullName);
if(SettingsDictionary.ContainsKey(setting.Name))
{
value.SerializedValue = SettingsDictionary[setting.Name].value;
value.PropertyValue = Convert.ChangeType(SettingsDictionary[setting.Name].value, t);
}
else //use defaults in the case where there are no settings yet
{
value.SerializedValue = setting.DefaultValue;
value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t);
}
values.Add(value);
}
return values;
}
/// <summary>
/// Must override this, this is the bit that does the saving to file. Called when Settings.Save() is called
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
{
//grab the values from the collection parameter and update the values in our dictionary.
foreach(SettingsPropertyValue value in collection)
{
var setting = new SettingStruct()
{
value = (value.PropertyValue == null ? String.Empty : value.PropertyValue.ToString()),
name = value.Name,
serializeAs = value.Property.SerializeAs.ToString()
};
if(!SettingsDictionary.ContainsKey(value.Name))
SettingsDictionary.Add(value.Name, setting);
else
SettingsDictionary[value.Name] = setting;
}
//now that our local dictionary is up-to-date, save it to disk.
SaveValuesToFile();
}
/// <summary>
/// Loads the values of the file into memory.
/// </summary>
private void LoadValuesFromFile()
{
string strUserConfigPath;
strUserConfigPath = UserConfigPath;
//if the config file is not where it's supposed to be create a new one.
if(!File.Exists(strUserConfigPath))
CreateEmptyConfig(strUserConfigPath);
//System.Security.Policy.StrongName strongName = new System.Security.Policy.StrongName(
//ClickOnce
//load the xml
var configXml = XDocument.Load(UserConfigPath);
//get all of the <setting name="..." serializeAs="..."> elements.
var settingElements = configXml.Element(CONFIG).Element(USER_SETTINGS).Element(typeof(Properties.Settings).FullName).Elements(SETTING);
//iterate through, adding them to the dictionary, (checking for nulls, xml no likey nulls)
//using "String" as default serializeAs...just in case, no real good reason.
foreach(var element in settingElements)
{
var newSetting = new SettingStruct()
{
name = element.Attribute(NAME) == null ? String.Empty : element.Attribute(NAME).Value,
serializeAs = element.Attribute(SERIALIZE_AS) == null ? "String" : element.Attribute(SERIALIZE_AS).Value,
value = element.Value ?? String.Empty
};
SettingsDictionary.Add(element.Attribute(NAME).Value, newSetting);
}
}
/// <summary>
/// Creates an empty user.config file...looks like the one MS creates.
/// This could be overkill a simple key/value pairing would probably do.
/// </summary>
private void CreateEmptyConfig(string strUserConfigPath)
{
Configuration config1;
config1 = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
if(File.Exists(config1.FilePath))
{
File.Copy(config1.FilePath, strUserConfigPath);
}
else
{
string s = Properties.Settings.Default.LastLoadedImage;
var doc = new XDocument();
var declaration = new XDeclaration("1.0", "utf-8", "true");
var config = new XElement(CONFIG);
var userSettings = new XElement(USER_SETTINGS);
var group = new XElement(typeof(Properties.Settings).FullName);
userSettings.Add(group);
config.Add(userSettings);
doc.Add(config);
doc.Declaration = declaration;
doc.Save(strUserConfigPath);
}
}
/// <summary>
/// Saves the in memory dictionary to the user config file
/// </summary>
private void SaveValuesToFile()
{
//load the current xml from the file.
var import = XDocument.Load(UserConfigPath);
//get the settings group (e.g. <Company.Project.Desktop.Settings>)
var settingsSection = import.Element(CONFIG).Element(USER_SETTINGS).Element(typeof(Properties.Settings).FullName);
//iterate though the dictionary, either updating the value or adding the new setting.
foreach(var entry in SettingsDictionary)
{
var setting = settingsSection.Elements().FirstOrDefault(e => e.Attribute(NAME).Value == entry.Key);
if(setting == null) //this can happen if a new setting is added via the .settings designer.
{
var newSetting = new XElement(SETTING);
newSetting.Add(new XAttribute(NAME, entry.Value.name));
newSetting.Add(new XAttribute(SERIALIZE_AS, entry.Value.serializeAs));
newSetting.Value = (entry.Value.value ?? String.Empty);
settingsSection.Add(newSetting);
}
else //update the value if it exists.
{
setting.Value = (entry.Value.value ?? String.Empty);
}
}
import.Save(UserConfigPath);
}
#region Angelo
private object GetDefaultValue(SettingsProperty setting)
{
if (setting.PropertyType.IsEnum)
return Enum.Parse(setting.PropertyType, setting.DefaultValue.ToString());
// Return the default value if it is set
// Return the default value if it is set
if (setting.DefaultValue != null)
{
System.ComponentModel.TypeConverter tc = System.ComponentModel.TypeDescriptor.GetConverter(setting.PropertyType);
return tc.ConvertFromString(setting.DefaultValue.ToString());
}
else // If there is no default value return the default object
{
return Activator.CreateInstance(setting.PropertyType);
}
}
#endregion
}
}
To read properties that are serialized as XML, you first need to deserialize them.
Probably the easiest way is to add a new method called something like getPropertyValue, which determines whether it should return the string value directly or deserialize it first. Then, in your code shown below, you can just call this method instead of using Convert.ChangeType to set property values:
var t = Type.GetType(setting.PropertyType.FullName);
if (SettingsDictionary.ContainsKey(setting.Name))
{
value.SerializedValue = SettingsDictionary[setting.Name].value;
// value.PropertyValue = Convert.ChangeType(SettingsDictionary[setting.Name].value, t);
value.PropertyValue = getPropertyValue(SettingsDictionary[setting.Name].value, t, setting.SerializeAs);
}
else //use defaults in the case where there are no settings yet
{
value.SerializedValue = setting.DefaultValue;
// value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t);
value.PropertyValue = getPropertyValue((string)setting.DefaultValue, t, setting.SerializeAs);
}
An example of how your new method getPropertyValue might work:
private object getPropertyValue(string settingValue, Type settingType, SettingsSerializeAs serializeAs)
{
switch (serializeAs)
{
case SettingsSerializeAs.String:
return settingValue;
case SettingsSerializeAs.Xml:
//for demo purposes, assumes this is your int array--otherwise do further checking to get the correct type
XmlSerializer serializer = new XmlSerializer(typeof(int[]));
return serializer.Deserialize(new StringReader(settingValue));
//implement further types as required
default:
throw new NotImplementedException(string.Format("Settings deserialization as {0} is not implemented", serializeAs));
}
}
This will resolve the invalid cast error and load your array of integers into the setting.
You'll need to apply the corresponding treatment when you save the settings. If you hit complications with that, I suggest you post a new question as the issues are somewhat different.

Google analytics with web application Error

private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "XXXXXXXXX",
ClientSecret = "XXXXXXXXXXXX"
},
Scopes = new[] { AnalyticsService.Scope.AnalyticsReadonly, AnalyticsService.Scope.AnalyticsEdit },
DataStore = new FileDataStore("Analytics.Auth.Store")//new FileDataStore("Drive.Api.Auth.Store")
});
I am using above code for google console web application(Google Analytic) but it gives error System.UnauthorizedAccessException: Access to the path 'Analytics.Auth.Store' is denied.
FileDataStore stores the data in %AppData% on the pc. You need to make sure that you have access to that.
If you are planning on running this from a webserver you should not be using FileDataStore. You should create your own implementation of iDataStore, this will enable you to store the refresh tokens in the database.
Example:
///
/// Saved data store that implements .
/// This Saved data store stores a StoredResponse object.
///
class SavedDataStore : IDataStore
{
public StoredResponse _storedResponse { get; set; }
///
/// Constructs Load previously saved StoredResponse.
///
///Stored response
public SavedDataStore(StoredResponse pResponse)
{
this._storedResponse = pResponse;
}
public SavedDataStore()
{
this._storedResponse = new StoredResponse();
}
///
/// Stores the given value. into storedResponse
/// .
///
///The type to store in the data store
///The key
///The value to store in the data store
public Task StoreAsync(string key, T value)
{
var serialized = NewtonsoftJsonSerializer.Instance.Serialize(value);
JObject jObject = JObject.Parse(serialized);
// storing access token
var test = jObject.SelectToken("access_token");
if (test != null)
{
this._storedResponse.access_token = (string)test;
}
// storing token type
test = jObject.SelectToken("token_type");
if (test != null)
{
this._storedResponse.token_type = (string)test;
}
test = jObject.SelectToken("expires_in");
if (test != null)
{
this._storedResponse.expires_in = (long?)test;
}
test = jObject.SelectToken("refresh_token");
if (test != null)
{
this._storedResponse.refresh_token = (string)test;
}
test = jObject.SelectToken("Issued");
if (test != null)
{
this._storedResponse.Issued = (string)test;
}
return TaskEx.Delay(0);
}
///
/// Deletes StoredResponse.
///
///The key to delete from the data store
public Task DeleteAsync(string key)
{
this._storedResponse = new StoredResponse();
return TaskEx.Delay(0);
}
///
/// Returns the stored value for_storedResponse
///The type to retrieve
///The key to retrieve from the data store
/// The stored object
public Task GetAsync(string key)
{
TaskCompletionSource tcs = new TaskCompletionSource();
try
{
string JsonData = Newtonsoft.Json.JsonConvert.SerializeObject(this._storedResponse);
tcs.SetResult(Google.Apis.Json.NewtonsoftJsonSerializer.Instance.Deserialize(JsonData));
}
catch (Exception ex)
{
tcs.SetException(ex);
}
return tcs.Task;
}
///
/// Clears all values in the data store.
///
public Task ClearAsync()
{
this._storedResponse = new StoredResponse();
return TaskEx.Delay(0);
}
///// Creates a unique stored key based on the key and the class type.
/////The object key
/////The type to store or retrieve
//public static string GenerateStoredKey(string key, Type t)
//{
// return string.Format("{0}-{1}", t.FullName, key);
//}
}
Then instead of using FileDataStore you use your new SavedDataStore
//Now we load our saved refreshToken.
StoredResponse myStoredResponse = new StoredResponse(tbRefreshToken.Text);
// Now we pass a SavedDatastore with our StoredResponse.
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets { ClientId = "YourClientId", ClientSecret = "YourClientSecret" },
new[] { AnalyticsService.Scope.AnalyticsReadonly},
"user",
CancellationToken.None,
new SavedDataStore(myStoredResponse)).Result; }
This is because you dont have write access to AppData folder on a Webserver and FileDataStore uses that folder by default.
You can use a different folder by giving full path as parameter
FileDataStore(string folder, bool fullPath = false)
sample implementation
static FileDataStore GetFileDataStore()
{
var path = HttpContext.Current.Server.MapPath("~/App_Data/Drive.Api.Auth.Store");
var store = new FileDataStore(path, fullPath: true);
return store;
}
This way FileDataStore uses the App_Data folder of your application to write the TokenResponse. Dont forget to give write access to App_Data folder on the Webserver
You can read more about this at here and here

Google .NET APIs - any other DataStore other than the FileDataStore?

I'm using the Google .NET API to get analytics data from google analytics.
this is me code to start the authentication:
IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = googleApiClientId,
ClientSecret = googleApiClientSecret
},
Scopes = new[] {
Google.Apis.Analytics.v3.AnalyticsService.Scope.AnalyticsReadonly
},
DataStore = new Google.Apis.Util.Store.FileDataStore("Test_GoogleApi")
});
it users the FileDataStore which stores in local user profile as a file. I'm running this code inside an ASP.NET application so I can't really use that FileDataStore so what I will need is some other way to get the data.
Google.Apis.Util.Store contains only the FileDataStore and an interface of IDataStore. Before I go and implement my own DataStore - are there any other DataStore objects out there available for download?
Thanks
The source for Google's FileDataStore is available here.
I've written a simple Entity Framework (version 6) implementation of IDataStore as shown below.
If you're looking to put this in a separate project, as well as EF you'll need the Google.Apis.Core nuget package installed.
public class Item
{
[Key]
[MaxLength(100)]
public string Key { get; set; }
[MaxLength(500)]
public string Value { get; set; }
}
public class GoogleAuthContext : DbContext
{
public DbSet<Item> Items { get; set; }
}
public class EFDataStore : IDataStore
{
public async Task ClearAsync()
{
using (var context = new GoogleAuthContext())
{
var objectContext = ((IObjectContextAdapter)context).ObjectContext;
await objectContext.ExecuteStoreCommandAsync("TRUNCATE TABLE [Items]");
}
}
public async Task DeleteAsync<T>(string key)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("Key MUST have a value");
}
using (var context = new GoogleAuthContext())
{
var generatedKey = GenerateStoredKey(key, typeof(T));
var item = context.Items.FirstOrDefault(x => x.Key == generatedKey);
if (item != null)
{
context.Items.Remove(item);
await context.SaveChangesAsync();
}
}
}
public Task<T> GetAsync<T>(string key)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("Key MUST have a value");
}
using (var context = new GoogleAuthContext())
{
var generatedKey = GenerateStoredKey(key, typeof(T));
var item = context.Items.FirstOrDefault(x => x.Key == generatedKey);
T value = item == null ? default(T) : JsonConvert.DeserializeObject<T>(item.Value);
return Task.FromResult<T>(value);
}
}
public async Task StoreAsync<T>(string key, T value)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("Key MUST have a value");
}
using (var context = new GoogleAuthContext())
{
var generatedKey = GenerateStoredKey(key, typeof (T));
string json = JsonConvert.SerializeObject(value);
var item = await context.Items.SingleOrDefaultAsync(x => x.Key == generatedKey);
if (item == null)
{
context.Items.Add(new Item { Key = generatedKey, Value = json});
}
else
{
item.Value = json;
}
await context.SaveChangesAsync();
}
}
private static string GenerateStoredKey(string key, Type t)
{
return string.Format("{0}-{1}", t.FullName, key);
}
}
I know this question was answered some time ago, however I thought this would be a good place to share my findings for those having similar difficulty finding examples. I have found it to be difficult to find documentation/samples on using the Google APIs .Net library for an Desktop or MVC Web application. I finally found a good example in the tasks example you can find in the samples repository on the Google Project site here <- That really really helped me out.
I ended up snagging the source for the FileDataStore and created an AppDataStore class and placed it in my App_Code folder. You can find the source here, though it was a simple change really - changing the folder to point to ~/App_Data instead.
The last piece of the puzzle I'm looking into figuring out is getting the offline_access token.
Edit: Here's the code for convenience:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Util.Store;
using Google.Apis.Json;
namespace Google.Apis.Util.Store {
public class AppDataFileStore : IDataStore {
readonly string folderPath;
/// <summary>Gets the full folder path.</summary>
public string FolderPath { get { return folderPath; } }
/// <summary>
/// Constructs a new file data store with the specified folder. This folder is created (if it doesn't exist
/// yet) under <see cref="Environment.SpecialFolder.ApplicationData"/>.
/// </summary>
/// <param name="folder">Folder name.</param>
public AppDataFileStore(string folder) {
folderPath = Path.Combine(HttpContext.Current.Server.MapPath("~/App_Data/"), folder);
if (!Directory.Exists(folderPath)) {
Directory.CreateDirectory(folderPath);
}
}
/// <summary>
/// Stores the given value for the given key. It creates a new file (named <see cref="GenerateStoredKey"/>) in
/// <see cref="FolderPath"/>.
/// </summary>
/// <typeparam name="T">The type to store in the data store.</typeparam>
/// <param name="key">The key.</param>
/// <param name="value">The value to store in the data store.</param>
public Task StoreAsync<T>(string key, T value) {
if (string.IsNullOrEmpty(key)) {
throw new ArgumentException("Key MUST have a value");
}
var serialized = NewtonsoftJsonSerializer.Instance.Serialize(value);
var filePath = Path.Combine(folderPath, GenerateStoredKey(key, typeof(T)));
File.WriteAllText(filePath, serialized);
return TaskEx.Delay(0);
}
/// <summary>
/// Deletes the given key. It deletes the <see cref="GenerateStoredKey"/> named file in
/// <see cref="FolderPath"/>.
/// </summary>
/// <param name="key">The key to delete from the data store.</param>
public Task DeleteAsync<T>(string key) {
if (string.IsNullOrEmpty(key)) {
throw new ArgumentException("Key MUST have a value");
}
var filePath = Path.Combine(folderPath, GenerateStoredKey(key, typeof(T)));
if (File.Exists(filePath)) {
File.Delete(filePath);
}
return TaskEx.Delay(0);
}
/// <summary>
/// Returns the stored value for the given key or <c>null</c> if the matching file (<see cref="GenerateStoredKey"/>
/// in <see cref="FolderPath"/> doesn't exist.
/// </summary>
/// <typeparam name="T">The type to retrieve.</typeparam>
/// <param name="key">The key to retrieve from the data store.</param>
/// <returns>The stored object.</returns>
public Task<T> GetAsync<T>(string key) {
if (string.IsNullOrEmpty(key)) {
throw new ArgumentException("Key MUST have a value");
}
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
var filePath = Path.Combine(folderPath, GenerateStoredKey(key, typeof(T)));
if (File.Exists(filePath)) {
try {
var obj = File.ReadAllText(filePath);
tcs.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(obj));
}
catch (Exception ex) {
tcs.SetException(ex);
}
}
else {
tcs.SetResult(default(T));
}
return tcs.Task;
}
/// <summary>
/// Clears all values in the data store. This method deletes all files in <see cref="FolderPath"/>.
/// </summary>
public Task ClearAsync() {
if (Directory.Exists(folderPath)) {
Directory.Delete(folderPath, true);
Directory.CreateDirectory(folderPath);
}
return TaskEx.Delay(0);
}
/// <summary>Creates a unique stored key based on the key and the class type.</summary>
/// <param name="key">The object key.</param>
/// <param name="t">The type to store or retrieve.</param>
public static string GenerateStoredKey(string key, Type t) {
return string.Format("{0}-{1}", t.FullName, key);
}
}
}
I had to set the approval prompt to force in order to get the offline access token.
var req = HttpContext.Current.Request;
var oAuthUrl = Flow.CreateAuthorizationCodeRequest(new UriBuilder(req.Url.Scheme, req.Url.Host, req.Url.Port, GoogleCalendarUtil.CallbackUrl).Uri.ToString()) as GoogleAuthorizationCodeRequestUrl;
oAuthUrl.Scope = string.Join(" ", new[] { CalendarService.Scope.CalendarReadonly });
oAuthUrl.ApprovalPrompt = "force";
oAuthUrl.State = AuthState;
You basicly need to create your own implimitation of Idatastore and then use that.
IDataStore StoredRefreshToken = new myDataStore();
// Oauth2 Autentication.
using (var stream = new System.IO.FileStream("client_secret.json", System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { AnalyticsService.Scope.AnalyticsReadonly },
"user", CancellationToken.None, StoredRefreshToken).Result;
}
Check here for a basic example of an implimitation of Idatastore. Google Oauth loading stored refresh token
Update:
Several versions of this can be found in my Authentication sample project on GitHub Google-Dotnet-Samples / Authentication / Diamto.Google.Authentication
Update 2
Updated database datastore exmaples
There are implementations for Windows 8 applications and Windows Phone available here:
WP Data Store
WinRT Data Store
Take a look in the following thread Deploying ASP.NET to Windows Azure cloud, application gives error when running on cloud before you are going to implement your own DataStore.
In the future we might also have a EF DataStore. Remember that it's an open source project so you may implement it and send it to review :) Take a look in our contribution page (https://code.google.com/p/google-api-dotnet-client/wiki/BecomingAContributor)
Our Web App is hosted on Azure so I needed to create an IDataStore for that.
I used Azure Table Storage as our data store.
Here's a gist of my attempt
Feedback and suggestions are welcome

Categories