Skip to content

Commit

Permalink
Feat/search (#4)
Browse files Browse the repository at this point in the history
* Add search in ciphers : Allows to search for ciphers which match type, username and uri.
URI matching is done through the jslib service

* refactor: Externalize CipherType

* style: remove warnings about console

* fix: We missed this when converting init to setup
  • Loading branch information
edas authored Sep 3, 2019
1 parent 8cdfcdf commit 395dc1c
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 23 deletions.
3 changes: 2 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module.exports = {
moduleFileExtensions: ['js', 'jsx', 'json'],
setupFilesAfterEnv: ['<rootDir>enzyme.setup.js']
setupFilesAfterEnv: ['<rootDir>enzyme.setup.js'],
testPathIgnorePatterns: ['@bitwarden/jslib', 'transpiled/']
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"watch": "yarn watch:keys",
"watch:keys": "env BABEL_ENV=transpilation yarn babel src/ --out-dir transpiled --watch",
"lint": "eslint 'src/**/*.{js,jsx}' 'playgrounds/src/**/*.{js,jsx}'",
"test": "jest",
"test": "NODE_OPTIONS=\"--max-old-space-size=8192\" jest",
"semantic-release": "semantic-release"
},
"repository": {
Expand Down
4 changes: 3 additions & 1 deletion playgrounds/src/cozy-keys.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ const reducer = combineReducers({

const store = createStore(reducer)

const setToWindow = client => { window.vaultClient = client }
const setToWindow = client => {
window.vaultClient = client
}

function VaultComponent({ client }) {
const uri = client.getStackClient().uri
Expand Down
3 changes: 3 additions & 0 deletions src/CipherType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export {
CipherType as default
} from './@bitwarden/jslib/services/cipher.service'
6 changes: 3 additions & 3 deletions src/WebPlatformUtilsService.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import swal from 'sweetalert'
import StrippedWebPlatformUtilsService from './StrippedWebPlatformUtilsService'

import { Utils } from './@bitwarden/jslib/misc/utils'
import { DeviceType } from './@bitwarden/jslib/enums/deviceType'

export default class WebPlatformUtilsService extends StrippedWebPlatformUtilsService {
constructor(i18nService, messagingService) {
Expand All @@ -21,7 +20,8 @@ export default class WebPlatformUtilsService extends StrippedWebPlatformUtilsSer
}

analyticsId() {
console.log('No analytics, please fix the caller')
// eslint-disable-next-line no-console
console.warn('No analytics, please fix the caller')
return ''
}

Expand Down Expand Up @@ -232,7 +232,7 @@ export default class WebPlatformUtilsService extends StrippedWebPlatformUtilsSer
// Security exception may be thrown by some browsers.
doc.execCommand('copy')
} catch (e) {
// tslint:disable-next-line
// eslint-disable-next-line no-console
console.warn('Copy to clipboard failed.', e)
} finally {
copyEl.removeChild(textarea)
Expand Down
91 changes: 74 additions & 17 deletions src/WebVaultClient.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import MicroEE from 'microee'

import eq from 'lodash/eq'

import { Utils } from './@bitwarden/jslib/misc/utils'

import { ApiService } from './@bitwarden/jslib/services/api.service'
Expand Down Expand Up @@ -29,13 +31,6 @@ import { WebCryptoFunctionService } from './@bitwarden/jslib/services/webCryptoF

import { CipherType } from './@bitwarden/jslib/enums/cipherType'

import { Cipher } from './@bitwarden/jslib/models/domain/cipher'
import { Login } from './@bitwarden/jslib/models/domain/login'
import { Field } from './@bitwarden/jslib/models/domain/field'
import Domain from './@bitwarden/jslib/models/domain/domainBase'

import { CipherView } from './@bitwarden/jslib/models/view/cipherView'

import { EmailRequest } from './@bitwarden/jslib/models/request/emailRequest'
import { EmailTokenRequest } from './@bitwarden/jslib/models/request/emailTokenRequest'

Expand All @@ -55,7 +50,7 @@ Utils.init()
* vault = WebVaultClient(instance)
* await vault.unlock(masterPassword)
* await vault.sync()
* const all = vault.getAllDecrypted({type: vault.cipherTypes.Login})
* const all = vault.getAllDecrypted({type: CipherType.Login})
* ```
*/
class WebVaultClient {
Expand All @@ -75,15 +70,15 @@ class WebVaultClient {
this.email = CozyUtils.getEmail(instance_or_email)
this.urls = urls || {} //TODO
this.locale = locale || 'en'
this.init({unsafeStorage})
this.init({ unsafeStorage })
window.webVaultClient = this
}

/*
* @private
* Initialize the undelying libraries
*/
init({unsafeStorage}) {
init({ unsafeStorage }) {
const messagingService = new NoopMessagingService()
const i18nService = new I18nService(this.locale, './locales')
const platformUtilsService = new WebPlatformUtilsService(
Expand Down Expand Up @@ -308,7 +303,7 @@ class WebVaultClient {
* Get all (encrypted) data from the local vault
*
* @param {object} options - optional
* @param {integer} options.type - type of data to get, see `cipherTypes`
* @param {integer} options.type - type of data to get, see `CipherType`
* @return {[Cipher]} all ciphers in the vault, filtered by type if requested
*/
async getAll({ type } = {}) {
Expand All @@ -321,7 +316,7 @@ class WebVaultClient {
* @return {[Cipher]} all ciphers of type "Login" in the vault
*/
async getAllLogins() {
return this.getAll({ type: this.cipherTypes.Login })
return this.getAll({ type: CipherType.Login })
}

/**
Expand All @@ -334,24 +329,86 @@ class WebVaultClient {
return cipher.decrypt()
}

/**
* Get Data from a cipher
* @param {Cipher} cipher
* @return {CipherData} decrypted data
*/
async getData(cipher) {
this.attachToGlobal()
const userId = await this.userService.getUserId()
return cipher.toCipherData(userId)
}

/**
* Get all data from the local vault and decrypt them
*
* If provided a type of cipher or an uri, will only return
* matching ciphers.
*
* @param {object} options - optional
* @param {integer} options.type - type of data to get, see `cipherTypes`
* @param {integer} options.type - type of data to get, see `CipherType`
* @param {string} options.uri - uri of the remote service
* @return {[CipherView]} decrypted ciphers, filtered by type if requested
*/
async getAllDecrypted({ type } = {}) {
const all = await this.cipherService.getAll({ type })
return Promise.all(all.map(cipher => this.decrypt(cipher)))
async getAllDecrypted({ type, uri } = {}) {
if (uri) {
const all = await this.cipherService.getAllDecryptedForUrl(uri)
return type ? all.filter(c => c.type == type) : all
} else {
const all = await this.getAll({ type })
return Promise.all(all.map(cipher => this.decrypt(cipher)))
}
}

/**
* Get all logins from the local vault and decrypt them
* @return {[CipherView]} decrypted ciphers of type "Login"
*/
async getAllDecryptedLogins() {
return this.getAllDecrypted({ type: this.cipherTypes.Login })
return this.getAllDecrypted({ type: CipherType.Login })
}

/**
* Get all decrypted ciphers for a set of match
* @param {object} match
* @param {CipherType} match.type - type of cipher
* @param {string|RegExp} match.name - name of cipher
* @param {string|RegExp} match.username - a login.username to match
* @param {string|RegExp} match.uri - an login.uri to match
* @return {CipherView[]}
*/
async getAllDecryptedFor({ type, uri, username, name }) {
const all = await this.getAllDecrypted({ type, uri })
return all.filter(view => {
if (username) {
if (!view.login) return false
if (!this.weakMatch(view.login.username, username)) return false
}
if (name) {
if (!this.weakMatch(view.name, name)) return false
}
return true
})
}

/**
* @private
* test if two parameters match
* @param {object} source
* @param {string|number|boolean|RegExp|Array} compare
* @return boolean
*/
weakMatch(source, compare) {
if (compare === null) return true
if (source === null) return false
if (compare instanceof Array) {
return compare.find(c => this.weakMatch(source, c))
} else if (compare instanceof RegExp) {
return source.match(compare)
} else {
return eq(source, compare)
}
}

/**
Expand Down
83 changes: 83 additions & 0 deletions src/WebVaultClient.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import WebVaultClient from './WebVaultClient'

describe('WebVaultClient', () => {

describe('weakMatch', () => {
let client
beforeAll(() => {
client = new WebVaultClient('https://me.cozy.wtf')
})

it('should match two identic strings', () => {
const source = 'Hello'
const compare = 'Hello'
expect(client.weakMatch(source, compare)).toBeTruthy()
})

it('should not match two different strings', () => {
const source = 'Hello'
const compare = 'Bonjour'
expect(client.weakMatch(source, compare)).toBeFalsy()
})

it('should match two identic numbers', () => {
const source = 345
const compare = 345
expect(client.weakMatch(source, compare)).toBeTruthy()
})

it('should not match two different numbers', () => {
const source = 345
const compare = 999
expect(client.weakMatch(source, compare)).toBeFalsy()
})

xit('should match two booleans', () => {
const source = true
const compare = true
expect(client.weakMatch(source, compare)).toBeTruthy()
})

it('should not match different booleans', () => {
const source = true
const compare = false
expect(client.weakMatch(source, compare)).toBeFalsy()
})

it('should not match a string and a number', () => {
const source = '0'
const compare = 0
expect(client.weakMatch(source, compare)).toBeFalsy()
})

it('should not match a string with a boolean', () => {
const source = 'Hello'
const compare = true
expect(client.weakMatch(source, compare)).toBeFalsy()
})

it('should match a string with an identic string inside an array', () => {
const source = 'Hello'
const compare = ['Bonjour', 'Hello', 'Ciao']
expect(client.weakMatch(source, compare)).toBeTruthy()
})

it('should not match a string with an array of different strings', () => {
const source = 'Hello'
const compare = ['Bye', 'Salut', 'Arrivederci']
expect(client.weakMatch(source, compare)).toBeFalsy()
})

it('should match a string with a matching regexp', () => {
const source = 'Hello'
const compare = /Hel(l)?o/
expect(client.weakMatch(source, compare)).toBeTruthy()
})

it('should not match a string with a non-matching regexp', () => {
const source = 'Hello'
const compare = /Sal(l)?ut/
expect(client.weakMatch(source, compare)).toBeFalsy()
})
})
})
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
export { WebVaultClient } from './WebVaultClient'
export { CozyUtils } from './CozyUtils'
export { CipherType } from './CipherType'

export {
VaultContext,
VaultProvider,
withVaultClient
} from './components/VaultContext'

export VaultUnlocker from './components/VaultUnlocker'

0 comments on commit 395dc1c

Please sign in to comment.