From 0e47b03c566f41b244b8b661b01cd0546c9e30c4 Mon Sep 17 00:00:00 2001 From: "dmose%netscape.com" Date: Tue, 15 Nov 2005 20:08:58 +0000 Subject: [PATCH] Support LDAP authentication for autocomplete and addressbook (bug 135778); r=sspitzer@netscape.com, srilatha@netscape.com; sr=bienvenu@netscape.com --- .../public/nsILDAPAutoCompleteSession.idl | 13 ++ .../src/nsLDAPAutoCompleteSession.cpp | 183 ++++++++++++++++-- .../addrbook/src/nsLDAPAutoCompleteSession.h | 5 + 3 files changed, 189 insertions(+), 12 deletions(-) diff --git a/mailnews/addrbook/public/nsILDAPAutoCompleteSession.idl b/mailnews/addrbook/public/nsILDAPAutoCompleteSession.idl index 6e3438f8fddb..384a7967c22c 100644 --- a/mailnews/addrbook/public/nsILDAPAutoCompleteSession.idl +++ b/mailnews/addrbook/public/nsILDAPAutoCompleteSession.idl @@ -27,6 +27,7 @@ // interface nsILDAPURL; interface nsILDAPAutoCompFormatter; +interface nsIAuthPrompt; /** * Extends nsIAutoCompleteSession to have various LDAP-specific parameters. @@ -100,4 +101,16 @@ interface nsILDAPAutoCompleteSession : nsIAutoCompleteSession { * nsIAutoCompleteItem. */ attribute nsILDAPAutoCompFormatter formatter; + + /** + * "Login as..." this ID. Currently, this must be specified as a DN. + * In the future, we may support userid and/or email address as well. + * If unset, bind anonymously. + */ + attribute AUTF8String login; + + /** + * If set, use this object to get a password for logging in to the server. + */ + attribute nsIAuthPrompt authPrompter; }; diff --git a/mailnews/addrbook/src/nsLDAPAutoCompleteSession.cpp b/mailnews/addrbook/src/nsLDAPAutoCompleteSession.cpp index d68f0f6cc08a..2effb50eab09 100644 --- a/mailnews/addrbook/src/nsLDAPAutoCompleteSession.cpp +++ b/mailnews/addrbook/src/nsLDAPAutoCompleteSession.cpp @@ -41,7 +41,11 @@ #include "nsReadableUtils.h" #include "nspr.h" #include "nsLDAP.h" +#include "nsIStringBundle.h" #include "nsCRT.h" +#include "nsIObserverService.h" +#include "nsNetUtil.h" +#include "nsICategoryManager.h" #ifdef PR_LOGGING static PRLogModuleInfo *sLDAPAutoCompleteLogModule = 0; @@ -434,6 +438,7 @@ nsLDAPAutoCompleteSession::OnLDAPInit(nsresult aStatus) { nsresult rv; // temp for xpcom return values nsCOMPtr selfProxy; + nsXPIDLString passwd; // passwd to use to connect to server // Check the status from the initialization of the LDAP connection // @@ -443,6 +448,110 @@ nsLDAPAutoCompleteSession::OnLDAPInit(nsresult aStatus) return NS_ERROR_FAILURE; } + // If mAuthPrompter is set, we're expected to use it to get a password. + // + if (mAuthPrompter) { + nsUTF8String spec; + PRBool status; + + // we're going to use the URL spec of the server as the "realm" for + // wallet to remember the password by / for. + // + rv = mServerURL->GetSpec(spec); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPInit(): GetSpec" + " failed\n"); + FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, + rv, UNBOUND); + return NS_ERROR_FAILURE; + } + + // get the string bundle service + // + nsCOMPtr + stringBundleSvc(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv)); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPInit():" + " error getting string bundle service"); + FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, + rv, UNBOUND); + return rv; + } + + // get the LDAP string bundle + // + nsCOMPtr ldapBundle; + rv = stringBundleSvc->CreateBundle( + "chrome://mozldap/locale/ldap.properties", + getter_AddRefs(ldapBundle)); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPInit():" + " error creating string bundle" + " chrome://mozldap/locale/ldap.properties"); + FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, + rv, UNBOUND); + return rv; + } + + // get the title for the authentication prompt + // + nsXPIDLString authPromptTitle; + rv = ldapBundle->GetStringFromName( + NS_LITERAL_STRING("authPromptTitle").get(), + getter_Copies(authPromptTitle)); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPInit():" + "error getting 'authPromptTitle' string from bundle " + "chrome://mozldap/locale/ldap.properties"); + FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, + rv, UNBOUND); + return rv; + } + + // get the host name for the auth prompt + // + nsCAutoString host; + rv = mServerURL->GetAsciiHost(host); + if (NS_FAILED(rv)) { + FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, + UNBOUND); + return rv; + } + const PRUnichar *hostArray[1] = { NS_ConvertASCIItoUCS2(host).get() }; + + // format the hostname into the authprompt text string + // + nsXPIDLString authPromptText; + rv = ldapBundle->FormatStringFromName( + NS_LITERAL_STRING("authPromptText").get(), + hostArray, sizeof(hostArray), + getter_Copies(authPromptText)); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPInit():" + "error getting 'authPromptText' string from bundle " + "chrome://mozldap/locale/ldap.properties"); + FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, + rv, UNBOUND); + return rv; + } + + // get authentication password, prompting the user if necessary + // + rv = mAuthPrompter->PromptPassword( + authPromptTitle.get(), authPromptText.get(), + NS_ConvertUTF8toUCS2(spec).get(), + nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY, getter_Copies(passwd), + &status); + if (NS_FAILED(rv) || status == PR_FALSE) { + PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, + ("nsLDAPAutoCompleteSession::OnLDAPInit(): PromptPassword" + " encountered an error or was cancelled by the user")); + FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, + NS_ERROR_FAILURE, UNBOUND); + return NS_ERROR_FAILURE; + } + } + // create and initialize an LDAP operation (to be used for the bind) // mOperation = do_CreateInstance("@mozilla.org/network/ldap-operation;1", @@ -482,7 +591,7 @@ nsLDAPAutoCompleteSession::OnLDAPInit(nsresult aStatus) PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, ("nsLDAPAutoCompleteSession:OnLDAPInit(): initiating " "SimpleBind\n")); - rv = mOperation->SimpleBind(NULL); + rv = mOperation->SimpleBind(passwd); if (NS_FAILED(rv)) { switch (rv) { @@ -519,7 +628,6 @@ nsLDAPAutoCompleteSession::OnLDAPInit(nsresult aStatus) nsresult nsLDAPAutoCompleteSession::OnLDAPBind(nsILDAPMessage *aMessage) { - PRInt32 errCode; mOperation = 0; // done with bind op; make nsCOMPtr release it @@ -532,21 +640,46 @@ nsLDAPAutoCompleteSession::OnLDAPBind(nsILDAPMessage *aMessage) NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPBind(): couldn't get " "error code from aMessage"); - // reset to the default state + // reset to the default state, and pass along the LDAP error code + // to the formatter // - FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, - NS_ERROR_FAILURE, UNBOUND); + FinishAutoCompleteLookup( + nsIAutoCompleteStatus::failureItems, + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, errCode), + UNBOUND); return NS_ERROR_FAILURE; } - // check to be sure the bind succeeded // - if ( errCode != nsILDAPErrors::SUCCESS) { + if (errCode != nsILDAPErrors::SUCCESS) { PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_WARNING, - ("nsLDAPAutoCompleteSession::OnLDAPBind(): error binding to " - "LDAP server, errCode = %d", errCode)); + ("nsLDAPAutoCompleteSession::OnLDAPBind(): error binding to " + "LDAP server, errCode = 0x%x", errCode)); + + // if the login failed, tell the wallet to forget this password + // + if ( errCode == nsILDAPErrors::INAPPROPRIATE_AUTH || + errCode == nsILDAPErrors::INVALID_CREDENTIALS ) { + + // make sure the wallet service has been created, and in doing so, + // pass in a login-failed message to tell it to forget this passwd. + // + // apparently getting passwords stored in the wallet + // doesn't require the service to be running, which is why + // this might not exist yet. + // + rv = NS_CreateServicesFromCategory("passwordmanager", mServerURL, + "login-failed"); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPAutoCompleteSession::ForgetPassword(): error" + " creating password manager service"); + // not much to do at this point, though conceivably we could + // pop up a dialog telling the user to go manually delete + // this password in the password manager. + } + } // reset to the default state // @@ -1007,7 +1140,8 @@ nsLDAPAutoCompleteSession::InitConnection() // rv = mConnection->Init(host.get(), port, (options & nsILDAPURL::OPT_SECURE) ? PR_TRUE - : PR_FALSE, nsnull, selfProxy); + : PR_FALSE, NS_ConvertUTF8toUCS2(mLogin).get(), + selfProxy); if NS_FAILED(rv) { switch (rv) { @@ -1168,7 +1302,7 @@ nsLDAPAutoCompleteSession::FinishAutoCompleteLookup( "error calling mListener->OnAutoComplete()"); } - // set the state approrpiately + // set the state appropriately // mState = aEndState; @@ -1358,7 +1492,7 @@ nsLDAPAutoCompleteSession::IsMessageCurrent(nsILDAPMessage *aMessage, } // attribute nsILDAPAutoCompFormatter formatter -NS_IMETHODIMP +NS_IMETHODIMP nsLDAPAutoCompleteSession::GetFormatter(nsILDAPAutoCompFormatter* *aFormatter) { if (! aFormatter ) { @@ -1394,4 +1528,29 @@ nsLDAPAutoCompleteSession::SetFormatter(nsILDAPAutoCompFormatter* aFormatter) return NS_OK; } +NS_IMETHODIMP +nsLDAPAutoCompleteSession::SetLogin(const nsACString & aLogin) +{ + mLogin = aLogin; + return NS_OK; +} +NS_IMETHODIMP +nsLDAPAutoCompleteSession::GetLogin(nsACString & aLogin) +{ + aLogin = mLogin; + return NS_OK; +} + +NS_IMETHODIMP +nsLDAPAutoCompleteSession::GetAuthPrompter(nsIAuthPrompt **aAuthPrompter) +{ + NS_IF_ADDREF(*aAuthPrompter = mAuthPrompter); + return NS_OK; +} +NS_IMETHODIMP +nsLDAPAutoCompleteSession::SetAuthPrompter(nsIAuthPrompt *aAuthPrompter) +{ + mAuthPrompter = aAuthPrompter; + return NS_OK; +} #endif diff --git a/mailnews/addrbook/src/nsLDAPAutoCompleteSession.h b/mailnews/addrbook/src/nsLDAPAutoCompleteSession.h index ce73117b3aa8..719a854cf280 100644 --- a/mailnews/addrbook/src/nsLDAPAutoCompleteSession.h +++ b/mailnews/addrbook/src/nsLDAPAutoCompleteSession.h @@ -32,6 +32,7 @@ #include "nsISupportsArray.h" #include "nsIConsoleService.h" #include "nsVoidArray.h" +#include "nsIAuthPrompt.h" // 964665d0-1dd1-11b2-aeae-897834fb00b9 // @@ -73,6 +74,10 @@ class nsLDAPAutoCompleteSession : public nsILDAPMessageListener, PRUint32 mCjkMinStringLength; // ignore CJK strings < this size char **mSearchAttrs; // outputFormat search attrs for SearchExt call PRUint32 mSearchAttrsSize; // size of above array + nsCOMPtr mAuthPrompter; // used to prompt for the password +// XXX hack until nsUTF8String exists +#define nsUTF8String nsCString + nsUTF8String mLogin; // authenticate as this user // used to format the ldap message into an nsIAutoCompleteItem //