Sauvegarde

JijoJijo Membre
09:11 modifié dans API AppKit #1
J'ai essayé plusieurs tutoriels et je n'arrive toujours pas a sauvegarder mot de passe et login dans un plist à  la fermeture de mon appli et à  charger la plist au lancement.

Je ne sais pas non plus s'il faut passer par User Defaults Controller dans Interace Builder.

Merci

Réponses

  • AliGatorAliGator Membre, Modérateur
    09:11 modifié #2
    NSUserDefaults* prefs = [NSUserDefaults standardUserDefaults];<br /><br />// stockage<br />[prefs setObject:@&quot;monlogin&quot; forKey:@&quot;login&quot;];<br />[prefs setObject:@&quot;monmotdepasse&quot; forKey:@&quot;pwd&quot;];<br /><br />....<br />// récupération<br />NSString* login = [prefs stringForKey:@&quot;login&quot;];<br />NSString* pass = [prefs stringForKey:@&quot;pwd&quot;];
    

  • NoNo Membre
    09:11 modifié #3
    Je ne sais pas si c'est très prudent de stocker un mot de passe en clair dans un plist...
    Surtout quand dans OSX, il y a le KeyChain qui permet de stocker des infos sous forme protégée.

    Dans ce post il y a des pistes pour savoir utiliser le KeyChain (donc comment écrire puis lire un mot de passe) depuis une appli cocoa.
  • JijoJijo Membre
    09:11 modifié #4
    Je pense que ta solution est en effet la mellieur. Cepandant les exmple et fichier source du post ne fonctionne pas a cause d'erreur.

    Sinon j'ai trouvé ceci:

    http://developer.apple.com/documentation/Security/Conceptual/keychainServConcepts/02concepts/chapter_2_section_4.html


    La section AddingSimpleKeychainServicestoYourApplication du pdf est ce que je veux faire.

    Par contre je n'arrive pas a implémenter une classe pour les methodes utiliser car je voudrais executer l'ajout SecKeychainAddGenericPassword dans la methode action si la connexion est établie.


    J'ai vu que le plugin Smugmug d'iPhoto enregistrait dans le trousseau de clef dans utilitaire donc cela est faisable.
  • JijoJijo Membre
    09:11 modifié #5
    Je voulais dire cela est faisable pour les plugins d'iPhoto, donc pour mon projet de plugin iPhoto.

  • NoNo Membre
    décembre 2008 modifié #6
    dans 1230643828:

    Je pense que ta solution est en effet la mellieur. Cepandant les exmple et fichier source du post ne fonctionne pas a cause d'erreur.

    Bizarre car chez moi ça compile bien.
    Y'a des warnings dus à  l'utilisation des méthodes depercated (surtout lié aux méthodes cstring de NSStrings), mais cela n'empêche pas la compil et reste fonctionnel.

    Par contre j'ose imaginer que t'as oublié d'importer le framework Security à  ton projet.
    Donc le linkage lui plante.

    Ajoute ce framework (clic-droit sur "Frameworks" dans "groups & files" > Add... > Existing Frameworks > navigation jusqu'à  /System/libray/Frameworks puis sélection du Security.framework), puis recompile.

    De toute façon, les 2 fichiers .h et .m ne sont là  qu'à  titre d'exemple.
    A toi de picorer ce qui te sera utile dedans (et donc de modifier les méthodes deprecated par les nouvelles méthodes à  utiliser).
  • JijoJijo Membre
    décembre 2008 modifié #7
    Non, tu ne me feras pas dire que je suis un boulet car j'avais omis d'ajouter le framework Security.

    :)beta:

    Du coup j'ai pu réaliser la 1ère partie c'est a dire intégrer mon login et mot de passe dans le trousseau de cléf session principale sans créer un fichier keychain.

    SecKeychainAddGenericPassword (
    NULL, // default keychain
    6, // length of service name
            "plugin", // service name
    [[login stringValue] length], // length of account name
    [[login stringValue] cString], // account name
    [[password stringValue] length], // length of password
    [[password stringValue] cString], // pointer to password data
    NULL // the item reference
    );




    Cependant je ne sais pas comment charger le mot de passe et le login.


    - (NSString *)passwordForServiceName:(NSString *)servicename account:(NSString *)account
    {
    OSStatus err;
    UInt32 passl;
    void *pass;

    err=SecKeychainFindGenericPassword(_kcref, [servicename length], [servicename cString],
    [account length], [account cString],
    &passl, &pass, NULL);
    return [[[NSString alloc] initWithCString:pass length:passl] autorelease];
    }




    Cette fonction est celle qui conviendrait pourtant il faut passer en paramètre l'account et comme on le voit sur la photo l'account est mon login.

    Comment charger un login et un mot de passe s'il faut connaà®tre le login pour connaà®tre le mot de passe.


    Si je place cette fonction dans awakeFromNib je me trouve dans l'incapabilité de connaà®tre le login.
    Sinon j'ai essayé avec l'itemRef mais je ne suis pas arrivé a charger.



  • NoNo Membre
    09:11 modifié #8
    Tu peux stocker le "account name" dans les préférences de l'appli, ce n'est pas une faille.
    Une fois récupéré, tu peux alors aller dans le keychain pour accéder au mot de passe.
    Si le "account name" n'existe pas (donc première utilisation du plugin), alors tu le demandes à  l'utilisateur.
  • JijoJijo Membre
    09:11 modifié #9
    Oui je vois maintenant ce qu'il faut faire.
    Pour l'instant avec NSUserDefaults* prefs = [NSUserDefaults standardUserDefaults] j'ai pu stocker l'account et charger le mot de passe du trousseau de clef.


    Cependant le fichier plist correspondant est com.apple.iPhoto. Du coup je dois créer un fichier plist independant que pour mon plugin.

    J'ai trouvé ce-ci
    http://www.projectomega.org/contents/fr/php/oreilly/cocoa/MacOSX_Cocoa_11.pdf

    NSString *recordsFile;

    - (void)awakeFromNib 
    {
    recordsFile = @$(HOME)/Library/Preferences/iPhotoPlugin.plist;
    recordsFile = [recordsFile stringByExpandingTildeInPath];
    [recordsFile retain];

        NSUserDefaults * prefs = [[NSMutableArray alloc] initWithContentsOfFile:recordsFile];

    if ( nil == prefs )
    {
    prefs = [[NSMutableArray alloc] init];
    }
    else
    {
    id kc = [[KEYCHAINAccess alloc] init];

    // récupération
    NSString * account = [prefs stringForKey:@account];

    NSString * passe =[kc passwordForServiceName:@iPhotoPlugin account:account];

    // ajout du login et mot de passe au charrgement
    [o_login setStringValue:account];
    [o_password setStringValue:passe];
    }
    }

    J'ai mis NSUserDefaults comme type mais je ne sais pas si cela est correct.
    Deplus le fichier plist ne se crée pas.


    En gros je veux juste créer un fichier plist qui contient l'account.Il y a peut-être quelque chose de plus simple.


  • mpergandmpergand Membre
    janvier 2009 modifié #10
    Salut à  tous et bonne année !

    Pour créer un plist dans le dossier preferences, on peut utiliser setPersistentDomain: forName:
    <br />NSDictionary* plistDic=[NSDIctionary dictionaryWithObject:accountName&nbsp; forKey:@&quot;AccountName&quot;];<br /><br />[[NSUserDefaults standardUserDefaults] setPersistentDomain:plistDic forName:@&quot;MyPlist&quot;];<br />
    


    et pour le récupérer:
    <br />NSDictionary* plistDic=[[NSUserDefaults standardUserDefaults] persistentDomainForName:@&quot;MyPlist&quot;];<br />
    


  • janvier 2009 modifié #11
    dans 1230635809:

    Je ne sais pas si c'est très prudent de stocker un mot de passe en clair dans un plist...
    Surtout quand dans OSX, il y a le KeyChain qui permet de stocker des infos sous forme protégée.

    Dans ce post il y a des pistes pour savoir utiliser le KeyChain (donc comment écrire puis lire un mot de passe) depuis une appli cocoa.


    CA c'est utile ! 

    En revanche je trouve ça assez chiant qu'il demande le password à  chaque fois (quasiment) qu'on lance son application.
    "Machin wants to access the machin.keychain"
  • JijoJijo Membre
    09:11 modifié #12
    - (void)awakeFromNib 
    {
    // création du dictionnaire
    NSMutableDictionary * dict=[[NSMutableDictionary alloc] init];

    // remplissage de ce dictionnaire
    [dict setObject:@MyAccount forKey:@account];


    // création du dictionnaire dans le dossier préférence ($(HOME/Library/Preferences/MyPlist.plist")
    [[NSUserDefaults standardUserDefaults] setPersistentDomain:dict forName:@MyPlist];

    // extraction du dictionnaire ne marche pas
            NSDictionary * Extractionplist=NSUserDefaults standardUserDefaults] persitentDomainForName:@&quot;MyPlist&quot;];<br />&nbsp; &nbsp; &nbsp; &nbsp; NSString * login =[[NSString alloc]initWithString:[Extractionplist objectForKey:@&quot;account&quot;;




    id kc = [[KEYCHAINAccess alloc] init];

    // recherche du mot de passe dans le Keychain suivant le service et le compte de l'utilisateur
            NSString * passe =[kc passwordForServiceName:@plugin account:login];

    // ajout du login et mot de passe dans les NSTextField au chargement
    [login setStringValue: login];
    [password setStringValue:passe];
    }


    Pour te répondre Eaglelouk je stock bien mon login et mon mot de passe dans le keychain, cependant pour utiliser la fonction de recherche du mot de passe passwordForServiceName je dois lui passer en paramètre le login.

    Donc je me vois obligé de stocker le login dans une plist qui deplus doit être différente de celle par défaut faite par NSUserDefaults* prefs = [NSUserDefaults standardUserDefaults] car je fais un plugin et cela ne serai pas propre de le stocker dans com.apple.iPhoto.

    Ainsi le login est en clair mais le mot de passe est protégé.


    La création de la plist MyPlist marche, mais je n'arrive pas encore à  extraire ma variable account de celle -ci.
  • mpergandmpergand Membre
    09:11 modifié #13
    La création de la plist MyPlist marche, mais je n'arrive pas encore à  extraire ma variable account de celle -ci.

    NSDictionary* plistDic=[[NSUserDefaults standardUserDefaults] persistentDomainForName:@&quot;MyPlist&quot;];<br />NSLog(@&quot;%@ &#092;n AccountName=%@&quot;,plistDic,[plistDic objectForKey:@&quot;AccountName&quot;]);
    


  • JijoJijo Membre
    09:11 modifié #14
    Non vraiment ça ne marche pas pour moi.

    Sinon sans la création du fichier plist que je peux créer et installer avec le package d'installation,je voudrais essayer de charger le NSDictionary de cette plist.

    Avec le tuto de project omega que j'ai trouvé ici --> http://www.projectomega.org/contents/fr/php/oreilly/cocoa/MacOSX_Cocoa_11.pdf j'ai essayé de charger les données. Cela s'éxécute mais records est vide après chargement.

            NSString * recordsFile;
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];


    // je  ne sais pas quoi mettre pour récupérer e non de l'utilisateur
    recordsFile = @$(HOME)/Library/Preferences/MyPlist.plist;
    recordsFile = [recordsFile stringByExpandingTildeInPath];
    [recordsFile retain];
    NSMutableArray * records = [[NSMutableArray alloc] initWithContentsOfFile:recordsFile];


    [pool release];


    Ce que je veux faire est simple, je veux charger les variables d'une plist donnée et pouvoir à  la fermeture de l'application modifier oui ou non les variable de la plist.


    Merci


    Je sais pas si cela est faisable.
  • mpergandmpergand Membre
    janvier 2009 modifié #15
    C'est pas claire ton histoire, tu parles d'un NSDictionary et tu crées un NSMutableArray  ???


    Sinon, ce code marche chez moi:
    NSString *prefPath=[NSString stringWithFormat:@&quot;%@/%@/%@&quot;,NSHomeDirectory(),@&quot;Library/Preferences&quot;,@&quot;MyPlist.plist&quot;];<br />NSDictionary *dic=[NSDictionary dictionaryWithContentsOfFile:prefPath];<br />NSLog(@&quot; %@ %@&quot;,prefPath,dic);
    

  • JijoJijo Membre
    09:11 modifié #16
    Le chargement  est ok. Cela marche comme je le voulais.

    /Users/nomUser/Library/Preferences/MyPlist.plist {
        MyPlist =    {
            account = monLogin;
        };
    }

    J'ai essayé d'extraire la variable account comme j'avais déjà  pu le faire auparavant mais cela ne marche pas.

    NSLog(@Variable account =%@",[dic objectForKey:@account]);

    Console :
    Variable account =(null)

    Merci
  • NoNo Membre
    janvier 2009 modifié #17
    Ton plist me semble bizarre...
    Comment l'enregistres tu ?
    Car on dirait un plist au format texte.
    Donc tu ne passes par par les user defaults.

    Il nous faudrait plus de code pour voir ce qui se passe en amont (notamment comment tu charges le plist dans la dictionnaire dic).
  • mpergandmpergand Membre
    09:11 modifié #18
    Oui, bizarroà¯de

    Dans un projet cocoa vierge, dans awakeFromNib, colle ce code:
    NSDictionary* plistDic=[NSDictionary dictionaryWithObject:@&quot;name&quot;&nbsp; forKey:@&quot;AccountName&quot;];<br />[[NSUserDefaults standardUserDefaults] setPersistentDomain:plistDic forName:@&quot;MyPlist&quot;];<br />	<br />plistDic=[[NSUserDefaults standardUserDefaults] persistentDomainForName:@&quot;MyPlist&quot;];<br />NSLog(@&quot;%@ &#092;n AccountName=%@&quot;,plistDic,[plistDic objectForKey:@&quot;AccountName&quot;]);<br /><br />// ------------------------------	<br />NSString *prefPath=[NSString stringWithFormat:@&quot;%@/%@/%@&quot;,NSHomeDirectory(),@&quot;Library/Preferences&quot;,@&quot;MyPlist.plist&quot;];<br />NSDictionary *dic=[NSDictionary dictionaryWithContentsOfFile:prefPath];<br />NSLog(@&quot; %@ %@&quot;,prefPath,dic);<br />
    


    dans la console tu dois avoir:
    2009-01-03 10:59:54.030 Essai[774:10b] {
        AccountName = name;
    }
    AccountName=name
    2009-01-03 10:59:54.033 Essai[774:10b]  /Users/NomDuCOmpteCOurant/Library/Preferences/MyPlist.plist {
        AccountName = name;
    }

  • JijoJijo Membre
    janvier 2009 modifié #19
    Voilà  mon raisonnement pour que cela soit plus clair:

    En installant mon plugin iPhoto le package installe le plugin ainsi que la plist MyPlist dans le dossier /Users/NomDuCOmpteCOurant/Library/Preferences/MyPlist.plist

    J'utilise une plist autre que celle utilisée pour user defaults car sinon je modifierai la plist com.apple.iPhoto ce qui ne serai pas propre à  mon goût même si certain plugin le font.

    Ensuite au lancement de mon plugin:

    Chargement de la plist dans awakeFromNib

    - (void)awakeFromNib
    {
    // chargement du dictionaire contenu dans le fichier MyPlist.plist
    NSString *prefPath=[NSString stringWithFormat:@%@/%@/%@",NSHomeDirectory(),@Library/Preferences,@MyPlist.plist];
    NSDictionary *dic=[NSDictionary dictionaryWithContentsOfFile:prefPath];


    // ce que je voudrais faire
    // recupération du login contenu dans dic sous forme NSString
    NSString * monLogin = [dic objectForkey:@account];


    // recherche du password pour le login chargé et pour le service  iPhotoPlugin (nom de mon plugin) dans le fichier du KEYCHAIN (trousseau de clefs)
    id kc = [[KEYCHAINAccess alloc] init];
    NSString * passe =[kc passwordForServiceName:@iPhotoPlugin account: monLogin];

    // ajout du login et mot de passe dans les NSTexfields
    [login setStringValue: login];   
    [password setStringValue:passe];   
    }






    Si la connexion se fait avec un autre compte c'est à  dire avec un autre login que celui chargé, aprés connexion on modifie la valeur de account dans MyPlist pour la remplacer par celle du nouveau login pour le sauvegarder.

    Il me manque la récupération de la variable account en NSString et la modification de celle-ci pour la sauvegarde.

    Voilà 

    Si ce n'est pas clair dites le moi.


    Merci



    Photo de la fenêtre chargée au lancement du plugin avec le login et mot de passe.
  • JijoJijo Membre
    09:11 modifié #20
    Me prenant trop la tête avec la plist alternative, j'ai fini par utiliser celle faite pour user defaults.


    Donc plus la peine de se prendre le tête sur mon problème.

    Merci quand même d'avoir essayé.
  • NoNo Membre
    09:11 modifié #21
    dans 1231154122:

    Donc plus la peine de se prendre le tête sur mon problème.
    Merci quand même d'avoir essayé.

    On se prend pas la tête.
    Mais tu donnes si peu de code que c'est impossible de t'aider.
    Je suis sûr que ta ligne qui charge MyPlist dans le dictionnaire dic est pourrie.
    Mais c'est tout ce que je peux dire sans autre info.
  • JijoJijo Membre
    09:11 modifié #22
    Le dic que je charge de la plist est correct.


    La console affiche
    MyPlist =    {
            account = monLogin;
        };
    }

    Sauf que je n'arrive pas à  extraire la variable account en type NSString.


  • NoNo Membre
    09:11 modifié #23
    dans 1231163437:

    Le dic que je charge de la plist est correct.
    La console affiche
    MyPlist =     {
            account = monLogin;
        };
    }
    Sauf que je n'arrive pas à  extraire la variable account en type NSString.

    Peux tu afficher la ligne qui charge le dictionnaire telle qu'elle est dans ton code ?
  • JijoJijo Membre
    09:11 modifié #24
    // charge la plist  qui est nomUser/Library/Preferences/MyPlist.plist"
    NSString * prefPath=[NSString stringWithFormat:@%@/%@/%@",NSHomeDirectory(),@Library/Preferences,@MyPlist.plist];

    // extraction du dictionnaire
    NSDictionary * dic=[NSDictionary dictionaryWithContentsOfFile:prefPath];

    Voilà  comment je charge mon dictionnaire
  • NoNo Membre
    09:11 modifié #25
    dans 1231167088:

    // charge la plist  qui est nomUser/Library/Preferences/MyPlist.plist"
    NSString * prefPath=[NSString stringWithFormat:@%@/%@/%@",NSHomeDirectory(),@Library/Preferences,@MyPlist.plist];
    // extraction du dictionnaire
    NSDictionary * dic=[NSDictionary dictionaryWithContentsOfFile:prefPath];
    Voilà  comment je charge mon dictionnaire


    Bien.
    Je vois un problème potentiel de problème mémoire (perte du dictionnaire dic) à  cause de la méthode de classe dictionaryWithContentsOfFile: utilisée sans retain (cette méthode renvoie un NSDictionary en autorelease : il sera donc purger de la mémoire assez vite si tu ne fais rien).

    Remplace ta ligne [tt]NSDictionary * dic=[NSDictionary dictionaryWithContentsOfFile:prefPath];[/tt] par :
    NSDictionary * dic=[NSDictionary dictionaryWithContentsOfFile:prefPath];<br />[dic retain];
    


    ou mieux, par :
    <br />NSDictionary * dic=[[NSDictionary alloc] initWithContentsOfFile:prefPath];
    
  • JijoJijo Membre
    janvier 2009 modifié #26
    NSString * prefPath=[NSString stringWithFormat:@%@/%@/%@",NSHomeDirectory(),@Library/Preferences,@MyPlist.plist];
    NSDictionary * dic=[[NSDictionary alloc] initWithContentsOfFile:prefPath];

    NSLog(@%@ ",dic);
    NSLog(@Variable account =%@",[dic objectForKey:@account]);

    Console:
    {
        MyPlist =     {
            account = monLogin;
        };
    }
    Variable account =(null)

    Dans les 2 cas que tu me proposes [dic objectForKey:@account] apparaà®t comme null.

  • mpergandmpergand Membre
    janvier 2009 modifié #27

    Console:
    {
        MyPlist =     {
            account = monLogin;
        };
    }

    Ton dico contient un autre dico !!!?

    essaye ça pour voir:
    NSLog(@&quot;account=%@&quot;,[[dic objectForKey:@&quot;MyPlist&quot;] objectForKey:@&quot;account&quot;]);
    


    Tu peux aussi vérifier la structure de ton plist avec Property List Editor.
  • AliGatorAliGator Membre, Modérateur
    janvier 2009 modifié #28
    dans 1231171599:


    Console:
    {
        MyPlist =     {
            account = monLogin;
        };
    }

    Ton dico contient un autre dico !!!?
    Oui en fait je crois que c'est le cas depuis le début (sauf que parfois en nous copiant le contenu de la console, il oubliait le premier "{" et comme en plus il ne nous donnait pas le NSLog utilisé, on pensait que "MyPlist = " était un texte statique qu'il avait mis dans son NSLog... alors que non, ce "MyPlist = " fait partie de la description de son dictionary !

    Donc son dictionary est bien, d'après le log et vu comment il l'imprime, et ce depuis le début, un dictionnaire contenant une unique clé "MyPlist"... associée... à  son dictionary contenant, lui, sa clé "account" !
    Et donc il faut bien faire comme tu dis [tt][[dic objectForKey:@MyPlist] objectForKey:@account][/tt] pour le coup.

    Reste à  comprendre pourquoi son dictionnaire est lui-même encapsulé dans un doctionnaire avec la clé "MyPlist", je pense que c'est dû au fait qu'il le crée avec les userDefaults et enregistre un persistantDomainName.

    D'ailleurs ce qui me gène c'est que tu sembles enregistrer tes données avec userDefaults (mais pas le shared, bien celui pour lequel tu as rajouté ton persistant domain)... mais la lecture tu la fais comme une lecture plus bas niveau, en donnant le chemin du fichier plist pour le lire (et sur lequel tu n'es pas sensé te baser car les userDefaults pourraient être stockés à  un autre endroit un jour). Ne faudrait-il mieux pas lire le contenu de ton plist de la manière symétrique à  celle utilisée pour écrire le plist ?!

    D'autant que je trouve que d'utiliser directement en dur le chemin du plist n'est pas très classe pour le coup, même si y'a pire et que ça passe encore, c'est pas la façon idéale d'accéder aux prefs...
  • AliGatorAliGator Membre, Modérateur
    09:11 modifié #29
    Soit en reprennant le post de mpergand :
    <br />NSDictionary* plistDic=[NSDIctionary dictionaryWithObject:accountName&nbsp; forKey:@&quot;AccountName&quot;]; // tes données à  sauvegarder<br /><br />// Sauvegarder les données :<br />[[NSUserDefaults standardUserDefaults] setPersistentDomain:plistDic forName:@&quot;MyPlist&quot;];<br />NSLog(@&quot;dico sauvegargé : %@.&quot; , plistDic);<br /><br />// Relire les données puls tard :<br />NSDictionary* plistDic=[[NSUserDefaults standardUserDefaults] persistentDomainForName:@&quot;MyPlist&quot;];<br />NSLog(@&quot;dico récupéré : %@.&quot; , plistDic);<br />NSLog(@&quot;account name : %@ !&quot;, [plistDic objectForKey:@&quot;AccountName&quot;]);<br />
    


    Perso je préfère mettre un peu de texte avant et après le "%@" dans mes NSLogs pour bien voir d'une part de quel NSLog il s'agit, mais aussi pouvoir bien isoler le contenu à  afficher.
  • JijoJijo Membre
    09:11 modifié #30
    Merci beaucoup ça marche.

    Cela évite de pourir la pist com.apple.iPhoto.plist.




    Encore merci à  vous.
Connectez-vous ou Inscrivez-vous pour répondre.