git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2] Add a credential-helper for KDE

From
Lukas Sandström <luksan@gmail.com>
Date
Sep 18, 2011, 14:52 UTC
Message-ID
<4E7605CA.7020204@gmail.com>
In-Reply-To
<20110831014237.GA2519@sigill.intra.peff.net>

This Python script plugs into the credentials API of Git to ask the user for passwords with a nice KDE password dialog.

The password is saved in the KWallet.
Signed-off-by: Lukas Sandström <luksan@gmail.com>
---
On 2011-08-31 03:42, Jeff King wrote:
Show 7 quoted lines
> Can we call it git-credential-kdewallet or similar? Then users can just
> do:
> 
>   git config credential.helper kdewallet
> 
> (where "kdewallet" can be whatever you think is most appropriate; the
> key is naming it git-credential-*).
Done.
[...]
Show 33 quoted lines
> If I am reading this correctly, you look up based purely on the context
> token. Which means that if I do something like this:
> 
>   $ git push https://host.com/repo.git
>   [enter username: user1, password: foo]
>   $ git push https://user2@host.com/other-repo.git
> 
> We will invoke the helper as:
> 
>   git credential-kdewallet --unique=https:host.com --username=user2
> 
> but the helper will ignore the "user2" bit, and return "user1 / foo".
> 
> The "cache" helper I wrote handles this situation better, by indexing
> both on the token and the username. I wonder if the username should
> become part of the token. Or if the token should really just become a
> canonicalized URL, minus the actual path. So the first one would get:
> 
>   --unique=https://host.com
> 
> and the second would get:
> 
>   --unique=https://user2@host.com
> 
> Then helpers wouldn't need to worry about doing anything special.
> 
> What do you think? Also, any comments in general on writing a helper?
> You are the first one besides me to do so. Did you find anything in the
> interface or the documentation confusing? Suggestions are very welcome,
> as nothing has been released yet and we're free to tweak as much as we
> want.
> 
> -Peff

Right. Multiple usernames per "unique" context is supported in this version. I looked at the git-credential-storage helper when I wrote the first patch, which didn't have obvious support for multiple usernames per unique context.

Keeping the username outside the token is probably a good thing, but perhaps it should be clarified in the api-docs that multiple usernames has to be supported.

Also; what about rejecting credentials. This code currently deletes just a username/password pair if a username is specified, and all credentials associated with the token if only --unique and --reject is specified. Is this correct/expected behavior?

When I first wrote the helper I tried to immediately ask for a new password if a credential was rejected, but this didn't work with the HTTP auth code, since it doesn't retry the auth with the new credentials after a reject. I think it would be better if we asked for a new password instead of just saying "auth failed" and having the user retry the fetch/pull when the stored credentials are incorrect.

/Lkas
 .../git-credential-kdewallet.py                    |  137 ++++++++++++++++++++
 1 files changed, 137 insertions(+), 0 deletions(-)
 create mode 100755 contrib/git-credential-kdewallet/git-credential-kdewallet.py
diff --git a/contrib/git-credential-kdewallet/git-credential-kdewallet.py b/contrib/git-credential-kdewallet/git-credential-kdewallet.py
new file mode 100755
index 0000000..29c4ae1
--- /dev/null
+++ b/contrib/git-credential-kdewallet/git-credential-kdewallet.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# encoding=utf-8
+#
+# Copyright 2011, Lukas Sandström
+#
+# Licensed under the GPL version 2.
+
+import sys
+from PyQt4.QtCore import QString
+from PyKDE4.kdecore import i18n, ki18n, KAboutData, KCmdLineArgs, KCmdLineOptions
+from PyKDE4.kdeui import KApplication, KWallet, KPasswordDialog
+
+appName     = "git-credential-kdewallet"
+catalog     = ""
+programName = ki18n ("Git KDE credentials helper")
+version     = "0.1"
+description = ki18n ("Credentials storage helper for Git")
+license     = KAboutData.License_GPL_V2
+copyright   = ki18n ("(c) 2011 Lukas Sandström")
+text        = ki18n ("none")
+homePage    = "http://www.git-scm.com"
+bugEmail    = "luksan@gmail.com"
+
+aboutData   = KAboutData (appName, catalog, programName, version, description,
+                          license, copyright, text, homePage, bugEmail)
+
+class CredentialHelper(KApplication):
+    def __init__(self, token, username = None, desc = None, reject = False):
+        super(CredentialHelper, self).__init__()
+        self.password = None
+        self.username = username
+        self.save_password = False
+        self.token = token
+        self.desc = desc
+
+        if not self.token:
+            return
+
+        self.open_wallet()
+
+        if reject:
+            self.reject_credential()
+            return
+
+        if not self.check_wallet():
+            self.ask_password_dialog()
+
+        if self.save_password:
+            self.store_password()
+
+        self.output_credentials()
+
+    def output_credentials(self):
+        if self.username:
+            print "username=" + self.username
+        if self.password:
+            print "password=" + self.password
+
+    def reject_credential(self):
+        (res, data) = self.wallet.readMap(self.token)
+        if self.username:
+            try:
+                del data[QString(self.username)]
+            except KeyError:
+                pass
+            self.wallet.writeMap(self.token, data)
+        else:
+            self.wallet.removeEntry(self.token)
+
+    def store_password(self):
+        (res, data) = self.wallet.readMap(self.token)
+        data[QString(self.username)] = QString(self.password)
+        self.wallet.writeMap(QString(self.token), data)
+
+    def open_wallet(self):
+        self.wallet = KWallet.Wallet.openWallet(
+            KWallet.Wallet.LocalWallet(), 0, KWallet.Wallet.Synchronous)
+        if not self.wallet.isOpen():
+            return None
+        if not self.wallet.hasFolder("GitCredentials"):
+            self.wallet.createFolder("GitCredentials")
+        self.wallet.setFolder("GitCredentials")
+
+    def check_wallet(self):
+        (res, data) = self.wallet.readMap(self.token)
+        if res != 0:
+            return None
+        for u, p in data.iteritems():
+            # Pick the first complete credential if no username is specified
+            if not self.username and u and p:
+                self.username = u
+                self.password = p
+                return True
+            if self.username == u:
+                self.password = p
+                return True
+        return None
+
+    def ask_password_dialog(self):
+        dlg = KPasswordDialog(None,
+            KPasswordDialog.KPasswordDialogFlag(
+                KPasswordDialog.ShowKeepPassword |
+                KPasswordDialog.ShowUsernameLine))
+        if self.desc:
+            desc = self.desc
+        else:
+            desc = self.token
+        dlg.setPrompt(i18n("Please enter username and password for %s" % (desc)))
+        dlg.setUsername(self.username)
+        dlg.setKeepPassword(True)
+        if not dlg.exec_():
+            return
+        self.username = dlg.username()
+        self.password = dlg.password()
+        self.save_password = dlg.keepPassword()
+
+def main():
+    KCmdLineArgs.init(sys.argv, aboutData)
+
+    options = KCmdLineOptions()
+    options.add("unique <token>", ki18n("Unique token identifying the credential"))
+    options.add("description <desc>", ki18n("Human readable description of the credential"))
+    options.add("username <username>", ki18n("Requested username"))
+    options.add("reject", ki18n("Purge credential"))
+
+    KCmdLineArgs.addCmdLineOptions(options)
+    args = KCmdLineArgs.parsedArgs();
+
+    username = args.getOption("username")
+    token = args.getOption("unique")
+    desc = args.getOption("description")
+    reject = args.isSet("reject")
+
+    app = CredentialHelper(token, username, desc, reject)
+
+if __name__ == "__main__":
+    main()
-- 
1.7.6.1
Previous: Jeff KingNext: Jeff King
Message 3 of 5 in “Add a credential-helper for KDE”
  1. Add a credential-helper for KDELukas Sandström, Aug 27, 2011
  2. Jeff KingAug 31, 2011
  3. Add a credential-helper for KDELukas Sandström, Sep 18, 2011
  4. Jeff KingSep 18, 2011
  5. Jeff KingSep 30, 2011

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.