Skip to content

Commit

Permalink
feat: Check if extension has been installed before ask for unlock
Browse files Browse the repository at this point in the history
We figured than only checking if some ciphers exist is not a strong
enough condition to know if we should ask the user to unlock the vault
or not.

To resolve our problems, the stack now adds a
`extension_installed` attribute on the `io.cozy.settings.bitwarden`
document when an extension has been connected for the first time to the
instance. See cozy/cozy-stack#2315.

So we now check this attribute. The flow is now:

* Check if there is some cipher, is yes => the user should unlock
* If there is no cipher, check if the extension has been installed, if
  yes => the user should unlock
* Otherwise, skip the unlock step
  • Loading branch information
drazik committed Jan 15, 2020
1 parent 1e36fa9 commit c4b5ba7
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 17 deletions.
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = {
moduleFileExtensions: ['js', 'jsx', 'json'],
setupFilesAfterEnv: ['<rootDir>enzyme.setup.js'],
setupFilesAfterEnv: ['<rootDir>enzyme.setup.js', '<rootDir>jest.setup.js'],
testPathIgnorePatterns: ['@bitwarden/jslib', 'transpiled/']
}
33 changes: 33 additions & 0 deletions jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* eslint-disable no-console */

const ignoreOnConditions = (originalWarn, ignoreConditions) => {
return function(...args) {
const msg = args[0]
if (ignoreConditions.some(condition => condition(msg))) {
return
}
originalWarn.apply(this, args)
}
}

const callAndThrow = (fn, errorMessage) => {
return function() {
fn.apply(this, arguments)
throw new Error(errorMessage)
}
}

const ignoredErrors = {
'mocked error': {
reason: 'Mocked error',
matcher: message => message.includes('mock error')
}
}

console.error = ignoreOnConditions(
callAndThrow(
console.error,
'console.error should not be called during tests'
),
Object.values(ignoredErrors).map(x => x.matcher)
)
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"babel-plugin-mock-imports": "^1.0.2",
"babel-plugin-rewire": "1.2.0",
"babel-preset-cozy-app": "^1.6.0",
"cozy-client": "^8.5.1",
"cozy-client": "^8.8.0",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.14.0",
"enzyme-to-json": "3.4.0",
Expand Down Expand Up @@ -78,6 +78,6 @@
"zxcvbn": "^4.4.2"
},
"peerDependencies": {
"cozy-client": "^8.5.1"
"cozy-client": "^8.8.0"
}
}
9 changes: 6 additions & 3 deletions src/components/VaultUnlocker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import localesEn from '../locales/en.json'
import localesFr from '../locales/fr.json'
import Spinner from 'cozy-ui/transpiled/react/Spinner'
import { withClient } from 'cozy-client'
import { checkHasCiphers } from '../utils'
import { checkHasCiphers, checkHasInstalledExtension } from '../utils'

const locales = {
en: localesEn,
Expand All @@ -27,8 +27,11 @@ const VaultUnlocker = ({

useEffect(() => {
const checkShouldUnlock = async () => {
const hasCiphers = await checkHasCiphers(cozyClient)
const shouldUnlock = hasCiphers
let shouldUnlock = await checkHasCiphers(cozyClient)

if (!shouldUnlock) {
shouldUnlock = await checkHasInstalledExtension(cozyClient)
}

setShouldUnlock(shouldUnlock)
setIsChecking(false)
Expand Down
30 changes: 28 additions & 2 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const CIPHERS_DOCTYPE = 'com.bitwarden.ciphers'
const SETTINGS_DOCTYPE = 'io.cozy.settings'

const isForbiddenError = rawError => {
return rawError.message.match(/code=403/)
Expand All @@ -13,13 +14,38 @@ export const checkHasCiphers = async cozyClient => {
return ciphers.length > 0
} catch (err) {
/* eslint-disable no-console */
console.error('Error while fetching ciphers:')
if (isForbiddenError(err)) {
console.error(
`Your app must have the GET permission on the ${CIPHERS_DOCTYPE} doctype.`
)
} else {
console.error(err)
console.error(err.message)
}
/* eslint-enable no-console */

return false
}
}

export const checkHasInstalledExtension = async cozyClient => {
try {
const { rows: docs } = await cozyClient
.getStackClient()
.fetchJSON('GET', `/data/${SETTINGS_DOCTYPE}/_normal_docs`)

const [bitwardenSettings] = docs.filter(
doc => doc._id === 'io.cozy.settings.bitwarden'
)

return bitwardenSettings && bitwardenSettings.extension_installed
} catch (err) {
/* eslint-disable no-console */
if (isForbiddenError(err)) {
console.error(
`Your app must have the GET permission on the ${SETTINGS_DOCTYPE} doctype.`
)
} else {
console.error(err.message)
}
/* eslint-enable no-console */

Expand Down
110 changes: 110 additions & 0 deletions src/utils.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { checkHasCiphers, checkHasInstalledExtension } from './utils'
import { createMockClient } from 'cozy-client/dist/mock'

describe('checkHasCiphers', () => {
describe('when there are ciphers', () => {
let client

beforeEach(() => {
client = createMockClient({
remote: {
'com.bitwarden.ciphers': [{ _id: 'cipher1' }, { _id: 'cipher2' }]
}
})
})

it('should return true', async () => {
const hasCiphers = await checkHasCiphers(client)

expect(hasCiphers).toBe(true)
})
})

describe('when there is no cipher', () => {
let client

beforeEach(() => {
client = createMockClient({
remote: {
'com.bitwarden.ciphers': []
}
})
})

it('should return false', async () => {
const hasCiphers = await checkHasCiphers(client)

expect(hasCiphers).toBe(false)
})
})

describe('when there is an error while fetching ciphers', () => {
let client

beforeEach(() => {
client = createMockClient({})
client.query = jest.fn().mockRejectedValue({ message: 'mock error' })
})

it('should return false', async () => {
const hasCiphers = await checkHasCiphers(client)

expect(hasCiphers).toBe(false)
})
})
})

describe('checkHasInstalledExtension', () => {
describe('when the extension has been installed', () => {
let client

beforeEach(() => {
client = createMockClient({})
client.stackClient.fetchJSON = jest.fn().mockResolvedValue({
rows: [{ _id: 'io.cozy.settings.bitwarden', extension_installed: true }]
})
})

it('should return true', async () => {
const hasInstalledExtension = await checkHasInstalledExtension(client)

expect(hasInstalledExtension).toBe(true)
})
})

describe('when the extension has not been installed', () => {
let client

beforeEach(() => {
client = createMockClient({})
client.stackClient.fetchJSON = jest.fn().mockResolvedValue({
rows: [
{ _id: 'io.cozy.settings.bitwarden', extension_installed: false }
]
})
})

it('should return false', async () => {
const hasInstalledExtension = await checkHasInstalledExtension(client)

expect(hasInstalledExtension).toBe(false)
})
})

describe('when there is an error while fetching settings', () => {
let client

beforeEach(() => {
client = createMockClient({})
client.stackClient.fetchJSON = jest
.fn()
.mockRejectedValue({ message: 'mock error' })
})

it('should return false', async () => {
const hasInstalledExtension = await checkHasInstalledExtension(client)

expect(hasInstalledExtension).toBe(false)
})
})
})
18 changes: 9 additions & 9 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2735,13 +2735,13 @@ cosmiconfig@^5.0.1:
js-yaml "^3.13.1"
parse-json "^4.0.0"

cozy-client@^8.5.1:
version "8.5.1"
resolved "https://registry.yarnpkg.com/cozy-client/-/cozy-client-8.5.1.tgz#7303302a3f0241b965798c1a7239c3697f659b46"
integrity sha512-Ds+U2sGkQjK8nslcCIlysK/RAhCkjNX7jZdpun8KIZCRoryvmDYWND9Xprd+cB+MN4kueLPIZdasTHoFF4i38g==
cozy-client@^8.8.0:
version "8.10.1"
resolved "https://registry.yarnpkg.com/cozy-client/-/cozy-client-8.10.1.tgz#d1663c915141274f5cf413c12f31e2cc936a963f"
integrity sha512-VYEx4GGfcSpL3MKcFcxV//PsklFDB3azwDJIIBmPmamLVY+1Axgwc8uTeweIZSetbtPZHXMQyogQ4qf9vq80fg==
dependencies:
cozy-device-helper "^1.7.3"
cozy-stack-client "^8.5.1"
cozy-stack-client "^8.8.0"
lodash "^4.17.13"
microee "^0.0.6"
prop-types "^15.6.2"
Expand All @@ -2765,10 +2765,10 @@ cozy-device-helper@^1.7.5:
dependencies:
lodash "4.17.15"

cozy-stack-client@^8.5.1:
version "8.5.1"
resolved "https://registry.yarnpkg.com/cozy-stack-client/-/cozy-stack-client-8.5.1.tgz#a3f88eee07d168a0eafe70bc68f9333ed34c71a3"
integrity sha512-SZB1hma9Zh50FeTiCl9bFneUy2XNwo++W6x9zd70tfNsMlXt504iDaV8MCew2lvuVszK+HFzsq+hyByB0m2VlA==
cozy-stack-client@^8.8.0:
version "8.8.0"
resolved "https://registry.yarnpkg.com/cozy-stack-client/-/cozy-stack-client-8.8.0.tgz#0bfbfcc3180c1eb6b1468a3d14b965b05c84c84d"
integrity sha512-Ltc/OjDN9EdlMioSsQM0zCw7lb+LZisjU0XeOaTK+1jH1AdWBdywdu1uUrxCSF/rStsqfBqleNFwPK5WySvPTw==
dependencies:
detect-node "^2.0.4"
mime "^2.4.0"
Expand Down

0 comments on commit c4b5ba7

Please sign in to comment.