Skip to content

Commit 5e17ec8

Browse files
bf: ZENKO-835 do not replicate lifecycle actions
All actions coming from lifecycle (or potentially any service account) are not replicated anymore. This applies now to delete markers created by lifecycle expiration rules.
1 parent 6e7f5c4 commit 5e17ec8

File tree

5 files changed

+48
-6
lines changed

5 files changed

+48
-6
lines changed

conf/authdata.json

+11
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,16 @@
4141
"access": "replicationKey1",
4242
"secret": "replicationSecretKey1"
4343
}]
44+
},
45+
{
46+
"name": "Lifecycle",
47+
"email": "[email protected]",
48+
"arn": "arn:aws:iam::123456789016:root",
49+
"canonicalID": "http://acs.zenko.io/accounts/service/lifecycle",
50+
"shortid": "123456789016",
51+
"keys": [{
52+
"access": "lifecycleKey1",
53+
"secret": "lifecycleSecretKey1"
54+
}]
4455
}]
4556
}

lib/api/apiUtils/object/createAndStoreObject.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ function createAndStoreObject(bucketName, bucketMD, objectKey, objMD, authInfo,
107107
size,
108108
headers,
109109
isDeleteMarker,
110-
replicationInfo: getReplicationInfo(objectKey, bucketMD, false, size),
110+
replicationInfo: getReplicationInfo(
111+
objectKey, bucketMD, false, size, null, null, authInfo),
111112
log,
112113
};
113114
if (!isDeleteMarker) {

lib/api/apiUtils/object/getReplicationInfo.js

+16-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const s3config = require('../../../Config').config;
22
const constants = require('../../../../constants');
3+
const { isBackbeatUser } = require('../authorization/aclChecks');
34

45
function _getBackend(objectMD, site) {
56
const backends = objectMD ? objectMD.replicationInfo.backends : [];
@@ -68,14 +69,26 @@ function _getReplicationInfo(rule, replicationConfig, content, operationType,
6869
* @param {boolean} objSize - The size, in bytes, of the object being PUT
6970
* @param {string} operationType - The type of operation to replicate
7071
* @param {object} objectMD - The object metadata
72+
* @param {AuthInfo} [authInfo] - authentication info of object owner
7173
* @return {undefined}
7274
*/
7375
function getReplicationInfo(objKey, bucketMD, isMD, objSize, operationType,
74-
objectMD) {
76+
objectMD, authInfo) {
7577
const content = isMD || objSize === 0 ? ['METADATA'] : ['DATA', 'METADATA'];
7678
const config = bucketMD.getReplicationConfiguration();
77-
// If bucket does not have a replication configuration, do not replicate.
78-
if (config) {
79+
80+
// Do not replicate object in the following cases:
81+
//
82+
// - bucket does not have a replication configuration
83+
//
84+
// - replication configuration does not apply to the object
85+
// (i.e. no rule matches object prefix)
86+
//
87+
// - object owner is an internal service account like Lifecycle
88+
// (because we do not want to replicate objects created from
89+
// actions triggered by internal services, by design)
90+
91+
if (config && (!authInfo || !isBackbeatUser(authInfo.getCanonicalID()))) {
7992
const rule = config.rules.find(rule => objKey.startsWith(rule.prefix));
8093
if (rule) {
8194
return _getReplicationInfo(rule, config, content, operationType,

tests/unit/api/objectReplicationMD.js

+18-2
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ const objectPutTagging = require('../../../lib/api/objectPutTagging');
1919
const objectDeleteTagging = require('../../../lib/api/objectDeleteTagging');
2020

2121
const log = new DummyRequestLogger();
22-
const canonicalID = 'accessKey1';
23-
const authInfo = makeAuthInfo(canonicalID);
22+
const authInfo = makeAuthInfo('accessKey1');
2423
const ownerID = authInfo.getCanonicalID();
24+
const authInfoLifecycleService = makeAuthInfo('lifecycleKey1');
2525
const namespace = 'default';
2626
const bucketName = 'source-bucket';
2727
const mpuShadowBucket = `${constants.mpuBucketPrefix}${bucketName}`;
@@ -381,6 +381,22 @@ describe('Replication object MD without bucket replication config', () => {
381381
return done();
382382
}));
383383

384+
it('should not update metadata if putting a delete marker owned by ' +
385+
'Lifecycle service account', done =>
386+
async.series([
387+
next => putObjectAndCheckMD(keyA, newReplicationMD, next),
388+
next => objectDelete(authInfoLifecycleService, deleteReq,
389+
log, next),
390+
], err => {
391+
if (err) {
392+
return done(err);
393+
}
394+
const objectMD = metadata.keyMaps.get(bucketName).get(keyA);
395+
assert.strictEqual(objectMD.isDeleteMarker, true);
396+
checkObjectReplicationInfo(keyA, emptyReplicationMD);
397+
return done();
398+
}));
399+
384400
describe('Object tagging', () => {
385401
beforeEach(done => async.series([
386402
next => putObjectAndCheckMD(keyA, newReplicationMD, next),

tests/unit/helpers.js

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ function makeAuthInfo(accessKey) {
6969
+ 'cd47ef2be',
7070
accessKey2: '79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7'
7171
+ 'cd47ef2bf',
72+
lifecycleKey1: '0123456789abcdef/lifecycle',
7273
default: crypto.randomBytes(32).toString('hex'),
7374
};
7475
canIdMap[constants.publicId] = constants.publicId;

0 commit comments

Comments
 (0)