Skip to content

Commit

Permalink
Implemented new request type "get-logins-custom-search" to search for…
Browse files Browse the repository at this point in the history
… entries like through the search box in KeePass
  • Loading branch information
berrnd committed Sep 16, 2015
1 parent 73306a1 commit 5361f72
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 1 deletion.
Binary file modified KeePassHttp.plgx
Binary file not shown.
191 changes: 191 additions & 0 deletions KeePassHttp/Handlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,145 @@ private void GetAllLoginsHandler(Request r, Response resp, Aes aes)
}
}

private void GetLoginsCustomSearchHandler(Request r, Response resp, Aes aes)
{
if (!VerifyRequest(r, aes))
return;

string clientId = r.Id;

string searchString = null;
if (r.SearchString != null)
searchString = CryptoTransform(r.SearchString, true, false, aes, CMode.DECRYPT);

var items = FindMatchingEntriesLikeSearchbox(r, aes);
if (items.ToList().Count > 0)
{
Func<PwEntry, bool> filter = delegate (PwEntry e)
{
var c = GetEntryConfig(e);
if (c != null)
{
return !c.Allow.Contains(clientId);
}
return true;
};

var configOpt = new ConfigOpt(this.host.CustomConfig);
var config = GetConfigEntry(true);
var autoAllowS = config.Strings.ReadSafe("Auto Allow");
var autoAllow = autoAllowS != null && autoAllowS.Trim() != "";
autoAllow = autoAllow || configOpt.AlwaysAllowAccess;
var needPrompting = from e in items where filter(e.entry) select e;

if (needPrompting.ToList().Count > 0 && !autoAllow)
{
var win = this.host.MainWindow;

using (var f = new AccessControlForm())
{
win.Invoke((MethodInvoker)delegate
{
f.Icon = win.Icon;
f.Plugin = this;
f.Entries = (from e in items where filter(e.entry) select e.entry).ToList();
f.Host = clientId;
f.Load += delegate { f.Activate(); };
f.ShowDialog(win);
if (f.Remember && (f.Allowed || f.Denied))
{
foreach (var e in needPrompting)
{
var c = GetEntryConfig(e.entry);
if (c == null)
c = new KeePassHttpEntryConfig();
var set = f.Allowed ? c.Allow : c.Deny;
set.Add(clientId);
SetEntryConfig(e.entry, c);
}
}
if (!f.Allowed)
{
items = items.Except(needPrompting);
}
});
}
}

var itemsList = items.ToList();

if (configOpt.SpecificMatchingOnly)
{
itemsList = (from e in itemsList
orderby e.entry.UsageCount ascending
select e).ToList();

ulong lowestDistance = itemsList[0].entry.UsageCount;

itemsList = (from e in itemsList
where e.entry.UsageCount == lowestDistance
orderby e.entry.UsageCount
select e).ToList();

}

if (configOpt.SortResultByUsername)
{
var items2 = from e in itemsList orderby e.entry.UsageCount ascending, GetUserPass(e)[0] ascending select e;
itemsList = items2.ToList();
}
else
{
var items2 = from e in itemsList orderby e.entry.UsageCount ascending, e.entry.Strings.ReadSafe(PwDefs.TitleField) ascending select e;
itemsList = items2.ToList();
}

foreach (var entryDatabase in itemsList)
{
var e = PrepareElementForResponseEntries(configOpt, entryDatabase);
resp.Entries.Add(e);
}

if (itemsList.Count > 0)
{
var names = (from e in resp.Entries select e.Name).Distinct<string>();
var n = String.Join("\n ", names.ToArray<string>());

if (configOpt.ReceiveCredentialNotification)
ShowNotification(String.Format("{0}: is receiving credentials for:\n {1}", r.Id, n));
}

resp.Success = true;
resp.Id = r.Id;
SetResponseVerifier(resp, aes);

foreach (var entry in resp.Entries)
{
entry.Name = CryptoTransform(entry.Name, false, true, aes, CMode.ENCRYPT);
entry.Login = CryptoTransform(entry.Login, false, true, aes, CMode.ENCRYPT);
entry.Uuid = CryptoTransform(entry.Uuid, false, true, aes, CMode.ENCRYPT);
entry.Password = CryptoTransform(entry.Password, false, true, aes, CMode.ENCRYPT);

if (entry.StringFields != null)
{
foreach (var sf in entry.StringFields)
{
sf.Key = CryptoTransform(sf.Key, false, true, aes, CMode.ENCRYPT);
sf.Value = CryptoTransform(sf.Value, false, true, aes, CMode.ENCRYPT);
}
}
}

resp.Count = resp.Entries.Count;
}
else
{
resp.Success = true;
resp.Id = r.Id;
SetResponseVerifier(resp, aes);
}
}

private IEnumerable<PwEntryDatabase> FindMatchingEntries(Request r, Aes aes)
{
string submitHost = null;
Expand Down Expand Up @@ -237,6 +376,58 @@ private IEnumerable<PwEntryDatabase> FindMatchingEntries(Request r, Aes aes)
return result;
}

private IEnumerable<PwEntryDatabase> FindMatchingEntriesLikeSearchbox(Request r, Aes aes)
{
string searchString = null;
string realm = null;
var listResult = new List<PwEntryDatabase>();
if (r.SearchString != null)
{
searchString = CryptoTransform(r.SearchString, true, false, aes, CMode.DECRYPT);
}

if (r.Realm != null)
realm = CryptoTransform(r.Realm, true, false, aes, CMode.DECRYPT);

var parms = MakeSearchParametersLikeSearchBox();

List<PwDatabase> listDatabases = new List<PwDatabase>();

var configOpt = new ConfigOpt(this.host.CustomConfig);
if (configOpt.SearchInAllOpenedDatabases)
{
foreach (PwDocument doc in host.MainWindow.DocumentManager.Documents)
{
if (doc.Database.IsOpen)
{
listDatabases.Add(doc.Database);
}
}
}
else
{
listDatabases.Add(host.Database);
}

int listCount = 0;
foreach (PwDatabase db in listDatabases)
{
while (listResult.Count == listCount)
{
parms.SearchString = searchString;
var listEntries = new PwObjectList<PwEntry>();
db.RootGroup.SearchEntries(parms, listEntries);
foreach (var le in listEntries)
{
listResult.Add(new PwEntryDatabase(le, db));
}
}
listCount = listResult.Count;
}

return listResult;
}

private void GetLoginsCountHandler(Request r, Response resp, Aes aes)
{
if (!VerifyRequest(r, aes))
Expand Down
17 changes: 17 additions & 0 deletions KeePassHttp/KeePassHttp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@ private SearchParameters MakeSearchParameters()
return p;
}

private SearchParameters MakeSearchParametersLikeSearchBox()
{
var p = new SearchParameters();
p.SearchInTitles = true;
p.RegularExpression = false;
p.SearchInGroupNames = true;
p.SearchInNotes = true;
p.SearchInOther = true;
p.SearchInPasswords = true;
p.SearchInTags = true;
p.SearchInUrls = true;
p.SearchInUserNames = true;
p.SearchInUuids = true;
return p;
}

private string CryptoTransform(string input, bool base64in, bool base64out, Aes cipher, CMode mode)
{
byte[] bytes;
Expand Down Expand Up @@ -192,6 +208,7 @@ public override bool Initialize(IPluginHost host)
handlers.Add(Request.TEST_ASSOCIATE, TestAssociateHandler);
handlers.Add(Request.ASSOCIATE, AssociateHandler);
handlers.Add(Request.GET_LOGINS, GetLoginsHandler);
handlers.Add(Request.GET_LOGINS_CUSTOM_SEARCH, GetLoginsCustomSearchHandler);
handlers.Add(Request.GET_LOGINS_COUNT, GetLoginsCountHandler);
handlers.Add(Request.GET_ALL_LOGINS, GetAllLoginsHandler);
handlers.Add(Request.SET_LOGIN, SetLoginHandler);
Expand Down
8 changes: 7 additions & 1 deletion KeePassHttp/Protocol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ private void SetResponseVerifier(Response r, Aes aes)
public class Request
{
public const string GET_LOGINS = "get-logins";
public const string GET_LOGINS_CUSTOM_SEARCH = "get-logins-custom-search";
public const string GET_LOGINS_COUNT = "get-logins-count";
public const string GET_ALL_LOGINS = "get-all-logins";
public const string SET_LOGIN = "set-login";
Expand Down Expand Up @@ -97,6 +98,11 @@ public class Request
/// </summary>
public string SubmitUrl;

/// <summary>
/// Always encrypted, used with get-logins-custom-search
/// </summary>
public string SearchString;

/// <summary>
/// Send the AES key ID with the 'associate' request
/// </summary>
Expand Down Expand Up @@ -129,7 +135,7 @@ public Response(string request, string hash)
{
RequestType = request;

if (request == Request.GET_LOGINS || request == Request.GET_ALL_LOGINS || request == Request.GENERATE_PASSWORD)
if (request == Request.GET_LOGINS || request == Request.GET_ALL_LOGINS || request == Request.GENERATE_PASSWORD || request == Request.GET_LOGINS_CUSTOM_SEARCH)
Entries = new List<ResponseEntry>();
else
Entries = null;
Expand Down

0 comments on commit 5361f72

Please sign in to comment.