cozy-client 27.16.0 → 27.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2464,7 +2464,7 @@ var CozyClient = /*#__PURE__*/function () {
2464
2464
  CozyClient.hooks = CozyClient.hooks || {};
2465
2465
  CozyClient.fetchPolicies = _policies.default; //COZY_CLIENT_VERSION_PACKAGE in replaced by babel. See babel config
2466
2466
 
2467
- CozyClient.version = "27.16.0";
2467
+ CozyClient.version = "27.18.0";
2468
2468
 
2469
2469
  _microee.default.mixin(CozyClient);
2470
2470
 
@@ -13,6 +13,8 @@ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/
13
13
 
14
14
  var _CozyClient = _interopRequireDefault(require("../CozyClient"));
15
15
 
16
+ var _logger = _interopRequireDefault(require("../logger"));
17
+
16
18
  var _storeAttestation = require("./store-attestation");
17
19
 
18
20
  /**
@@ -134,25 +136,38 @@ var certifyFlagship = /*#__PURE__*/function () {
134
136
  throw new Error('[FLAGSHIP_CERTIFICATION] Certification configuration is not set');
135
137
 
136
138
  case 2:
137
- _context3.next = 4;
139
+ _context3.prev = 2;
140
+ _context3.next = 5;
138
141
  return getStackChallenge(client);
139
142
 
140
- case 4:
143
+ case 5:
141
144
  stackChallengeNonce = _context3.sent;
142
- _context3.next = 7;
145
+ _context3.next = 8;
143
146
  return (0, _storeAttestation.getAppAttestationFromStore)(stackChallengeNonce, certificationConfig);
144
147
 
145
- case 7:
148
+ case 8:
146
149
  appAttestation = _context3.sent;
147
- _context3.next = 10;
150
+ _context3.next = 11;
148
151
  return giveAppAttestationToStack(appAttestation, stackChallengeNonce, client);
149
152
 
150
- case 10:
153
+ case 11:
154
+ _context3.next = 17;
155
+ break;
156
+
157
+ case 13:
158
+ _context3.prev = 13;
159
+ _context3.t0 = _context3["catch"](2);
160
+
161
+ _logger.default.warn('[FLAGSHIP_CERTIFICATION] Certification failed but the cozy-stack will continue with 2FA certification');
162
+
163
+ _logger.default.warn(_context3.t0.message);
164
+
165
+ case 17:
151
166
  case "end":
152
167
  return _context3.stop();
153
168
  }
154
169
  }
155
- }, _callee3);
170
+ }, _callee3, null, [[2, 13]]);
156
171
  }));
157
172
 
158
173
  return function certifyFlagship(_x5, _x6) {
@@ -0,0 +1,215 @@
1
+ import { certifyFlagship } from './flagship-certification'
2
+ import { createMockClient } from '../mock'
3
+
4
+ import { getAppAttestationFromStore } from './store-attestation'
5
+ import logger from '../logger'
6
+
7
+ jest.mock('./store-attestation', () => ({
8
+ getAppAttestationFromStore: jest.fn()
9
+ }))
10
+
11
+ jest.mock('../logger', () => ({
12
+ warn: jest.fn()
13
+ }))
14
+
15
+ const getClientMock = () => {
16
+ const client = createMockClient({
17
+ clientOptions: {
18
+ oauth: {}
19
+ }
20
+ })
21
+
22
+ client.stackClient.oauthOptions = {
23
+ clientID: 'SOME_CLIENT_ID',
24
+ registrationAccessToken: 'SOME_REGISTRATION_ACCESS_TOKEN'
25
+ }
26
+
27
+ return client
28
+ }
29
+
30
+ const mockCorrectChallengeRequest = client => {
31
+ client.stackClient.fetchJSON.mockResolvedValueOnce({
32
+ nonce: 'SOME_NONCE'
33
+ })
34
+ }
35
+
36
+ const mockCorrectStoreApiRequest = () => {
37
+ getAppAttestationFromStore.mockResolvedValueOnce({
38
+ platform: 'android',
39
+ attestation: 'SOME_STORE_ATTESTATION',
40
+ keyId: 'SOME_KEY_ID'
41
+ })
42
+ }
43
+
44
+ const mockCorrectCertificationConfig = () => {
45
+ return {
46
+ androidSafetyNetApiKey: 'SOME_ANDROID_SAFETY_NET_API_KEY'
47
+ }
48
+ }
49
+
50
+ const getFetchJsonPostParams = (url, body = null) => {
51
+ return [
52
+ 'POST',
53
+ url,
54
+ body,
55
+ {
56
+ headers: {
57
+ Authorization: 'Bearer SOME_REGISTRATION_ACCESS_TOKEN'
58
+ }
59
+ }
60
+ ]
61
+ }
62
+
63
+ describe('certifyFlagship', () => {
64
+ afterEach(() => {
65
+ jest.restoreAllMocks()
66
+ getAppAttestationFromStore.mockReset()
67
+ })
68
+
69
+ it('should ask challenge to cozy-stack, call the store API and then send the result to the stack', async () => {
70
+ const client = getClientMock()
71
+
72
+ const certificationConfig = mockCorrectCertificationConfig()
73
+
74
+ mockCorrectChallengeRequest(client)
75
+
76
+ mockCorrectStoreApiRequest()
77
+
78
+ await certifyFlagship(certificationConfig, client)
79
+
80
+ expect(client.stackClient.fetchJSON).toHaveBeenCalledWith(
81
+ ...getFetchJsonPostParams(`/auth/clients/SOME_CLIENT_ID/challenge`)
82
+ )
83
+
84
+ expect(getAppAttestationFromStore).toHaveBeenCalledWith('SOME_NONCE', {
85
+ androidSafetyNetApiKey: 'SOME_ANDROID_SAFETY_NET_API_KEY'
86
+ })
87
+
88
+ expect(client.stackClient.fetchJSON).toHaveBeenCalledWith(
89
+ ...getFetchJsonPostParams(`/auth/clients/SOME_CLIENT_ID/attestation`, {
90
+ platform: 'android',
91
+ attestation: 'SOME_STORE_ATTESTATION',
92
+ challenge: 'SOME_NONCE',
93
+ keyId: 'SOME_KEY_ID'
94
+ })
95
+ )
96
+ })
97
+
98
+ it('should throw if no certification config is provided', async () => {
99
+ const client = getClientMock()
100
+
101
+ const certificationConfig = undefined
102
+
103
+ mockCorrectChallengeRequest(client)
104
+
105
+ client.stackClient.fetchJSON.mockImplementationOnce(() => {
106
+ throw Error('SOME_STACK_CERTIFICATION_ERROR')
107
+ })
108
+
109
+ mockCorrectStoreApiRequest()
110
+
111
+ await expect(certifyFlagship(certificationConfig, client)).rejects.toEqual(
112
+ new Error(
113
+ '[FLAGSHIP_CERTIFICATION] Certification configuration is not set'
114
+ )
115
+ )
116
+
117
+ expect(client.stackClient.fetchJSON).not.toHaveBeenCalled()
118
+ expect(getAppAttestationFromStore).not.toHaveBeenCalled()
119
+ })
120
+
121
+ it('should ask challenge to cozy-stack and then handle challenge query failure', async () => {
122
+ const client = getClientMock()
123
+
124
+ const certificationConfig = mockCorrectCertificationConfig()
125
+
126
+ // Mock errored stack challenge request
127
+ client.stackClient.fetchJSON.mockImplementation(() => {
128
+ throw Error('SOME_STACK_CHALLENGE_ERROR')
129
+ })
130
+
131
+ mockCorrectStoreApiRequest()
132
+
133
+ await certifyFlagship(certificationConfig, client)
134
+
135
+ expect(client.stackClient.fetchJSON).toHaveBeenCalledWith(
136
+ ...getFetchJsonPostParams(`/auth/clients/SOME_CLIENT_ID/challenge`)
137
+ )
138
+
139
+ expect(logger.warn).toHaveBeenCalledWith(
140
+ '[FLAGSHIP_CERTIFICATION] Certification failed but the cozy-stack will continue with 2FA certification'
141
+ )
142
+ expect(logger.warn).toHaveBeenCalledWith(
143
+ '[FLAGSHIP_CERTIFICATION] Something went wrong while requesting a challenge from CozyStack:\nSOME_STACK_CHALLENGE_ERROR'
144
+ )
145
+ })
146
+
147
+ it('should ask challenge to cozy-stack, call the store API and then handle store API failure', async () => {
148
+ const client = getClientMock()
149
+
150
+ const certificationConfig = mockCorrectCertificationConfig()
151
+
152
+ mockCorrectChallengeRequest(client)
153
+
154
+ // Mock errored store API
155
+ getAppAttestationFromStore.mockImplementationOnce(() => {
156
+ throw Error('SOME_STORE_API_ERROR')
157
+ })
158
+
159
+ await certifyFlagship(certificationConfig, client)
160
+
161
+ expect(client.stackClient.fetchJSON).toHaveBeenCalledWith(
162
+ ...getFetchJsonPostParams(`/auth/clients/SOME_CLIENT_ID/challenge`)
163
+ )
164
+
165
+ expect(getAppAttestationFromStore).toHaveBeenCalledWith('SOME_NONCE', {
166
+ androidSafetyNetApiKey: 'SOME_ANDROID_SAFETY_NET_API_KEY'
167
+ })
168
+
169
+ expect(logger.warn).toHaveBeenCalledWith(
170
+ '[FLAGSHIP_CERTIFICATION] Certification failed but the cozy-stack will continue with 2FA certification'
171
+ )
172
+ expect(logger.warn).toHaveBeenCalledWith('SOME_STORE_API_ERROR')
173
+ })
174
+
175
+ it('should ask challenge to cozy-stack, call the store API, send the result to the stack and then handle cozy-stack certification failure', async () => {
176
+ const client = getClientMock()
177
+
178
+ const certificationConfig = mockCorrectCertificationConfig()
179
+
180
+ mockCorrectChallengeRequest(client)
181
+
182
+ // Mock errored stack certification request
183
+ client.stackClient.fetchJSON.mockImplementationOnce(() => {
184
+ throw Error('SOME_STACK_CERTIFICATION_ERROR')
185
+ })
186
+
187
+ mockCorrectStoreApiRequest()
188
+
189
+ await certifyFlagship(certificationConfig, client)
190
+
191
+ expect(client.stackClient.fetchJSON).toHaveBeenCalledWith(
192
+ ...getFetchJsonPostParams(`/auth/clients/SOME_CLIENT_ID/challenge`)
193
+ )
194
+
195
+ expect(getAppAttestationFromStore).toHaveBeenCalledWith('SOME_NONCE', {
196
+ androidSafetyNetApiKey: 'SOME_ANDROID_SAFETY_NET_API_KEY'
197
+ })
198
+
199
+ expect(client.stackClient.fetchJSON).toHaveBeenCalledWith(
200
+ ...getFetchJsonPostParams(`/auth/clients/SOME_CLIENT_ID/attestation`, {
201
+ platform: 'android',
202
+ attestation: 'SOME_STORE_ATTESTATION',
203
+ challenge: 'SOME_NONCE',
204
+ keyId: 'SOME_KEY_ID'
205
+ })
206
+ )
207
+
208
+ expect(logger.warn).toHaveBeenCalledWith(
209
+ '[FLAGSHIP_CERTIFICATION] Certification failed but the cozy-stack will continue with 2FA certification'
210
+ )
211
+ expect(logger.warn).toHaveBeenCalledWith(
212
+ '[FLAGSHIP_CERTIFICATION] Something went wrong while giving attestation to CozyStack:\nSOME_STACK_CERTIFICATION_ERROR'
213
+ )
214
+ })
215
+ })
@@ -25,7 +25,7 @@ var validateAppMock = /*#__PURE__*/function () {
25
25
  while (1) {
26
26
  switch (_context.prev = _context.next) {
27
27
  case 0:
28
- throw new Error("validateApp can only be called from a React Native container");
28
+ throw new Error("getAppAttestationFromStore can only be called from a React Native container");
29
29
 
30
30
  case 1:
31
31
  case "end":
@@ -159,16 +159,16 @@ var getCollectionFromSlice = function getCollectionFromSlice() {
159
159
  return Object.values(state[doctype]);
160
160
  };
161
161
  /*
162
- This method has been created in order to get a returned object
163
- in `data` with the full set on information coming potentially from
164
- ìncluded`
162
+ This method has been created in order to get a returned object
163
+ in `data` with the full set on information coming potentially from
164
+ `included`
165
165
 
166
- This method should be somewhere else. The `document` shall not be
167
- deal with included / data and so on.
166
+ This method should be somewhere else. The `document` shall not be
167
+ dealt with included / data and so on.
168
168
 
169
- This method takes data and included and merge both sources
170
- together. It should be always up to date. The returned object
171
- will be as full of informations as it can be.
169
+ This method takes `data` and `included` and merge both sources
170
+ together. It should be always up-to-date. The returned object
171
+ will be as full of information as it can be.
172
172
  */
173
173
 
174
174
 
@@ -185,8 +185,8 @@ var extractAndMergeDocument = function extractAndMergeDocument(data, updatedStat
185
185
 
186
186
  var sortedData = (0, _keyBy.default)(data, _helpers.properId);
187
187
  var mergedData = Object.assign({}, updatedStateWithIncluded);
188
+ mergedData[doctype] = Object.assign({}, updatedStateWithIncluded[doctype]);
188
189
  Object.values(sortedData).map(function (data) {
189
- if (!mergedData[doctype]) mergedData[doctype] = {};
190
190
  var id = (0, _helpers.properId)(data);
191
191
 
192
192
  if (mergedData[doctype][id]) {
@@ -4,23 +4,25 @@ import {
4
4
  } from './documents'
5
5
 
6
6
  describe('extractAndMerge', () => {
7
- it('should return the right data', () => {
8
- const data = {
9
- 0: {
10
- id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
11
- _id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
12
- type: 'io.cozy.files',
13
- _type: 'io.cozy.files'
14
- },
15
- 1: {
16
- id: 'b6ff135b34e041ffb2d4a4865f3e235f',
17
- type: 'io.cozy.files',
18
- _type: 'io.cozy.files',
19
- _id: 'b6ff135b34e041ffb2d4a4865f3e235f'
20
- }
7
+ const data = {
8
+ 0: {
9
+ id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
10
+ _id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
11
+ type: 'io.cozy.files',
12
+ _type: 'io.cozy.files',
13
+ blabla: 'new field'
14
+ },
15
+ 1: {
16
+ id: 'b6ff135b34e041ffb2d4a4865f3e235f',
17
+ type: 'io.cozy.files',
18
+ _type: 'io.cozy.files',
19
+ _id: 'b6ff135b34e041ffb2d4a4865f3e235f',
20
+ blibli: 'another new field'
21
21
  }
22
- const updatedStateWithIncluded = {
23
- 0: {
22
+ }
23
+ const updatedStateWithIncluded = {
24
+ 'io.cozy.files': {
25
+ b6ff135b34e041ffb2d4a4865f3e0a53: {
24
26
  attributes: {
25
27
  type: 'file',
26
28
  name: 'IMG_0016.PNG',
@@ -38,7 +40,7 @@ describe('extractAndMerge', () => {
38
40
  type: 'io.cozy.files',
39
41
  _type: 'io.cozy.files'
40
42
  },
41
- 1: {
43
+ b6ff135b34e041ffb2d4a4865f3e235f: {
42
44
  attributes: {
43
45
  type: 'file',
44
46
  name: 'IMG_0054.PNG',
@@ -57,65 +59,112 @@ describe('extractAndMerge', () => {
57
59
  _type: 'io.cozy.files'
58
60
  }
59
61
  }
62
+ }
63
+
64
+ it('should return the right data', () => {
60
65
  const returnedDatas = extractAndMergeDocument(
61
66
  data,
62
67
  updatedStateWithIncluded
63
68
  )
64
69
 
65
70
  const manuallyMergedData = {
66
- '0': {
67
- _id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
68
- _type: 'io.cozy.files',
69
- attributes: {
70
- dir_id: '7d2f9c24cd345ce3171bf71f401e80c6',
71
- name: 'IMG_0016.PNG',
72
- type: 'file'
73
- },
74
- id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
75
- links: { self: '/files/b6ff135b34e041ffb2d4a4865f3e0a53' },
76
- meta: { rev: '5-87840eceaab358e38aa8b6bb0d4577b1' },
77
- relationships: {
78
- parent: {
79
- links: { related: '/files/7d2f9c24cd345ce3171bf71f401e80c6' }
80
- }
81
- },
82
- type: 'io.cozy.files'
83
- },
84
- '1': {
85
- _id: 'b6ff135b34e041ffb2d4a4865f3e235f',
86
- _type: 'io.cozy.files',
87
- attributes: {
88
- dir_id: '7d2f9c24cd345ce3171bf71f401e80c6',
89
- name: 'IMG_0054.PNG',
90
- type: 'file'
91
- },
92
- id: 'b6ff135b34e041ffb2d4a4865f3e235f',
93
- links: { self: '/files/b6ff135b34e041ffb2d4a4865f3e235f' },
94
- meta: { rev: '5-ca91c0dc02dafb38eb56070c1d80d62c' },
95
- relationships: {
96
- parent: {
97
- links: { related: '/files/7d2f9c24cd345ce3171bf71f401e80c6' }
98
- }
99
- },
100
- type: 'io.cozy.files'
101
- },
102
71
  'io.cozy.files': {
103
72
  b6ff135b34e041ffb2d4a4865f3e0a53: {
104
73
  _id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
105
74
  _type: 'io.cozy.files',
75
+ attributes: {
76
+ dir_id: '7d2f9c24cd345ce3171bf71f401e80c6',
77
+ name: 'IMG_0016.PNG',
78
+ type: 'file'
79
+ },
80
+ blabla: 'new field',
106
81
  id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
82
+ links: { self: '/files/b6ff135b34e041ffb2d4a4865f3e0a53' },
83
+ meta: { rev: '5-87840eceaab358e38aa8b6bb0d4577b1' },
84
+ relationships: {
85
+ parent: {
86
+ links: { related: '/files/7d2f9c24cd345ce3171bf71f401e80c6' }
87
+ }
88
+ },
107
89
  type: 'io.cozy.files'
108
90
  },
109
91
  b6ff135b34e041ffb2d4a4865f3e235f: {
110
92
  _id: 'b6ff135b34e041ffb2d4a4865f3e235f',
111
93
  _type: 'io.cozy.files',
94
+ attributes: {
95
+ dir_id: '7d2f9c24cd345ce3171bf71f401e80c6',
96
+ name: 'IMG_0054.PNG',
97
+ type: 'file'
98
+ },
99
+ blibli: 'another new field',
112
100
  id: 'b6ff135b34e041ffb2d4a4865f3e235f',
101
+ links: { self: '/files/b6ff135b34e041ffb2d4a4865f3e235f' },
102
+ meta: { rev: '5-ca91c0dc02dafb38eb56070c1d80d62c' },
103
+ relationships: {
104
+ parent: {
105
+ links: { related: '/files/7d2f9c24cd345ce3171bf71f401e80c6' }
106
+ }
107
+ },
113
108
  type: 'io.cozy.files'
114
109
  }
115
110
  }
116
111
  }
117
112
  expect(returnedDatas).toEqual(manuallyMergedData)
118
113
  })
114
+
115
+ it(
116
+ 'should assign a new object for doctype of document to update' +
117
+ 'in order to allow selector to recompute their data when documents are updated',
118
+ () => {
119
+ const returnedDatas = extractAndMergeDocument(
120
+ data,
121
+ updatedStateWithIncluded
122
+ )
123
+
124
+ // Then
125
+ expect(
126
+ returnedDatas['io.cozy.files'] ===
127
+ updatedStateWithIncluded['io.cozy.files']
128
+ ).toBeFalsy()
129
+ }
130
+ )
131
+
132
+ it(
133
+ 'should assign a new object for doctype of document to update' +
134
+ 'even if the mergedData is the same, because reselect createSelector update ' +
135
+ 'according to object reference, not only value',
136
+ () => {
137
+ const dataAlreadyIncludedInUpdatedStateWithIncluded = {
138
+ 0: {
139
+ id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
140
+ _id: 'b6ff135b34e041ffb2d4a4865f3e0a53',
141
+ type: 'io.cozy.files',
142
+ _type: 'io.cozy.files'
143
+ },
144
+ 1: {
145
+ id: 'b6ff135b34e041ffb2d4a4865f3e235f',
146
+ type: 'io.cozy.files',
147
+ _type: 'io.cozy.files',
148
+ _id: 'b6ff135b34e041ffb2d4a4865f3e235f'
149
+ }
150
+ }
151
+
152
+ const returnedDatas = extractAndMergeDocument(
153
+ dataAlreadyIncludedInUpdatedStateWithIncluded,
154
+ updatedStateWithIncluded
155
+ )
156
+
157
+ // Then
158
+ expect(
159
+ returnedDatas['io.cozy.files'] ===
160
+ updatedStateWithIncluded['io.cozy.files']
161
+ ).toBeFalsy()
162
+
163
+ expect(returnedDatas['io.cozy.files']).toEqual(
164
+ updatedStateWithIncluded['io.cozy.files']
165
+ )
166
+ }
167
+ )
119
168
  })
120
169
 
121
170
  describe('mergeDocumentsWithRelationships', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-client",
3
- "version": "27.16.0",
3
+ "version": "27.18.0",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.node.js",
6
6
  "browser": "dist/index.js",
@@ -21,7 +21,7 @@
21
21
  "cozy-device-helper": "^1.12.0",
22
22
  "cozy-flags": "2.7.1",
23
23
  "cozy-logger": "^1.6.0",
24
- "cozy-stack-client": "^27.15.0",
24
+ "cozy-stack-client": "^27.17.0",
25
25
  "json-stable-stringify": "^1.0.1",
26
26
  "lodash": "^4.17.13",
27
27
  "microee": "^0.0.6",
@@ -71,5 +71,5 @@
71
71
  "react-native-ios11-devicecheck": "https://github.com/cozy/react-native-devicecheck#app-attest-v0.1"
72
72
  },
73
73
  "sideEffects": false,
74
- "gitHead": "b52a6830a34c3a13bee265132f527c0b30d580a5"
74
+ "gitHead": "df36e3ca46da4adab20629f869296d20942fbc75"
75
75
  }