1
0
mirror of https://github.com/bitwarden/mobile synced 2026-01-01 16:13:15 +00:00

access group for keychain. load sites for given hostname in extension

This commit is contained in:
Kyle Spearrin
2016-06-25 01:58:42 -04:00
parent 9d8f54af9d
commit 1307b6a1b2
12 changed files with 104 additions and 22 deletions

View File

@@ -0,0 +1,92 @@
using System;
using System.Runtime.CompilerServices;
using Bit.App.Abstractions;
using Foundation;
using Security;
namespace Bit.iOS.Core.Services
{
public class KeyChainStorageService : ISecureStorageService
{
public void Store(string key, byte[] dataBytes)
{
using(var data = NSData.FromArray(dataBytes))
using(var newRecord = GetKeyRecord(key, data))
{
Delete(key);
CheckError(SecKeyChain.Add(newRecord));
}
}
public byte[] Retrieve(string key)
{
SecStatusCode resultCode;
using(var existingRecord = GetKeyRecord(key))
using(var record = SecKeyChain.QueryAsRecord(existingRecord, out resultCode))
{
if(resultCode == SecStatusCode.ItemNotFound)
{
return null;
}
CheckError(resultCode);
return record.Generic.ToArray();
}
}
public void Delete(string key)
{
using(var record = GetExistingRecord(key))
{
if(record != null)
{
CheckError(SecKeyChain.Remove(record));
}
}
}
public bool Contains(string key)
{
using(var existingRecord = GetExistingRecord(key))
{
return existingRecord != null;
}
}
private static void CheckError(SecStatusCode resultCode, [CallerMemberName] string caller = null)
{
if(resultCode != SecStatusCode.Success)
{
throw new Exception(string.Format("Failed to execute {0}. Result code: {1}", caller, resultCode));
}
}
private static SecRecord GetKeyRecord(string key, NSData data = null)
{
var record = new SecRecord(SecKind.GenericPassword)
{
Service = NSBundle.MainBundle.BundleIdentifier,
Account = key,
AccessGroup = "TEAMID.bitwarden"
};
if(data != null)
{
record.Generic = data;
}
return record;
}
private static SecRecord GetExistingRecord(string key)
{
var existingRecord = GetKeyRecord(key);
SecStatusCode resultCode;
SecKeyChain.QueryAsRecord(existingRecord, out resultCode);
return resultCode == SecStatusCode.Success ? existingRecord : null;
}
}
}

View File

@@ -0,0 +1,235 @@

using System;
#if __UNIFIED__
using Foundation;
#else
using MonoTouch.Foundation;
#endif
using Plugin.Settings.Abstractions;
namespace Bit.iOS.Core.Services
{
/// <summary>
/// Main implementation for ISettings
/// </summary>
public class Settings : ISettings
{
private readonly object locker = new object();
private readonly NSUserDefaults _defaults;
public Settings(string defaultsName)
{
_defaults = string.IsNullOrWhiteSpace(defaultsName) ? NSUserDefaults.StandardUserDefaults
: new NSUserDefaults(defaultsName, NSUserDefaultsType.SuiteName);
}
/// <summary>
/// Gets the current value or the default that you specify.
/// </summary>
/// <typeparam name="T">Vaue of t (bool, int, float, long, string)</typeparam>
/// <param name="key">Key for settings</param>
/// <param name="defaultValue">default value if not set</param>
/// <returns>Value or default</returns>
public T GetValueOrDefault<T>(string key, T defaultValue = default(T))
{
lock(locker)
{
var defaults = _defaults;
if(defaults.ValueForKey(new NSString(key)) == null)
return defaultValue;
Type typeOf = typeof(T);
if(typeOf.IsGenericType && typeOf.GetGenericTypeDefinition() == typeof(Nullable<>))
{
typeOf = Nullable.GetUnderlyingType(typeOf);
}
object value = null;
var typeCode = Type.GetTypeCode(typeOf);
switch(typeCode)
{
case TypeCode.Decimal:
var savedDecimal = defaults.StringForKey(key);
value = Convert.ToDecimal(savedDecimal, System.Globalization.CultureInfo.InvariantCulture);
break;
case TypeCode.Boolean:
value = defaults.BoolForKey(key);
break;
case TypeCode.Int64:
var savedInt64 = defaults.StringForKey(key);
value = Convert.ToInt64(savedInt64, System.Globalization.CultureInfo.InvariantCulture);
break;
case TypeCode.Double:
value = defaults.DoubleForKey(key);
break;
case TypeCode.String:
value = defaults.StringForKey(key);
break;
case TypeCode.Int32:
#if __UNIFIED__
value = (Int32)defaults.IntForKey(key);
#else
value = defaults.IntForKey(key);
#endif
break;
case TypeCode.Single:
#if __UNIFIED__
value = (float)defaults.FloatForKey(key);
#else
value = defaults.FloatForKey(key);
#endif
break;
case TypeCode.DateTime:
var savedTime = defaults.StringForKey(key);
if(string.IsNullOrWhiteSpace(savedTime))
{
value = defaultValue;
}
else
{
var ticks = Convert.ToInt64(savedTime, System.Globalization.CultureInfo.InvariantCulture);
if(ticks >= 0)
{
//Old value, stored before update to UTC values
value = new DateTime(ticks);
}
else
{
//New value, UTC
value = new DateTime(-ticks, DateTimeKind.Utc);
}
}
break;
default:
if(defaultValue is Guid)
{
var outGuid = Guid.Empty;
var savedGuid = defaults.StringForKey(key);
if(string.IsNullOrWhiteSpace(savedGuid))
{
value = outGuid;
}
else
{
Guid.TryParse(savedGuid, out outGuid);
value = outGuid;
}
}
else
{
throw new ArgumentException(string.Format("Value of type {0} is not supported.", value.GetType().Name));
}
break;
}
return null != value ? (T)value : defaultValue;
}
}
/// <summary>
/// Adds or updates a value
/// </summary>
/// <param name="key">key to update</param>
/// <param name="value">value to set</param>
/// <returns>True if added or update and you need to save</returns>
public bool AddOrUpdateValue<T>(string key, T value)
{
Type typeOf = typeof(T);
if(typeOf.IsGenericType && typeOf.GetGenericTypeDefinition() == typeof(Nullable<>))
{
typeOf = Nullable.GetUnderlyingType(typeOf);
}
var typeCode = Type.GetTypeCode(typeOf);
return AddOrUpdateValue(key, value, typeCode);
}
private bool AddOrUpdateValue(string key, object value, TypeCode typeCode)
{
lock(locker)
{
var defaults = _defaults;
switch(typeCode)
{
case TypeCode.Decimal:
defaults.SetString(Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture), key);
break;
case TypeCode.Boolean:
defaults.SetBool(Convert.ToBoolean(value), key);
break;
case TypeCode.Int64:
defaults.SetString(Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture), key);
break;
case TypeCode.Double:
defaults.SetDouble(Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture), key);
break;
case TypeCode.String:
defaults.SetString(Convert.ToString(value), key);
break;
case TypeCode.Int32:
defaults.SetInt(Convert.ToInt32(value, System.Globalization.CultureInfo.InvariantCulture), key);
break;
case TypeCode.Single:
defaults.SetFloat(Convert.ToSingle(value, System.Globalization.CultureInfo.InvariantCulture), key);
break;
case TypeCode.DateTime:
defaults.SetString(Convert.ToString(-(Convert.ToDateTime(value)).ToUniversalTime().Ticks), key);
break;
default:
if(value is Guid)
{
if(value == null)
value = Guid.Empty;
defaults.SetString(((Guid)value).ToString(), key);
}
else
{
throw new ArgumentException(string.Format("Value of type {0} is not supported.", value.GetType().Name));
}
break;
}
try
{
defaults.Synchronize();
}
catch(Exception ex)
{
Console.WriteLine("Unable to save: " + key, " Message: " + ex.Message);
}
}
return true;
}
/// <summary>
/// Removes a desired key from the settings
/// </summary>
/// <param name="key">Key for setting</param>
public void Remove(string key)
{
lock(locker)
{
var defaults = _defaults;
try
{
var nsString = new NSString(key);
if(defaults.ValueForKey(nsString) != null)
{
defaults.RemoveObject(key);
defaults.Synchronize();
}
}
catch(Exception ex)
{
Console.WriteLine("Unable to remove: " + key, " Message: " + ex.Message);
}
}
}
}
}

View File

@@ -33,6 +33,14 @@
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
<Reference Include="Plugin.Settings, Version=2.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugins.Settings.2.1.0\lib\Xamarin.iOS10\Plugin.Settings.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Plugin.Settings.Abstractions, Version=2.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugins.Settings.2.1.0\lib\Xamarin.iOS10\Plugin.Settings.Abstractions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLite-net, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\sqlite-net-pcl.1.1.1\lib\portable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10\SQLite-net.dll</HintPath>
<Private>True</Private>
@@ -48,10 +56,14 @@
<ItemGroup>
<Compile Include="Constants.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\KeyChainStorageService.cs" />
<Compile Include="Services\Settings.cs" />
<Compile Include="Services\SqlService.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\App\App.csproj">

View File

@@ -2,4 +2,5 @@
<packages>
<package id="sqlite-net-pcl" version="1.1.1" targetFramework="xamarinios10" />
<package id="SQLitePCL.raw" version="0.8.6" targetFramework="xamarinios10" />
<package id="Xam.Plugins.Settings" version="2.1.0" targetFramework="xamarinios10" />
</packages>