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:
92
src/iOS.Core/Services/KeyChainStorageService.cs
Normal file
92
src/iOS.Core/Services/KeyChainStorageService.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
235
src/iOS.Core/Services/Settings.cs
Normal file
235
src/iOS.Core/Services/Settings.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user