diff --git a/imports/personal-access-tokens/client/personalAccessTokens.js b/imports/personal-access-tokens/client/personalAccessTokens.js index 348bc6ba9a8c..98260fe5ef81 100644 --- a/imports/personal-access-tokens/client/personalAccessTokens.js +++ b/imports/personal-access-tokens/client/personalAccessTokens.js @@ -12,7 +12,7 @@ const PersonalAccessTokens = new Mongo.Collection('personal_access_tokens'); Template.accountTokens.helpers({ isAllowed() { - return RocketChat.settings.get('API_Enable_Personal_Access_Tokens'); + return RocketChat.authz.hasAllPermission(['create-personal-access-tokens']); }, tokens() { return (PersonalAccessTokens.find({}).fetch()[0] && PersonalAccessTokens.find({}).fetch()[0].tokens) || []; @@ -46,7 +46,7 @@ Template.accountTokens.events({ return toastr.error(t(error.error)); } showSuccessModal(token); - instance.find('#input-token-name').value = ''; + instance.find('#tokenName').value = ''; }); }, 'click .remove-personal-access-token'() { diff --git a/imports/personal-access-tokens/server/api/methods/generateToken.js b/imports/personal-access-tokens/server/api/methods/generateToken.js index 68f337e357c5..ff6ca2f764c6 100644 --- a/imports/personal-access-tokens/server/api/methods/generateToken.js +++ b/imports/personal-access-tokens/server/api/methods/generateToken.js @@ -7,8 +7,8 @@ Meteor.methods({ if (!Meteor.userId()) { throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'personalAccessTokens:generateToken' }); } - if (!RocketChat.settings.get('API_Enable_Personal_Access_Tokens')) { - throw new Meteor.Error('error-personal-access-tokens-are-current-disabled', 'Personal Access Tokens are currently disabled', { method: 'personalAccessTokens:generateToken' }); + if (!RocketChat.authz.hasPermission(Meteor.userId(), 'create-personal-access-tokens')) { + throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'personalAccessTokens:generateToken' }); } const token = Random.secret(); diff --git a/imports/personal-access-tokens/server/api/methods/regenerateToken.js b/imports/personal-access-tokens/server/api/methods/regenerateToken.js index 6b36f9adf25c..48f030c895f8 100644 --- a/imports/personal-access-tokens/server/api/methods/regenerateToken.js +++ b/imports/personal-access-tokens/server/api/methods/regenerateToken.js @@ -5,8 +5,8 @@ Meteor.methods({ if (!Meteor.userId()) { throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'personalAccessTokens:regenerateToken' }); } - if (!RocketChat.settings.get('API_Enable_Personal_Access_Tokens')) { - throw new Meteor.Error('error-personal-access-tokens-are-current-disabled', 'Personal Access Tokens are currently disabled', { method: 'personalAccessTokens:regenerateToken' }); + if (!RocketChat.authz.hasPermission(Meteor.userId(), 'create-personal-access-tokens')) { + throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'personalAccessTokens:regenerateToken' }); } const tokenExist = RocketChat.models.Users.findPersonalAccessTokenByTokenNameAndUserId({ diff --git a/imports/personal-access-tokens/server/api/methods/removeToken.js b/imports/personal-access-tokens/server/api/methods/removeToken.js index ff7be8de698c..3e94195132d2 100644 --- a/imports/personal-access-tokens/server/api/methods/removeToken.js +++ b/imports/personal-access-tokens/server/api/methods/removeToken.js @@ -5,8 +5,8 @@ Meteor.methods({ if (!Meteor.userId()) { throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'personalAccessTokens:removeToken' }); } - if (!RocketChat.settings.get('API_Enable_Personal_Access_Tokens')) { - throw new Meteor.Error('error-personal-access-tokens-are-current-disabled', 'Personal Access Tokens are currently disabled', { method: 'personalAccessTokens:removeToken' }); + if (!RocketChat.authz.hasPermission(Meteor.userId(), 'create-personal-access-tokens')) { + throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'personalAccessTokens:removeToken' }); } const tokenExist = RocketChat.models.Users.findPersonalAccessTokenByTokenNameAndUserId({ userId: Meteor.userId(), diff --git a/imports/personal-access-tokens/server/index.js b/imports/personal-access-tokens/server/index.js index ca18192c8e77..861bc4add4a6 100644 --- a/imports/personal-access-tokens/server/index.js +++ b/imports/personal-access-tokens/server/index.js @@ -1,5 +1,4 @@ import './api/methods'; -import './settings'; import './models'; import './publications'; diff --git a/imports/personal-access-tokens/server/publications/personalAccessTokens.js b/imports/personal-access-tokens/server/publications/personalAccessTokens.js index bdc75b9a9ecf..3760a85f7401 100644 --- a/imports/personal-access-tokens/server/publications/personalAccessTokens.js +++ b/imports/personal-access-tokens/server/publications/personalAccessTokens.js @@ -4,7 +4,7 @@ Meteor.publish('personalAccessTokens', function() { if (!this.userId) { return this.ready(); } - if (!RocketChat.settings.get('API_Enable_Personal_Access_Tokens')) { + if (!RocketChat.authz.hasPermission(this.userId, 'create-personal-access-tokens')) { return this.ready(); } const self = this; diff --git a/imports/personal-access-tokens/server/settings.js b/imports/personal-access-tokens/server/settings.js deleted file mode 100644 index fa2aba8cfb24..000000000000 --- a/imports/personal-access-tokens/server/settings.js +++ /dev/null @@ -1,5 +0,0 @@ -RocketChat.settings.addGroup('General', function() { - this.section('REST API', function() { - this.add('API_Enable_Personal_Access_Tokens', false, { type: 'boolean', public: true }); - }); -}); diff --git a/packages/rocketchat-api/server/v1/users.js b/packages/rocketchat-api/server/v1/users.js index c9476c34f3aa..d1e96b8c1f26 100644 --- a/packages/rocketchat-api/server/v1/users.js +++ b/packages/rocketchat-api/server/v1/users.js @@ -498,8 +498,8 @@ RocketChat.API.v1.addRoute('users.regeneratePersonalAccessToken', { authRequired RocketChat.API.v1.addRoute('users.getPersonalAccessTokens', { authRequired: true }, { get() { - if (!RocketChat.settings.get('API_Enable_Personal_Access_Tokens')) { - throw new Meteor.Error('error-personal-access-tokens-are-current-disabled', 'Personal Access Tokens are currently disabled'); + if (!RocketChat.authz.hasPermission(this.userId, 'create-personal-access-tokens')) { + throw new Meteor.Error('not-authorized', 'Not Authorized'); } const loginTokens = RocketChat.models.Users.getLoginTokensByUserId(this.userId).fetch()[0]; const getPersonalAccessTokens = () => loginTokens.services.resume.loginTokens diff --git a/packages/rocketchat-authorization/server/startup.js b/packages/rocketchat-authorization/server/startup.js index 9da43268023c..9827343848ce 100644 --- a/packages/rocketchat-authorization/server/startup.js +++ b/packages/rocketchat-authorization/server/startup.js @@ -21,6 +21,7 @@ Meteor.startup(function() { { _id: 'create-c', roles : ['admin', 'user', 'bot'] }, { _id: 'create-d', roles : ['admin', 'user', 'bot'] }, { _id: 'create-p', roles : ['admin', 'user', 'bot'] }, + { _id: 'create-personal-access-tokens', roles : ['admin', 'user'] }, { _id: 'create-user', roles : ['admin'] }, { _id: 'clean-channel-history', roles : ['admin'] }, { _id: 'delete-c', roles : ['admin', 'owner'] }, diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 9a1e4b2e476c..aa8730d4aeab 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -278,8 +278,6 @@ "API_Enable_CORS": "Enable CORS", "API_Enable_Direct_Message_History_EndPoint": "Enable Direct Message History Endpoint", "API_Enable_Direct_Message_History_EndPoint_Description": "This enables the `/api/v1/im.history.others` which allows the viewing of direct messages sent by other users that the caller is not part of.", - "API_Enable_Personal_Access_Tokens": "Enable Personal Access Tokens to REST API", - "API_Enable_Personal_Access_Tokens_Description": "Enable personal access tokens for use with the REST API", "API_Enable_Shields": "Enable Shields", "API_Enable_Shields_Description": "Enable shields available at `/api/v1/shield.svg`", "API_GitHub_Enterprise_URL": "Server URL", diff --git a/packages/rocketchat-ui-account/client/accountFlex.js b/packages/rocketchat-ui-account/client/accountFlex.js index 8d38234ebf28..f1c8d945636f 100644 --- a/packages/rocketchat-ui-account/client/accountFlex.js +++ b/packages/rocketchat-ui-account/client/accountFlex.js @@ -15,7 +15,7 @@ Template.accountFlex.helpers({ return RocketChat.settings.get('Accounts_AllowUserProfileChange'); }, accessTokensEnabled() { - return RocketChat.settings.get('API_Enable_Personal_Access_Tokens'); + return RocketChat.authz.hasAllPermission(['create-personal-access-tokens']); }, encryptionEnabled() { return RocketChat.settings.get('E2E_Enable'); diff --git a/server/startup/migrations/v136.js b/server/startup/migrations/v136.js new file mode 100644 index 000000000000..41f2e2dc4d60 --- /dev/null +++ b/server/startup/migrations/v136.js @@ -0,0 +1,15 @@ +RocketChat.Migrations.add({ + version: 136, + up() { + const personalTokensEnabled = RocketChat.settings.get('API_Enable_Personal_Access_Tokens'); + const roles = RocketChat.models.Roles.find({ scope: 'Users' }).fetch().map((role) => role._id); + + if (personalTokensEnabled) { + RocketChat.models.Permissions.upsert({ _id: 'create-personal-access-tokens' }, { $set: { roles } }); + } + + RocketChat.models.Settings.remove({ + _id: 'API_Enable_Personal_Access_Tokens', + }); + }, +}); diff --git a/tests/end-to-end/api/01-users.js b/tests/end-to-end/api/01-users.js index 692e0605d64a..28bbb9e42471 100644 --- a/tests/end-to-end/api/01-users.js +++ b/tests/end-to-end/api/01-users.js @@ -1247,17 +1247,7 @@ describe('[Users]', function() { describe('Personal Access Tokens', () => { const tokenName = `${ Date.now() }token`; describe('successful cases', () => { - it('Enable "API_Enable_Personal_Access_Tokens" setting...', (done) => { - request.post('/api/v1/settings/API_Enable_Personal_Access_Tokens') - .set(credentials) - .send({ value: true }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end(done); - }); + it('Grant necessary permission "create-personal-accss-tokens" to user', (done) => updatePermission('create-personal-access-tokens', ['admin']).then(done)); describe('[/users.generatePersonalAccessToken]', () => { it('should return a personal access token to user', (done) => { request.post(api('users.generatePersonalAccessToken')) @@ -1359,18 +1349,8 @@ describe('[Users]', function() { }); }); describe('unsuccessful cases', () => { - it('disable "API_Enable_Personal_Access_Tokens" setting...', (done) => { - request.post('/api/v1/settings/API_Enable_Personal_Access_Tokens') - .set(credentials) - .send({ value: false }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - }) - .end(done); - }); - describe('should return an error when setting "API_Enable_Personal_Access_Tokens" is disabled for the following routes', () => { + it('Remove necessary permission "create-personal-accss-tokens" to user', (done) => updatePermission('create-personal-access-tokens', []).then(done)); + describe('should return an error when the user dont have the necessary permission "create-personal-access-tokens"', () => { it('/users.generatePersonalAccessToken', (done) => { request.post(api('users.generatePersonalAccessToken')) .set(credentials) @@ -1381,7 +1361,7 @@ describe('[Users]', function() { .expect(400) .expect((res) => { expect(res.body).to.have.property('success', false); - expect(res.body.errorType).to.be.equal('error-personal-access-tokens-are-current-disabled'); + expect(res.body.errorType).to.be.equal('not-authorized'); }) .end(done); }); @@ -1395,7 +1375,7 @@ describe('[Users]', function() { .expect(400) .expect((res) => { expect(res.body).to.have.property('success', false); - expect(res.body.errorType).to.be.equal('error-personal-access-tokens-are-current-disabled'); + expect(res.body.errorType).to.be.equal('not-authorized'); }) .end(done); }); @@ -1406,7 +1386,7 @@ describe('[Users]', function() { .expect(400) .expect((res) => { expect(res.body).to.have.property('success', false); - expect(res.body.errorType).to.be.equal('error-personal-access-tokens-are-current-disabled'); + expect(res.body.errorType).to.be.equal('not-authorized'); }) .end(done); }); @@ -1420,7 +1400,7 @@ describe('[Users]', function() { .expect(400) .expect((res) => { expect(res.body).to.have.property('success', false); - expect(res.body.errorType).to.be.equal('error-personal-access-tokens-are-current-disabled'); + expect(res.body.errorType).to.be.equal('not-authorized'); }) .end(done); }); @@ -1434,7 +1414,7 @@ describe('[Users]', function() { .expect(400) .expect((res) => { expect(res.body).to.have.property('success', false); - expect(res.body.errorType).to.be.equal('error-personal-access-tokens-are-current-disabled'); + expect(res.body.errorType).to.be.equal('not-authorized'); }) .end(done); });