I have a Xamarin.iOS application that requires users to log-in in order to view content. I have two text fields, one for username and one for password. Once a user has logged in and the API has returned success. how can I save the users credentials so when they launch the app they get signed in automatically?
I tried this, however, I don't know how to retrieve the values or re-save credentials if user logs out
void StoreKeysInKeychain(string key, string value)
{
var s = new SecRecord(SecKind.GenericPassword)
{
ValueData = NSData.FromString(value),
Generic = NSData.FromString(key)
};
var err = SecKeyChain.Add(s);
}
Thanks.
You can install this plugin and all of the work is already done for you: https://github.com/sameerkapps/SecureStorage, nuget: https://www.nuget.org/packages/sameerIOTApps.Plugin.SecureStorage/.
If you use the plugin it is as simple as:
CrossSecureStorage.Current.SetValue("SessionToken", "1234567890");
var sessionToken = CrossSecureStorage.Current.GetValue ("SessionToken");
If you don't want to use it, then look into github repo and see how they did it for iOS:
https://github.com/sameerkapps/SecureStorage/blob/master/SecureStorage/Plugin.SecureStorage.iOSUnified/SecureStorageImplementation.cs
public override string GetValue(string key, string defaultValue)
{
SecStatusCode ssc;
var found = GetRecord(key, out ssc);
if (ssc == SecStatusCode.Success)
{
return found.ValueData.ToString();
}
return defaultValue;
}
private SecRecord GetRecord(string key, out SecStatusCode ssc)
{
var sr = new SecRecord(SecKind.GenericPassword);
sr.Account = key;
return SecKeyChain.QueryAsRecord(sr, out ssc);
}
Better to use iOS default NSUserDefaults.StandardUserDefaults to store your credentials.
Check for stored value in Login ViewController, if it doesn't exist then after successful login set the user name and password to "GetStoredCredentials" else fetch the saved credentials and use.
public String GetStoredCredentials
{
get {
string value = NSUserDefaults.StandardUserDefaults.StringForKey("Key");
if (value == null)
return "";
else
return value;
}
set {
NSUserDefaults.StandardUserDefaults.SetString(value.ToString (), "Key");
NSUserDefaults.StandardUserDefaults.Synchronize ();
}
}
Either you can save as string array or comma seperated value.
Let me know for any further assistance.
For your refrence : https://developer.xamarin.com/guides/ios/application_fundamentals/user-defaults/
Related
I'm trying to create a user in Active Directory, using a function I adapted from some sample code I have found.
public bool crearUsuario(string usu, string pass, string path)
{
string path = #"LDAP://" + path;
//string oGUID = string.Empty;
try {
DirectoryEntry entrada = new DirectoryEntry(path);
DirectoryEntry nuevoUsuario = entrada.Children.Add("CN=" + usu, "users");
nuevoUsuario.Properties["samAccountName"].Value = usu;
nuevoUsuario.CommitChanges();
//oGUID = nuevoUsuario.Guid.ToString();
nuevoUsuario.Invoke("SetPassword", new object[] { pass });
nuevoUsuario.CommitChanges();
entrada.Close();
nuevoUsuario.Close();
return true;
}
catch (System.DirectoryServices.DirectoryServicesCOMException E)
{
//E.Message.ToString();
return false;
}
//return oGUID;
}
The original code returned a string (oGUID), but I only need a boolean.
My question is, why do they use that string? I only need true or false values, so I don't know if I need to return a string instead of a boolean value.
String seems an odd choice to me, also: I would have returned the raw Guid type. But the reason to return a string, guid, or other key, is there's an expectation you'll want to do something with that user account after you create it. Returning the new key as part of the original creation function is very likely to save you some work later.
If you know you won't be in that situation, it's perfectly fine to just return true/false... keeping in mind the comment on the question about letting the exception bubble up also has merit.
I am making a windows application that sync the source data to Active Directory.
This application works like this.
Choose Source Data(Department, User)
Mapping user or department attributes from source data
When Application service is run, it create groups and users in Active Directory
And also it sets the attributes to users and groups.
When I try to set group or user attributes(properties), it throws exception message like this.
in DirectoryEntry.CommitChanges(); block
The directory
service cannot perform the requested operation on the RDN attribute of an object.
I tried to solve it, but it's really hard to me because I`m not good at Active directory...
Code is below, Please share your knowledge.
//ppk: department key column, pk:user key column, row : Source DataTable's row
void CreateADUser(string ppk,string pk,DataRow row)
{
//password
string pass = GetPass(pk,row,LogSections.AD);
//OU
DirectoryEntry addept = adm.FindOU(ppk);
//principal path
string sOU = adm.GetPrincipalPath(addept);
var aduser = adm.CreateNewUser(sOU, pk, pass, pk, null, null, adm.sDomain);
SetAdUserProperties(pk, pass, row);
MoveUser(ppk,pk);
}
void SetAdUserProperties(string pk,string pass,DataRow row)
{
if (row == null) return;
//list of mapped column(AD User attributes)
List<ADMapping> MappingPatterns = GetAdMappings(Words.User,false);
//Columns name of Source Data table's row
var colnames = Tool.GetColNames(row);
//get user proterties
var aduser = adm.GetUser(pk);
//directory entry of users
var de=aduser.GetUnderlyingObject() as DirectoryEntry;
//looping mapped column of user attributes
foreach (var ADMap in MappingPatterns)
{
string val = ADMap.Mapping;
//mapped columns value
val=Util.ReplaceColPattern(val, row);
SetProperty(de, ADMap.CN, val);
}
if (!string.IsNullOrWhiteSpace(pass))
{
var UserPkColumn = AppConfigHelper.GetAppString(Words.SourceUserPKColumn);
UserPkColumn = Util.GetActualColName(UserPkColumn);
aduser.SetPassword(pass);
QueryHelper.Update(QueryHelper.ConnectionString, Words.ShadowUserTable
,new SqlParameter[] { new SqlParameter("#passwd", pass) }
, new SqlParameter("#"+UserPkColumn,pk));
}
aduser.Save();
}
public void SetProperty(DirectoryEntry oDE, string sPropertyName, object sPropertyValue)
{
if (sPropertyValue != null && !string.IsNullOrWhiteSpace(sPropertyValue.ToString()))
{
if (oDE.Properties.Contains(sPropertyName))
{
oDE.Properties[sPropertyName].Value = sPropertyValue;
}
else
{
oDE.Properties[sPropertyName].Add(sPropertyValue);
}
try
{
oDE.CommitChanges(); //exception here.
oDE.Close();
}
catch (Exception)
{
}
}
}
I also asked this question to other forums, and finally got it.
Before DirectoryEntry.CommitChanges(); set UserPropertyCache property to true
and call the RefreshCache method.
It's hard to see what's the cause of the issue here as we're not seeing what attributes you are trying to set.
That said, you can't just add an attribute if it doesn't exist on your AD object so this part of your code definitely has an issue :
if (oDE.Properties.Contains(sPropertyName))
{
oDE.Properties[sPropertyName].Value = sPropertyValue;
}
else
{
//The following line will never work in this context
oDE.Properties[sPropertyName].Add(sPropertyValue);
}
If I had to make an educated guess, I'd say you're either trying to set an attribute that can't be set, or the User you're adding doesn't have all it's mandatory attributes set.
I have model like below:
public class User
{
private string password;
public string Password
{
get { return Decrypt(password); }
set { password = Encrypt(value); }
}
}
I want from user code to use just insert and select password easily and see in clear text and want the logic layer to handle the encryption and decryption.
But when selecting, EF will set the value again so the encrypted password gotten from database will be encrypted again for me to get from the client code which is a problem. I also cannot distinguish between an Insert and Select to set conditionals.
I can very well do this:
//Insert
user.Password = Encrypt("123"); //Encrypt or Hash does not matter
context.Users.Add(user);
//Select
var hashedpassword = context.Users.Find(1).Select(u => u.Password).Single();
var unhashed = Decrypt(hashedpassword);
Instead I would like it not to be apparent from client code:
//Insert
user.Password = "123"; //Inserts encrypted password in database
context.Users.Add(user);
//Select
var cleartextpassword = context.Users.Find(1).Select(u => u.Password).Single();
Hope I was able to explain the problem and someone can point me to the right direction. If it is even possible or not.
The better solution is indeed to use a Hash.
But a general pattern for injecting some logic:
public class User
{
// the mapped-to-column property
protected virtual string PasswordStored
{
get ;
set ;
}
[NotMapped]
public string Password
{
get { return Decrypt(PasswordStored); }
set { PasswordStored = Encrypt(value); }
}
}
Instead of [NotMapped] you can also use the fluent API to keep EF from storing it directly.
Read and write your entities only from a repository, i.e., through a data access class that you write:
var myEntities = userRepository.GetUserById(id);
then your GetUserById() method can perform the decryption in-place. Then when you do
userRepository.UpdateUser(myUser);
you can encrypt the field again in the UpdateUser() method.
Please find following method which will get you a password hash:
public static string GetPasswordHash(this string password)
{
using (var sha1 = new SHA1Managed())
{
var hash = Encoding.UTF8.GetBytes(password);
var generatedHash = sha1.ComputeHash(hash);
var generatedHashString = Convert.ToBase64String(generatedHash);
return generatedHashString;
}
}
Currently, I'm sending some data to Parse.com. All works well, however, I would like to add a row if it's a new user or update the current table if it's an old user.
So what I need to do is check if the current Facebook ID (the key I'm using) shows up anywhere in the fbid column, then update it if case may be.
How can I check if the key exists in the column?
Also, I'm using C#/Unity.
static void sendToParse()
{
ParseObject currentUser = new ParseObject("Game");
currentUser["name"] = fbname;
currentUser["email"] = fbemail;
currentUser["fbid"] = FB.UserId;
Task saveTask = currentUser.SaveAsync();
Debug.LogError("Sent to Parse");
}
Okay, I figured it out.
First, I check which if there is any Facebook ID in the table that matches the current ID, then get the number of matches.
public static void getObjectID()
{
var query = ParseObject.GetQuery("IdealStunts")
.WhereEqualTo("fbid", FB.UserId);
query.FirstAsync().ContinueWith(t =>
{
ParseObject obj = t.Result;
objectID = obj.ObjectId;
Debug.LogError(objectID);
});
}
If there is any key matching the current Facebook ID, don't do anything. If there aren't, just add a new user.
public static void sendToParse()
{
if (count != 0)
{
Debug.LogError("Already exists");
}
else
{
ParseObject currentUser = new ParseObject("IdealStunts");
currentUser["name"] = fbname;
currentUser["email"] = fbemail;
currentUser["fbid"] = FB.UserId;
Task saveTask = currentUser.SaveAsync();
Debug.LogError("New User");
}
}
You will have to do a StartCoroutine for sendToParse, so getObjectID has time to look through the table.
It may be a crappy implementation, but it works.
What you need to do is create a query for the fbid. If the query returns an object, you update it. If not, you create a new.
I'm not proficient with C#, but here is an example in Objective-C:
PFQuery *query = [PFQuery queryWithClassName:#"Yourclass]; // Name of your class in Parse
query.cachePolicy = kPFCachePolicyNetworkOnly;
[query whereKey:#"fbid" equalTo:theFBid]; // Variable containing the fb id
NSArray *users = [query findObjects];
self.currentFacebookUser = [users lastObject]; // Array should contain only 1 object
if (self.currentFacebookUser) { // Might have to test for NULL, but probably not
// Update the object and save it
} else {
// Create a new object
}
Is it possible to read the publisher name of the currently running ClickOnce application (the one you set at Project Properties -> Publish -> Options -> Publisher name in Visual Studio)?
The reason why I need it is to run another instance of the currently running application as described in this article and pass parameters to it.
Of course I do know my application's publisher name, but if I hard code it and later on I decide to change my publisher's name I will most likely forget to update this piece of code.
Here is another option. Note that it will only get the publisher name for the currently running application, which is all I need.
I'm not sure if this is the safest way to parse the XML.
public static string GetPublisher()
{
XDocument xDocument;
using (MemoryStream memoryStream = new MemoryStream(AppDomain.CurrentDomain.ActivationContext.DeploymentManifestBytes))
using (XmlTextReader xmlTextReader = new XmlTextReader(memoryStream))
{
xDocument = XDocument.Load(xmlTextReader);
}
var description = xDocument.Root.Elements().Where(e => e.Name.LocalName == "description").First();
var publisher = description.Attributes().Where(a => a.Name.LocalName == "publisher").First();
return publisher.Value;
}
You would think this would be trivial, but I don't see anything in the framework that gives you this info.
If you want a hack, you can get the publisher from the registry.
Disclaimer - Code is ugly and untested...
...
var publisher = GetPublisher("My App Name");
...
public static string GetPublisher(string application)
{
using (var key = Registry.CurrentUser.OpenSubKey(#"Software\Microsoft\Windows\CurrentVersion\Uninstall"))
{
var appKey = key.GetSubKeyNames().FirstOrDefault(x => GetValue(key, x, "DisplayName") == application);
if (appKey == null) { return null; }
return GetValue(key, appKey, "Publisher");
}
}
private static string GetValue(RegistryKey key, string app, string value)
{
using (var subKey = key.OpenSubKey(app))
{
if (!subKey.GetValueNames().Contains(value)) { return null; }
return subKey.GetValue(value).ToString();
}
}
If you find a better solution, please follow-up.
I dont know about ClickOnce, but normally, you can read the assembly-info using the System.Reflection framework:
public string AssemblyCompany
{
get
{
object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
if (attributes.Length == 0)
{
return "";
}
return ((AssemblyCompanyAttribute)attributes[0]).Company;
}
}
Unfortunately, theres no "publisher" custom-attribute, just throwing this out as a possible work-around