Suggestion Developing a Quicksilver Plugin for 1Password

pjrobertsonpjrobertson Member
edited January 2010 in 1Password for Mac 3 and 4
Hey guys,

I've just installed AllBookmarks, and thought it would be good to try and integrate it with QuickSilver.

Could you let me know where all bookmarks keeps the bookmarks stored?

Are they encrypted, and if so - is there a bridge or applescript support to access the urls etc. from another location?

[edit] Just looked into it and it doesn't look like the app stores anything right? Does it access stuff straight from 1P, and if so - can 3rd party apps get in?[/edit]

Cheers
«1

Comments

  • jxpx777jxpx777 Code Wrangler

    AgileBits Team Member
    edited December 1969
    AllBookmarks simply aggregates your bookmarks from your various browsers for display. It does not actually store any data of its own.
  • pjrobertsonpjrobertson Member
    edited December 1969
    What about the go and fill feature? How does it get this to work?

    That's the part I'm interested in. 1Passwords login addresses, and go and fill.

    The collection of browsers' bookmarks is just another part...
  • jxpx777jxpx777 Code Wrangler

    AgileBits Team Member
    edited December 1969
    Basically, AllBookmarks uses some of the same code that is in 1Password. It loads the logins and when you select one, it calls the same Go & Fill method that 1Password or its browser extension sends when invoking Go & Fill from there.
  • pjrobertsonpjrobertson Member
    edited December 1969
    ...which I'm guessing is closed source so there's no way for 3rd party apps to call (applescript, an application bridge, framework?) into 1pwd and get the deal?

    Cheers
  • jxpx777jxpx777 Code Wrangler

    AgileBits Team Member
    edited December 1969
    Yes, it is closed source. You might be interested in this thread: http://support.agilewebsolutions.com/showthread.php?20634-1P-LaunchBar
  • pjrobertsonpjrobertson Member
    edited December 1969
    Just what I was looking for!

    I'll look at writing a QS plugin (yes, it is still alive ;)) for 1password and basically parsing the data :)
  • jxpx777jxpx777 Code Wrangler

    AgileBits Team Member
    edited December 1969
    That will be pretty awesome. Keep in mind that sometimes the UUID parameter can cause problems, especially in cases where form parameters are checked and verified by the server.

    The other option is a little more fragile in the event that we ever changed the code (I don't think that is likely to happen any time soon, but it's not guaranteed…), but you could write out a plist file matching the format we use to trigger go and fill. Have a look at ~/Library/Application Support/1Password/Fill. (There will only be files present after you use Go & Fill, so try invoking it on a login first.) The "form" key is the UUID and the location key is pulled from the login file JSON as well. The timestamp is how 1Password knows whether to fill the page or not in case there are old ones hanging around.

    So, with the second method, you would just write out that plist file and then immediately open the URL with an NSWorkspace call and the 1Password extension should check the Fill folder for a matching, current entry and autofill if it's there.

    I hope that helps. :)
  • pjrobertsonpjrobertson Member
    edited January 2010
    Thanks for the extra info :)

    The second method sounds best, although

    1. I'm not exactly sure what's going on - the plist is created by pulling info from the agile keychain right?
    - surely an NSWorspace call on the URL would just bring up the URL - not involving 1pwd at all?
    2. I've tried the 'go & fill' and noticed that the plist is created, I get logged in, then the plist is delete?

    Thanks in advance :)

    P.S. the applescript you linked to doesn't seem to work.

    The URLs are coming up as:

    w w w.domain.com/subfoler/tdrc/index.cfm?loc=en&promoid=EBYEU&product=photoshop&onepasswdfill=SOMEHASOFNUMBERS

    Is this what it should be?

    Aaah I think I've got what you mean for 1. Just call an NSWorkspace on key 'location' stringByAppending... '?onepasswdfill=PLIST_KEY

    ??
  • jxpx777jxpx777 Code Wrangler

    AgileBits Team Member
    edited December 1969
    The first method uses the 1Click bookmarks feature of 1Password with the onepasswdfill parameter. The other method with the plists just creates the plist for the G&F operation and then opens the location that is stored in the JSON for the entry. The browser extension is then in charge of recognizing that there is a pending fill request for the URL that is opening.
  • pjrobertsonpjrobertson Member
    edited December 1969
    Still not 100% following on the second method :(

    I have a plist that looks like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
    <key>form</key>
    <string>someLongNumber</string>
    <key>location</key>
    <string>https://domain.com</string&gt;
    <key>timestamp</key>
    <date>2009-10-11T21:27:25Z</date>
    </dict>
    </plist>

    and what do I do with that?


    Just to make sure - are the .1password files in the 1password.agilekeychain/data UTF-8 encoded, and will they always be this?
    Is the data always stored in /data/default or are there other subdirectories?

    I'm reading in the files now, and just need to make a plist / QSObject :)
    Incidentally - what are the .1password file formats? It would be really easy if I could convert the 'title' works straight into keys for an NSDictionary or .plist as opposed to having to scan the contents of each file for the right parts.
  • pjrobertsonpjrobertson Member
    edited January 2010
    Got the data into an NSDict using the JSON framework (didn't realise it existed) and dumping the important data into plists :)

    So I'm guessing by method 2 you just mean dumping a load of plists in the Fill folder, then the browser extension automatically knows when I open an NSURL?
    There's no way I can dump them somewhere else?

    One question - is the timeStamp value in the 'real' plists the 'createdAt' or 'updatedAt' key in the JSONs?
    With updatedAt and DateWithTimeSince1970 I'm getting about 2 minutes off the time created in the actual PLIST when 1Pwd creates it
    :(

    Almost there now!
    I'm getting all my 200 or so plists created in a few seconds :D

    Thanks for the help!
  • jxpx777jxpx777 Code Wrangler

    AgileBits Team Member
    edited December 1969
    I think you're on the right track. The one pointer I would give is that you don't have to write out the plists all at once. The way it works is like this

    1. User invokes Go & Fill for a login
    2. You look up the login and write out the plist
    3. You invoke the appropriate NSWorkspace method to open the Location field's URL
    4. The browser extension realizes that a URL is being opened and checks for the fill plist so it knows to fill the login when the page finishes loading


    Yes, the data format is JSON and UTF-8 encoded. The timestamp is just using
    [NSDate date]
    
    for the plist timestamp. This is the timestamp for when the Go & Fill request was generated and it is what the browser extension uses to make sure that the fill plist is still applicable and not a leftover artifact from a previous (perhaps aborted?) attempt at filling.
  • pjrobertsonpjrobertson Member
    edited December 1969
    Thanks so much Jamie :)

    I've got a primitive plugin working - if you want to have a look see here: http://groups.google.com/group/blacktree-quicksilver/browse_thread/thread/8ef432f1d076e094# )

    I know lots of people would be interested in this, but I don't want to 'release' it yet!

    There's a short list at the end of that post with things I hope to add, and I'm not sure if it's possible with a 3rd party app:

    copy password to
    clipboard (useful for terminal) etc. - obviously the 1Pwd browser extension can do this without needing to unlock the 1Password app itself (the keychain still needs to be unlocked though of course), but I'm not sure if this is possible
    Open webform in 1Password - again, opening in 1Password would require 1Pwd to be unlocked (or could edit raw JSON files but that's asking for trouble!
    Edit webform - same as above

    I'm hoping to figure out QS then I can add the categories on the LHS of the 1Password app (web forms, identities etc) to appear when you right arrow into the app so you can grab info from all of them and copy to clipboard / manipulate with QS

    P.S. Would it be possible to get some higher res browser.png (in the 1Pwd/contents/resources/ folder) - the icon for web forms?
    P.P.S I realise why you were avoiding the question of deleting the plist afterward - the browser extension does this :D
  • jxpx777jxpx777 Code Wrangler

    AgileBits Team Member
    edited January 2010
    Basically, anything that will get at the user's encrypted contents is going to be off limits. So, no copying password or other encrypted data. Basically, if it's not in plaintext in that JSON file, you won't be able to do anything with it at this time and we don't have any plans to allow more extensive scripting for secure contents.

    You can allow the user to select the login in 1Password. Here's how 1Password does it:

    First, you want to set a key in the 1Password preferences. The key is "find" and should be set to the value of the URL that you want to locate the login for. With the location, this is pretty simple, but if multiple logins have identical locations, it might not find the right one using this method.

    In Objective-C, to set preferences outside your app's domain, you have to drop down to Carbon:

    CFPreferencesSetAppValue((CFStringRef)@"find", value, CFSTR("ws.agile.1Password"));
    CFPreferencesAppSynchronize(CFSTR("ws.agile.1Password"));
    


    This could also be done on the command line too with

    defaults write ws.agile.1Password fill "http://mysecretloginpage.com"
    


    Once that default value is set, launch 1Password. We do this with osascript:

    NSString *script = [NSString stringWithFormat:@"/usr/bin/osascript -e 'tell application \"Finder\"' -e 'open POSIX file \"%@\"' -e 'end tell' &", mainAppPath];
    


    but I don't see any reason a standard NSWorkspace call wouldn't work. You can play around with that a bit.

    I noticed that it would make this process much easier for your situation and more reliable for users to be able to "Reveal in 1Password" by UUID, so I'm adding that to the startup code. Should be available in the next release. When it is, you can then set the key to "findUUID" and the value to the UUID of the entry rather than just "find" key and it will select the object by UUID, which will be a much better experience for your users and will allow you to let them select other kinds of entries rather than just logins.

    Which icon are you asking for higher resolution image of? Most of those can be extracted from the icns files (Browser-Safari.png for instance could be extracted from the Safari.icns file. Should have at least 512px size. Likewise for the 1Password logo.)

    Sorry for not catching what you were on about deleting the plist. I assumed too much. It's the curse of knowledge. :)

    The one thing that I would caution you about with all of this is that these are obviously "undocumented" code paths that could be subject to change at any time. I don't think it's likely that we'll change any of it significantly since it's pretty boring stuff, but keep in mind that if we do make changes, it could break your plugin. So, caveat emptor and all of that. :)
  • jxpx777jxpx777 Code Wrangler

    AgileBits Team Member
    edited December 1969
    I've moved this to the Feature Requests and changed the title to reflect the true nature of the thread.
  • pjrobertsonpjrobertson Member
    edited January 2010
    Cheers again Jamie,

    Does the 3.0.4 release have support for the findUUID key?
    I'll add this into the plugin when I get some time. Busy trying to iron out any bugs atm (mainly with the was QS runs!)
    Thanks for the REALLY clear instruction :D
    -- does the key need to be deleted once 1Pwd is launched or does 1Pwd do this?

    The icon I'm referring to is the one attached. It's ever so slightly different from the BookmarkIcon.icns that is in the Apple resources folder.
    This icon is called Browsers.png and is found in the 1Pwd resources folder.
    It's the icon that shows up next to 'Logins' in the 1Pwd sidebar (Left hand side)
    A larger icon would be great - hopefully it'll make it easier to distinguish 1Pwd forms from other things :)

    EDIT:
    Looks like findUUID hasn't been added yet... :(
  • pjrobertsonpjrobertson Member
    edited December 1969
    Also, am I right in thinking the find key is only checked by 1Pwd on launch, so in order to open a different login 1Pwd must be restarted (meaning the keychain password has to be re-submitted)

    Cheers Jamie :D
  • jxpx777jxpx777 Code Wrangler

    AgileBits Team Member
    edited December 1969
    The find and findUUID keys would be removed from 1Password's preferences after the lookup process is completed. I've double checked and the code is in place and it's working correctly for me. You can find a UUID by looking at the filename of an entry in 1Password.agilekeychain/data/default and you can test it out by quitting 1Password and setting a findUUID and then launching 1Password. Here's how I tested this just now:

    defaults write ws.agile.1Password findUUID "A094E0CE8C9B4CC195BAA264BF795C04"
    open /Applications/1Password.app
    


    Of course, since UUIDs are globally unique, you would need to specify one of your own UUIDs so it works. Then, you can see that the findUUID was removed by running

    defaults read ws.agile.1Password findUUID
    


    which should tell you the preference is not specified.

    If you're using the Objective-C way, make sure that your have the CFStringRef casts properly set up and that the preferences domain is correct. This can be a bit tricky actually, which I discovered the hard way when writing the 1Password plugin for NetworkLocation. If you're using the shell, make sure you're wrapping the UUID in quotes. Other than that, I'm not sure where this process might be breaking down. Start with baby steps like making sure that your code is actually setting the preference. So, set the preference but don't launch 1Password from your code and then read the preferences from the command line and see if the findUUID is actually there.
  • pjrobertsonpjrobertson Member
    edited December 1969
    Having released this plugin a month or so ago, it's had some time in the wild and I've managed to iron out any bugs.

    Anybody wanting to try out the plugin, suggest new features or bugs can do so at my Github repository:

    http://github.com/pjrobertson/1Password-Plugin

    The latest version includes the ability to open the Login Form in 1Password.

    Enjoy :)
  • MartySMartyS AgileBits Customer Care (retired)
    edited December 1969
    Thanks for your contribution!
  • pjrobertsonpjrobertson Member
    edited December 1969
    Just another quick question on getting my 2nd action to work - 'Open in 1Pwd'

    Is there any way of checking to see if the Agile keychain has / hasn't been unlocked. (I.e. if 1Pwd is open but it's on the 'locked' screen)

    My scenario is this:

    if (1Pwd is running)
    if(1Pwd unlocked)
    # use some applescript to go to the selected login
    else -- i.e. (1Pwd running but locked OR 1Pwd not running)
    # set the findUUID key in the prefs & active 1Pwd

    Cheers
  • jxpx777jxpx777 Code Wrangler

    AgileBits Team Member
    edited December 1969
    If you launch 1Password, it should look for the findUUID upon unlock. So, just set the key and launch 1Password. If it needs to be unlocked, it will locate the entry after unlock. If it's already unlocked, it should just locate the entry. Is this not working for you?
  • pjrobertsonpjrobertson Member
    edited December 1969
    Nope.

    If I set the UUID when

    a) 1Pwd isn't open
    b) 1Pwd is open but it's on the unlock screen

    then it'll go to the UUID specified when I unlock it.
    But if I set it when 1Pwd is unlocked it doesn't work.

    I've managed to get to the item when it's unlocked using applescript:

    tell application "1Password" to activate
    			keystroke "L" using {command down, shift down}
    			keystroke locationText
    


    and that works. I'm just looking for the in between one where 1Pwd is open but unlocked (I need something to go in an if() statement)

    Thanks
  • pjrobertsonpjrobertson Member
    edited March 2010
    I guess I'll just have to live with this problem. :(


    I've been getting quite a few requests for a 'copy password' action. I know you've asked me not to go decrypting the JSON files, and I totally agree with this.

    One alternative way I've thought of is to build on my 'open in 1Password' script and just add a 'click button 'password'' line to the script.

    Unfortunately it doesn't look like anything in 1Pwd has been Accessibility Verified (use the Accessibility Verifier app in Developer/Utilities).

    Would it be possible for you to give the Password button a different name other than just 'copy' so I can use UI scripting to click the copy button?

    I look forward to another release....

    Cheers :)

    [Edit] or to make life easier a 'copy password' keyboard shortcut would be WAY easier to implement and better all round ;)
  • jxpx777jxpx777 Code Wrangler

    AgileBits Team Member
    edited December 1969
    I just pushed a change that will make 1Password look for a specified UUID or location on activation as well as after launching, so that should help. It should be in the next release. I hope that helps with your show in 1Password action.

    The JSON stuff is kind of tricky. One option you have is to invoke the copy menu item and then parse the resulting JSON for the password. Of course, you would need to make sure that you take appropriate precautions with this so that folks' data does not get left on the clipboard. By default 1Password clears data after a timeout and most clipboard utilities can be configured to exclude applications, but it's something to keep in mind nevertheless.

    I hope this information helps.
  • pjrobertsonpjrobertson Member
    edited December 1969
    Awesome!

    So just to get this straight:
    If I 'defaults write' (or use a cocoa method) to set the findUUID key, then no matter what 1Pwd is doing it'll flick to the specified UUID?

    I'd rather not look at decrypting the JSON file - I'm not 100% confident and don't want to make a massive gaping hole in the side of 1Pwd!

    I have the whole of the JSON file parsed into QS's cache, so there would be no need to get it again from QS itself, but again, I'd rather stay away from decrypting all together.

    Thanks for the info anyway :)

    Look forward to the next release :D
  • jxpx777jxpx777 Code Wrangler

    AgileBits Team Member
    edited December 1969
    I've adjusted it so that if they user is in edit mode, the find* key will be silently dropped so they do not lose their edits or commit unwanted edits and the key will not be still floating around for the next time they activate 1Password after they exit edit mode.

    Regarding the JSON, I was talking about copying from 1Password. If the data file is unlocked and you copy an entire entry, it actually copies the plain text JSON to the clipboard. You could parse the password out from there in theory. :)
  • pjrobertsonpjrobertson Member
    edited December 1969
    Looks like the plugin's really taken off since you guys advertised it and it was put on MacUpdate - cheers :D

    I'm gonna have another stab at adding more depth to it in the next few weeks so I'll probably have a few more questions :)

    Seeing as my plugin seems to be appearing as 'OnePassword' on lots of software sites (annoying, I know! Happens because you can't start names/classes with numbers of course) I was just wondering what's the name of your classes? Is it OnePassword like mine? Just curious...
  • pjrobertsonpjrobertson Member
    edited June 2010
    I've just looked back into the idea of using the findUUID key for when 1Password is launched (I'm still using applescript and keystrokes, which isn't that robust) but it seems pretty flakey to me.

    It's definitely working, but it's normally taking about 30 seconds or so for 1Password to actually realise. I've tried 'touch'ing the .plist etc. to try and get 1Password to 'realise' there's been a change but it's not working.

    Also sometimes it doesn't work at all - the findUUID gets removed from the prefs (can see this using defaults read) and nothing happens.

    Cheers

    Edit: Just another Idea:
    Is the popup that new in 1P3 part of the browser extension? (For editing login items) and does the browser need to be running or is it something I could potentially launch from within QS?

    Thanks again
  • MartySMartyS AgileBits Customer Care (retired)
    edited December 1969
    You're going to likely have to wait a while for Jamie or one of the other developers to come around... they're all at Apple's WWDC this week.
«1
This discussion has been closed.