Skip to content

Commit

Permalink
Bug 1221588 - Added search by username, password, or hostname API to …
Browse files Browse the repository at this point in the history
…logins
  • Loading branch information
Stephan Leroux committed Nov 17, 2015
1 parent 6696a26 commit 847044f
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Storage/Logins.swift
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,8 @@ public protocol BrowserLogins {
func getUsageDataForLoginByGUID(guid: GUID) -> Deferred<Maybe<LoginUsageData>>
func getLoginsForProtectionSpace(protectionSpace: NSURLProtectionSpace) -> Deferred<Maybe<Cursor<LoginData>>>
func getLoginsForProtectionSpace(protectionSpace: NSURLProtectionSpace, withUsername username: String?) -> Deferred<Maybe<Cursor<LoginData>>>
func getAllLogins() -> Deferred<Maybe<Cursor<LoginData>>>
func searchLoginsByUsername(username: String?, orPassword password: String?, orHostname hostname: String?) -> Deferred<Maybe<Cursor<LoginData>>>

// Add a new login regardless of whether other logins might match some fields. Callers
// are responsible for querying first if they care.
Expand Down
30 changes: 30 additions & 0 deletions Storage/MockLogins.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,36 @@ public class MockLogins: BrowserLogins, SyncableLogins {
return Deferred(value: Maybe(success: cursor))
}

public func getAllLogins() -> Deferred<Maybe<Cursor<LoginData>>> {
let cursor = ArrayCursor(data: cache.sort({ (loginA, loginB) -> Bool in
return loginA.hostname > loginB.hostname
}).map({ login in
return login as LoginData
}))
return Deferred(value: Maybe(success: cursor))
}

public func searchLoginsByUsername(username: String?, orPassword password: String?, orHostname hostname: String?) -> Deferred<Maybe<Cursor<LoginData>>> {
let cursor = ArrayCursor(data: cache.filter({ login in
var checks = [Bool]()
if let username = username {
checks.append(login.username?.startsWith(username) ?? false)
}
if let password = password {
checks.append(login.password.startsWith(password))
}
if let hostname = hostname {
checks.append(login.hostname.startsWith(hostname))
}
return checks.contains(true)
}).sort({ (loginA, loginB) -> Bool in
return loginA.hostname > loginB.hostname
}).map({ login in
return login as LoginData
}))
return Deferred(value: Maybe(success: cursor))
}

// This method is only here for testing
public func getUsageDataForLoginByGUID(guid: GUID) -> Deferred<Maybe<LoginUsageData>> {
let res = cache.filter({ login in
Expand Down
31 changes: 31 additions & 0 deletions Storage/SQL/SQLiteLogins.swift
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,37 @@ public class SQLiteLogins: BrowserLogins {
return db.runQuery(sql, args: args, factory: SQLiteLogins.LoginDataFactory)
}

public func getAllLogins() -> Deferred<Maybe<Cursor<LoginData>>> {
return searchLoginsByUsername(nil)
}

public func searchLoginsByUsername(username: String?, orPassword password: String? = nil, orHostname hostname: String? = nil) -> Deferred<Maybe<Cursor<LoginData>>> {
let projection = SQLiteLogins.MainWithLastUsedColumns
var searchClauses = [String]()
if let username = username {
searchClauses.append(" username LIKE '\(username)%' ")
}

if let password = password {
searchClauses.append(" password LIKE '\(password)%' ")
}

if let hostname = hostname {
searchClauses.append(" hostname LIKE '\(hostname)%' ")
}

let whereSearchClause = searchClauses.count > 0 ? "AND" + searchClauses.joinWithSeparator("OR") : ""
let sql =
"SELECT \(projection) FROM " +
"\(TableLoginsLocal) WHERE is_deleted = 0 " + whereSearchClause +
"UNION ALL " +
"SELECT \(projection) FROM " +
"\(TableLoginsMirror) WHERE is_overridden = 0 " + whereSearchClause +
"ORDER BY hostname ASC"

return db.runQuery(sql, args: [], factory: SQLiteLogins.LoginDataFactory)
}

public func addLogin(login: LoginData) -> Success {
let nowMicro = NSDate.nowMicroseconds()
let nowMilli = nowMicro / 1000
Expand Down
109 changes: 109 additions & 0 deletions StorageTests/TestLogins.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,115 @@ class TestSQLiteLogins: XCTestCase {
waitForExpectationsWithTimeout(10.0, handler: nil)
}

func testSearchLogins() {
let loginA = Login.createWithHostname("alphabet.com", username: "username1", password: "password1")
let loginB = Login.createWithHostname("alpha.com", username: "username2", password: "password2")

func addLogins() -> Success {
return addLogin(loginB) >>> {
self.addLogin(loginA) >>> succeed
}
}

func checkAllLogins() -> Success {
return logins.getAllLogins() >>== { results in
XCTAssertEqual(results.count, 2)
return succeed()
}
}

func checkFindMultipleUsernames() -> Success {
return logins.searchLoginsByUsername("username") >>== { results in
XCTAssertEqual(results.count, 2)
XCTAssertEqual(results[0]!.username, "username2")
XCTAssertEqual(results[1]!.username, "username1")
return succeed()
}
}

func checkFindSingleUsername() -> Success {
return logins.searchLoginsByUsername("username1") >>== { results in
XCTAssertEqual(results.count, 1)
XCTAssertEqual(results[0]!.username, "username1")
return succeed()
}
}

func checkFindMultipleHostnames() -> Success {
return logins.searchLoginsByUsername(nil, orPassword: nil, orHostname: "alpha") >>== { results in
XCTAssertEqual(results.count, 2)
XCTAssertEqual(results[0]!.hostname, "alpha.com")
XCTAssertEqual(results[1]!.hostname, "alphabet.com")
return succeed()
}
}

func checkFindSingleHostname() -> Success {
return logins.searchLoginsByUsername(nil, orPassword: nil, orHostname: "alphabet") >>== { results in
XCTAssertEqual(results.count, 1)
XCTAssertEqual(results[0]!.hostname, "alphabet.com")
return succeed()
}
}

func checkFindMultiplePasswords() -> Success {
return logins.searchLoginsByUsername(nil, orPassword: "password") >>== { results in
XCTAssertEqual(results.count, 2)
XCTAssertEqual(results[0]!.password, "password2")
XCTAssertEqual(results[1]!.password, "password1")
return succeed()
}
}

func checkFindSinglePassword() -> Success {
return logins.searchLoginsByUsername(nil, orPassword: "password1") >>== { results in
XCTAssertEqual(results.count, 1)
XCTAssertEqual(results[0]!.password, "password1")
return succeed()
}
}

func checkFindPasswordOrHostname() -> Success {
return logins.searchLoginsByUsername(nil, orPassword: "password", orHostname: "alpha") >>== { results in
XCTAssertEqual(results.count, 2)
return succeed()
}
}

func checkFindHostnameOrUsername() -> Success {
return logins.searchLoginsByUsername("username", orPassword: nil, orHostname: "alpha") >>== { results in
XCTAssertEqual(results.count, 2)
return succeed()
}
}

func checkFindHostnameOrUsernameOrPassword() -> Success {
return logins.searchLoginsByUsername("username", orPassword: "password", orHostname: "alpha") >>== { results in
XCTAssertEqual(results.count, 2)
return succeed()
}
}

XCTAssertTrue(addLogins().value.isSuccess)

XCTAssertTrue(checkAllLogins().value.isSuccess)

XCTAssertTrue(checkFindMultipleUsernames().value.isSuccess)
XCTAssertTrue(checkFindSingleUsername().value.isSuccess)

XCTAssertTrue(checkFindMultipleHostnames().value.isSuccess)
XCTAssertTrue(checkFindSingleHostname().value.isSuccess)

XCTAssertTrue(checkFindMultiplePasswords().value.isSuccess)
XCTAssertTrue(checkFindSinglePassword().value.isSuccess)

XCTAssertTrue(checkFindPasswordOrHostname().value.isSuccess)
XCTAssertTrue(checkFindHostnameOrUsername().value.isSuccess)
XCTAssertTrue(checkFindHostnameOrUsernameOrPassword().value.isSuccess)

XCTAssertTrue(removeAllLogins().value.isSuccess)
}

/*
func testAddUseOfLogin() {
let expectation = self.expectationWithDescription("Add visit")
Expand Down

0 comments on commit 847044f

Please sign in to comment.