diff --git a/cypress/integration/add-forge-moderation.spec.js b/cypress/integration/add-forge-moderation.spec.js new file mode 100644 --- /dev/null +++ b/cypress/integration/add-forge-moderation.spec.js @@ -0,0 +1,284 @@ +/** + * Copyright (C) 2022 The Software Heritage developers + * See the AUTHORS file at the top-level directory of this distribution + * License: GNU Affero General Public License version 3, or any later version + * See top-level LICENSE file for more information + */ + +const defaultRedirect = '/admin/login/'; + +let url; + +function logout() { + cy.contains('a', 'logout') + .click(); +} + +describe('Test moderation Login/logout', function() { + before(function() { + url = this.Urls.moderation_forge_add(); + }); + + it('should redirect to default page', function() { + cy.visit(url) + .get('input[name="username"]') + .type('admin') + .get('input[name="password"]') + .type('admin') + .get('.container form') + .submit(); + + cy.location('pathname') + .should('be.equal', defaultRedirect); + + logout(); + }); + + it('should redirect to correct page after login', function() { + // mock calls to deposit list api to avoid possible errors + // while running the test + cy.intercept(`${this.Urls.api_1_add_forge_request_list()}**`, { + body: { + data: [], + recordsTotal: 0, + recordsFiltered: 0, + draw: 1 + } + }); + + cy.adminLogin(); + cy.visit(this.Urls.moderation_forge_add()) + .location('pathname') + .should('be.equal', this.Urls.moderation_forge_add()); + + logout(); + }); + + it('should not display moderation link in sidebar when not connected', function() { + cy.visit(url); + + cy.get(`.sidebar a[href="${this.Urls.moderation_forge_add()}"]`) + .should('not.be.visible'); + + logout(); + }); + + it('should not display moderation link in sidebar when connected as user', function() { + cy.userLogin(); + cy.visit(url); + + cy.get(`.sidebar a[href="${this.Urls.moderation_forge_add()}"]`) + .should('not.be.visible'); + + logout(); + }); + + it('should display moderation link in sidebar when connected as admin', function() { + cy.adminLogin(); + cy.visit(url); + + cy.get(`.sidebar a[href="${this.Urls.moderation_forge_add()}"]`) + .should('be.visible'); + + logout(); + }); + + it('should display moderation link in sidebar when connected as moderator', function() { + cy.moderatorLogin(); + cy.visit(url); + + cy.get(`.sidebar a[href="${this.Urls.moderation_forge_add()}"]`) + .should('be.visible'); + + logout(); + }); + + it('should prevent unauthorized access after logout', function() { + cy.visit(this.Urls.moderation_forge_add()) + .location('pathname') + .should('be.equal', '/admin/login/'); + }); +}); + +// const existingRowToSelect = 'https://bitbucket.org/'; + +// const originUrlListTestData = [ +// { +// listType: 'authorized', +// originToAdd: 'git://git.archlinux.org/', +// originToRemove: 'https://github.com/' +// }, +// { +// listType: 'unauthorized', +// originToAdd: 'https://random.org', +// originToRemove: 'https://gitlab.com' +// } +// ]; + +// const capitalize = s => s.charAt(0).toUpperCase() + s.slice(1); + +// describe('Test Admin Origin Save Urls Filtering', function() { + +// beforeEach(function() { +// cy.adminLogin(); +// cy.visit(this.Urls.admin_origin_save()); + +// cy.contains('a', 'Origin urls filtering') +// .click() +// .wait(500); +// }); + +// it(`should select or unselect a table row by clicking on it`, function() { +// cy.contains(`#swh-authorized-origin-urls tr`, existingRowToSelect) +// .click() +// .should('have.class', 'selected') +// .click() +// .should('not.have.class', 'selected'); +// }); + +// originUrlListTestData.forEach(testData => { + +// it(`should add a new origin url prefix in the ${testData.listType} list`, function() { + +// const tabName = capitalize(testData.listType) + ' urls'; + +// cy.contains('a', tabName) +// .click() +// .wait(500); + +// cy.get(`#swh-${testData.listType}-origin-urls tr`).each(elt => { +// if ($(elt).text() === testData.originToAdd) { +// cy.get(elt).click(); +// cy.get(`#swh-remove-${testData.listType}-origin-url`).click(); +// } +// }); + +// cy.get(`#swh-${testData.listType}-url-prefix`) +// .type(testData.originToAdd); + +// cy.get(`#swh-add-${testData.listType}-origin-url`) +// .click(); + +// cy.contains(`#swh-${testData.listType}-origin-urls tr`, testData.originToAdd) +// .should('be.visible'); + +// cy.contains('.alert-success', `The origin url prefix has been successfully added in the ${testData.listType} list.`) +// .should('be.visible'); + +// cy.get(`#swh-add-${testData.listType}-origin-url`) +// .click(); + +// cy.contains('.alert-warning', `The provided origin url prefix is already registered in the ${testData.listType} list.`) +// .should('be.visible'); + +// }); + +// it(`should remove an origin url prefix from the ${testData.listType} list`, function() { + +// const tabName = capitalize(testData.listType) + ' urls'; + +// cy.contains('a', tabName) +// .click(); + +// let originUrlMissing = true; +// cy.get(`#swh-${testData.listType}-origin-urls tr`).each(elt => { +// if ($(elt).text() === testData.originToRemove) { +// originUrlMissing = false; +// } +// }); + +// if (originUrlMissing) { +// cy.get(`#swh-${testData.listType}-url-prefix`) +// .type(testData.originToRemove); + +// cy.get(`#swh-add-${testData.listType}-origin-url`) +// .click(); + +// cy.get('.alert-dismissible button').click(); +// } + +// cy.contains(`#swh-${testData.listType}-origin-urls tr`, testData.originToRemove) +// .click(); + +// cy.get(`#swh-remove-${testData.listType}-origin-url`).click(); + +// cy.contains(`#swh-${testData.listType}-origin-urls tr`, testData.originToRemove) +// .should('not.exist'); + +// }); +// }); + +// }); + +// describe('Test Admin Origin Save', function() { + +// it(`should reject a save code now request with note`, function() { +// const originUrl = `https://example.org/${Date.now()}`; +// const rejectionNote = 'The provided URL does not target a git repository.'; + +// // anonymous user creates a request put in pending state +// cy.visit(this.Urls.origin_save()); + +// cy.get('#swh-input-origin-url') +// .type(originUrl); + +// cy.get('#swh-input-origin-save-submit') +// .click(); + +// // admin user logs in and visits save code now admin page +// cy.adminLogin(); +// cy.visit(this.Urls.admin_origin_save()); + +// // admin rejects the save request and adds a rejection note +// cy.contains('#swh-origin-save-pending-requests', originUrl) +// .click(); + +// cy.get('#swh-reject-save-origin-request') +// .click(); + +// cy.get('#swh-rejection-text') +// .then(textarea => { +// textarea.val(rejectionNote); +// }); + +// cy.get('#swh-rejection-submit') +// .click(); + +// cy.get('#swh-web-modal-confirm-ok-btn') +// .click(); + +// // checks rejection note has been saved to swh-web database +// cy.request(this.Urls.api_1_save_origin('git', originUrl)) +// .then(response => { +// expect(response.body[0]['note']).to.equal(rejectionNote); +// }); + +// // check rejection note is displayed by clicking on the info icon +// // in requests table from public save code now page +// cy.visit(this.Urls.origin_save()); +// cy.get('#swh-origin-save-requests-list-tab') +// .click(); + +// cy.contains('#swh-origin-save-requests tr', originUrl); +// cy.get('.swh-save-request-info') +// .eq(0) +// .click(); + +// cy.get('.popover-body') +// .should('have.text', rejectionNote); + +// // remove rejected request from swh-web database to avoid side effects +// // in tests located in origin-save.spec.js +// cy.visit(this.Urls.admin_origin_save()); +// cy.get('#swh-save-requests-rejected-tab') +// .click(); + +// cy.contains('#swh-origin-save-rejected-requests', originUrl) +// .click(); + +// cy.get('#swh-remove-rejected-save-origin-request') +// .click(); + +// cy.get('#swh-web-modal-confirm-ok-btn') +// .click(); +// }); diff --git a/cypress/support/index.js b/cypress/support/index.js --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -58,6 +58,10 @@ return loginUser('ambassador', 'ambassador'); }); +Cypress.Commands.add('moderatorLogin', () => { + return loginUser('moderator', 'moderator'); +}); + before(function() { this.unarchivedRepo = { url: 'https://github.com/SoftwareHeritage/swh-web', diff --git a/swh/web/api/views/add_forge_now.py b/swh/web/api/views/add_forge_now.py --- a/swh/web/api/views/add_forge_now.py +++ b/swh/web/api/views/add_forge_now.py @@ -24,11 +24,10 @@ from swh.web.add_forge_now.models import RequestStatus as AddForgeNowRequestStatus from swh.web.api.apidoc import api_doc, format_docstring from swh.web.api.apiurls import api_route +from swh.web.auth.utils import SWH_MODERATOR_PERMISSION from swh.web.common.exc import BadInputExc from swh.web.common.utils import reverse -MODERATOR_ROLE = "swh.web.add_forge_now.moderator" - def _block_while_testing(): """Replaced by tests to check concurrency behavior @@ -196,7 +195,7 @@ "You must be authenticated to update a new add-forge request" ) - if not request.user.has_perm(MODERATOR_ROLE): + if not request.user.has_perm(SWH_MODERATOR_PERMISSION): return HttpResponseForbidden("You are not a moderator") add_forge_request = ( @@ -321,7 +320,7 @@ paginator = Paginator(add_forge_requests, per_page) page = paginator.page(page_num) - if request.user.has_perm(MODERATOR_ROLE): + if request.user.has_perm(SWH_MODERATOR_PERMISSION): requests = AddForgeNowRequestSerializer(page.object_list, many=True).data else: requests = AddForgeNowRequestPublicSerializer(page.object_list, many=True).data @@ -387,7 +386,9 @@ request=add_forge_request ).order_by("id") - if request.user.is_authenticated and request.user.has_perm(MODERATOR_ROLE): + if request.user.is_authenticated and request.user.has_perm( + SWH_MODERATOR_PERMISSION + ): data = AddForgeNowRequestSerializer(add_forge_request).data history = AddForgeNowRequestHistorySerializer(request_history, many=True).data else: diff --git a/swh/web/auth/utils.py b/swh/web/auth/utils.py --- a/swh/web/auth/utils.py +++ b/swh/web/auth/utils.py @@ -19,6 +19,7 @@ API_SAVE_ORIGIN_PERMISSION = "swh.web.api.save_origin" ADMIN_LIST_DEPOSIT_PERMISSION = "swh.web.admin.list_deposits" MAILMAP_PERMISSION = "swh.web.mailmap" +SWH_MODERATOR_PERMISSION = "swh.web.add_forge_now.moderator" def _get_fernet(password: bytes, salt: bytes) -> Fernet: diff --git a/swh/web/templates/layout.html b/swh/web/templates/layout.html --- a/swh/web/templates/layout.html +++ b/swh/web/templates/layout.html @@ -221,7 +221,7 @@
Help
- {% if user.is_authenticated and user.is_staff or ADMIN_LIST_DEPOSIT_PERMISSION in user.get_all_permissions %} + {% if user.is_authenticated %}Save code now
Deposit