diff options
author | Stas Vilchik <stasvilchik@Stas-Vilchik-iMac.local> | 2014-12-10 13:58:37 +0100 |
---|---|---|
committer | Stas Vilchik <stasvilchik@Stas-Vilchik-iMac.local> | 2014-12-10 14:15:39 +0100 |
commit | 8bec488ab21ecfeacdc5089df7623d80569e2844 (patch) | |
tree | 9e92a4d425dadca95aa9a69e7cd642d430e053c7 | |
parent | a4f5098c74bb4bccfa33b288cc497029828144fb (diff) | |
download | sonarqube-8bec488ab21ecfeacdc5089df7623d80569e2844.tar.gz sonarqube-8bec488ab21ecfeacdc5089df7623d80569e2844.zip |
SONAR-5884 Show error message when no permission to see the source code
6 files changed, 450 insertions, 68 deletions
diff --git a/server/sonar-web/src/main/hbs/source-viewer/source-viewer.hbs b/server/sonar-web/src/main/hbs/source-viewer/source-viewer.hbs index 1739fd3b6dd..c47a2a3749c 100644 --- a/server/sonar-web/src/main/hbs/source-viewer/source-viewer.hbs +++ b/server/sonar-web/src/main/hbs/source-viewer/source-viewer.hbs @@ -1,54 +1,62 @@ <div class="source-viewer-header"></div> -{{#if hasSourceBefore}} - <i class="spinner js-component-viewer-source-before"></i> -{{/if}} +{{#if canSeeCode}} + + {{#if hasSourceBefore}} + <i class="spinner js-component-viewer-source-before"></i> + {{/if}} + + <table class="source-table"> + {{#each source}} + <tr class="source-line {{#eq line 0}}{{#empty issues}}hidden{{/empty}}{{/eq}}" {{#if line}}data-line-number="{{line}}"{{/if}}> + <td class="source-meta source-line-number" {{#if line}}data-line-number="{{line}}"{{/if}}></td> + + <td class="source-meta source-line-scm" {{#if line}}data-line-number="{{line}}"{{/if}}> + {{#ifSCMChanged2 ../source line}} + <div class="source-line-scm-inner" data-author="{{scmAuthor}}"></div> + {{/ifSCMChanged2}} + </td> + + <td class="source-meta source-line-coverage {{#notNull coverageStatus}}source-line-{{coverageStatus}}{{/notNull}}" + data-line-number="{{line}}"> + <div class="source-line-bar"></div> + </td> -<table class="source-table"> - {{#each source}} - <tr class="source-line {{#eq line 0}}{{#empty issues}}hidden{{/empty}}{{/eq}}" {{#if line}}data-line-number="{{line}}"{{/if}}> - <td class="source-meta source-line-number" {{#if line}}data-line-number="{{line}}"{{/if}}></td> - - <td class="source-meta source-line-scm" {{#if line}}data-line-number="{{line}}"{{/if}}> - {{#ifSCMChanged2 ../source line}} - <div class="source-line-scm-inner" data-author="{{scmAuthor}}"></div> - {{/ifSCMChanged2}} - </td> - - <td class="source-meta source-line-coverage {{#notNull coverageStatus}}source-line-{{coverageStatus}}{{/notNull}}" - data-line-number="{{line}}"> - <div class="source-line-bar"></div> - </td> - - <td class="source-meta source-line-duplications {{#if duplicated}}source-line-duplicated{{/if}}" - title="{{t 'source_viewer.expand_duplications'}}"> - <div class="source-line-bar"></div> - </td> - - {{#each duplications}} - <td class="source-meta source-line-duplications-extra {{#if this}}source-line-duplicated{{/if}}" - data-index="{{this}}" data-line-number="{{line}}"> + <td class="source-meta source-line-duplications {{#if duplicated}}source-line-duplicated{{/if}}" + title="{{t 'source_viewer.expand_duplications'}}"> <div class="source-line-bar"></div> </td> - {{/each}} - - <td class="source-line-code code {{#notEmpty issues}}has-issues{{/notEmpty}}" data-line-number="{{line}}"> - {{#notNull code}} - <pre>{{#if code}}{{{code}}}{{else}} {{/if}}</pre> - {{/notNull}} - - {{#notEmpty issues}} - <div class="issue-list"> - {{#each issues}} - <div class="issue" id="issue-{{key}}"></div> - {{/each}} - </div> - {{/notEmpty}} - </td> - </tr> - {{/each}} -</table> - -{{#if hasSourceAfter}} - <i class="spinner js-component-viewer-source-after"></i> + + {{#each duplications}} + <td class="source-meta source-line-duplications-extra {{#if this}}source-line-duplicated{{/if}}" + data-index="{{this}}" data-line-number="{{line}}"> + <div class="source-line-bar"></div> + </td> + {{/each}} + + <td class="source-line-code code {{#notEmpty issues}}has-issues{{/notEmpty}}" data-line-number="{{line}}"> + {{#notNull code}} + <pre>{{#if code}}{{{code}}}{{else}} {{/if}}</pre> + {{/notNull}} + + {{#notEmpty issues}} + <div class="issue-list"> + {{#each issues}} + <div class="issue" id="issue-{{key}}"></div> + {{/each}} + </div> + {{/notEmpty}} + </td> + </tr> + {{/each}} + </table> + + {{#if hasSourceAfter}} + <i class="spinner js-component-viewer-source-after"></i> + {{/if}} + +{{else}} + + <div class="message-error">{{t 'code_viewer.no_source_code_displayed_due_to_security'}}</div> + {{/if}} diff --git a/server/sonar-web/src/main/js/source-viewer/source.js b/server/sonar-web/src/main/js/source-viewer/source.js index 1ded0471cb7..a620f11245e 100644 --- a/server/sonar-web/src/main/js/source-viewer/source.js +++ b/server/sonar-web/src/main/js/source-viewer/source.js @@ -10,7 +10,9 @@ define([ hasSource: false, hasCoverage: false, hasDuplications: false, - hasSCM: false + hasSCM: false, + + canSeeCode: true }; }, diff --git a/server/sonar-web/src/main/js/source-viewer/viewer.js b/server/sonar-web/src/main/js/source-viewer/viewer.js index d8e8230e9f4..e5f9d49d005 100644 --- a/server/sonar-web/src/main/js/source-viewer/viewer.js +++ b/server/sonar-web/src/main/js/source-viewer/viewer.js @@ -72,7 +72,7 @@ define([ }, renderHeader: function () { - this.headerRegion.show(new HeaderView({ model: this.model })); + this.headerRegion.show(new HeaderView({model: this.model})); }, onRender: function () { @@ -102,18 +102,22 @@ define([ that.trigger('loaded'); }); }; - this.model.clear(); - this.model.set({ - uuid: id, - key: key - }); + this.model + .clear() + .set(_.result(this.model, 'defaults')) + .set({ + uuid: id, + key: key + }); this.requestComponent().done(function () { that.requestSource() .done(finalize) .fail(function () { - that.model.set({ source: [ - { line: 0 } - ] }); + that.model.set({ + source: [ + {line: 0} + ] + }); finalize(); }); }); @@ -123,10 +127,10 @@ define([ requestComponent: function () { var that = this, url = baseUrl + '/api/components/app', - options = { key: this.model.key() }; + options = {key: this.model.key()}; return $.get(url, options).done(function (data) { that.model.set(data); - that.model.set({ isUnitTest: data.q === 'UTS' }); + that.model.set({isUnitTest: data.q === 'UTS'}); }); }, @@ -154,14 +158,14 @@ define([ requestSource: function () { var that = this, url = baseUrl + '/api/sources/lines', - options = _.extend({ uuid: this.model.id }, this.linesLimit()); - return $.get(url, options, function (data) { + options = _.extend({uuid: this.model.id}, this.linesLimit()); + return $.get(url, options).done(function (data) { var source = (data.sources || []).slice(0); if (source.length === 0 || (source.length > 0 && _.first(source).line === 1)) { - source.unshift({ line: 0 }); + source.unshift({line: 0}); } source = source.map(function (row) { - return _.extend(row, { coverageStatus: that.getCoverageStatus(row) }); + return _.extend(row, {coverageStatus: that.getCoverageStatus(row)}); }); var firstLine = _.first(source).line, linesRequested = options.to - options.from + 1; @@ -170,13 +174,22 @@ define([ hasSourceBefore: firstLine > 1, hasSourceAfter: data.sources.length === linesRequested }); + }).fail(function (request) { + if (request.status === 403) { + that.model.set({ + source: [], + hasSourceBefore: false, + hasSourceAfter: false, + canSeeCode: false + }); + } }); }, requestDuplications: function () { var that = this, url = baseUrl + '/api/duplications/show', - options = { key: this.model.key() }; + options = {key: this.model.key()}; return $.get(url, options, function (data) { var hasDuplications = (data != null) && (data.duplications != null), duplications = []; @@ -276,7 +289,7 @@ define([ e.stopPropagation(); $('body').click(); var line = +$(e.currentTarget).data('line-number'), - row = _.findWhere(this.model.get('source'), { line: line }), + row = _.findWhere(this.model.get('source'), {line: line}), popup = new SCMPopupView({ triggerEl: $(e.currentTarget), model: new Backbone.Model(row) @@ -289,7 +302,7 @@ define([ $('body').click(); var r = window.process.addBackgroundProcess(), line = $(e.currentTarget).data('line-number'), - row = _.findWhere(this.model.get('source'), { line: line }), + row = _.findWhere(this.model.get('source'), {line: line}), url = baseUrl + '/api/tests/test_cases', options = { key: this.model.key(), @@ -437,7 +450,7 @@ define([ return $.get(url, options).done(function (data) { source = (data.sources || []).concat(source); if (source.length === 0 || (source.length > 0 && _.first(source).line === 1)) { - source.unshift({ line: 0 }); + source.unshift({line: 0}); } that.model.set({ source: source, diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/source-viewer-should-not-show-source-if-no-permission.js b/server/sonar-web/src/main/js/tests/e2e/tests/source-viewer-should-not-show-source-if-no-permission.js new file mode 100644 index 00000000000..49406ab01ee --- /dev/null +++ b/server/sonar-web/src/main/js/tests/e2e/tests/source-viewer-should-not-show-source-if-no-permission.js @@ -0,0 +1,30 @@ +/* global casper:false */ + +var lib = require('../lib'), + testName = lib.testName('Source Viewer'); + +lib.initMessages(); +lib.changeWorkingDirectory('source-viewer-should-not-show-source-if-no-permission'); + + +casper.test.begin(testName('source-viewer-should-not-show-source-if-no-permission'), function (test) { + casper + .start(lib.buildUrl('source-viewer'), function () { + lib.setDefaultViewport(); + + lib.mockRequest('/api/l10n/index', '{}'); + lib.mockRequestFromFile('/api/components/app', 'api-components-app.json'); + lib.mockRequest('/api/sources/lines', '{}', { status: 403 }); + lib.mockRequestFromFile('/api/issues/search', 'api-issues-search.json'); + }) + + .then(function () { + casper.waitForSelector('.message-error', function () { + test.assertDoesntExist('.source-line'); + }); + }) + + .run(function () { + test.done(); + }); +}); diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/source-viewer-should-not-show-source-if-no-permission/api-components-app.json b/server/sonar-web/src/main/js/tests/e2e/tests/source-viewer-should-not-show-source-if-no-permission/api-components-app.json new file mode 100644 index 00000000000..25b2e1e70f1 --- /dev/null +++ b/server/sonar-web/src/main/js/tests/e2e/tests/source-viewer-should-not-show-source-if-no-permission/api-components-app.json @@ -0,0 +1,95 @@ +{ + "key": "test:fake-project-for-tests:src/main/java/foo/Simplest.java", + "path": "src/main/java/foo/Simplest.java", + "name": "Simplest.java", + "longName": "src/main/java/foo/Simplest.java", + "q": "FIL", + "project": "test:fake-project-for-tests", + "projectName": "Fake Project for Tests", + "fav": false, + "canMarkAsFavourite": true, + "canBulkChange": true, + "canCreateManualIssue": true, + "periods": [ + [ + 1, + "since previous analysis (2014 Dec 09)", + "2014-12-09T10:47:33+0100" + ], + [ + 2, + "over 365 days (2014 Dec 09)", + "2014-12-09T10:47:33+0100" + ] + ], + "severities": [ + [ + "MINOR", + "Minor", + 1 + ], + [ + "MAJOR", + "Major", + 4 + ] + ], + "rules": [ + [ + "squid:IndentationCheck", + "Source code should be correctly indented", + 2 + ], + [ + "squid:S1118", + "Utility classes should not have a public constructor", + 1 + ], + [ + "squid:S00105", + "Tabulation characters should not be used", + 1 + ], + [ + "squid:S106", + "System.out and System.err should not be used as loggers", + 1 + ] + ], + "measures": { + "fNcloc": "8", + "fCoverage": "75.0%", + "fDebt": "44min", + "fSqaleRating": "C", + "fSqaleDebtRatio": "18.3%", + "fIssues": "5", + "fMinorIssues": "1", + "fMajorIssues": "4" + }, + "tabs": [ + "scm", + "coverage" + ], + "manual_rules": [ + { + "key": "manual:api", + "name": "API" + }, + { + "key": "manual:design", + "name": "Design" + }, + { + "key": "manual:error_handling", + "name": "Error handling" + }, + { + "key": "manual:performance", + "name": "Performance" + }, + { + "key": "manual:sql_pitfalls", + "name": "SQL Pitfall" + } + ] +} diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/source-viewer-should-not-show-source-if-no-permission/api-issues-search.json b/server/sonar-web/src/main/js/tests/e2e/tests/source-viewer-should-not-show-source-if-no-permission/api-issues-search.json new file mode 100644 index 00000000000..73d6a9fcf3d --- /dev/null +++ b/server/sonar-web/src/main/js/tests/e2e/tests/source-viewer-should-not-show-source-if-no-permission/api-issues-search.json @@ -0,0 +1,234 @@ +{ + "total": 5, + "p": 1, + "ps": 100, + "projects": [ + { + "uuid": "e07e9bed-f45d-4538-a5e8-77031c1ecae7", + "key": "test:fake-project-for-tests", + "id": 36125, + "qualifier": "TRK", + "name": "Fake Project for Tests", + "longName": "Fake Project for Tests" + } + ], + "components": [ + { + "uuid": "b4248001-df0c-436a-8acd-a152809b8a23", + "key": "test:fake-project-for-tests:src/main/java/foo/Simplest.java", + "id": 36129, + "enabled": true, + "qualifier": "FIL", + "name": "Simplest.java", + "longName": "src/main/java/foo/Simplest.java", + "path": "src/main/java/foo/Simplest.java", + "projectId": 36125, + "subProjectId": 36125 + }, + { + "uuid": "e07e9bed-f45d-4538-a5e8-77031c1ecae7", + "key": "test:fake-project-for-tests", + "id": 36125, + "enabled": true, + "qualifier": "TRK", + "name": "Fake Project for Tests", + "longName": "Fake Project for Tests" + } + ], + "issues": [ + { + "key": "91b13078-6f69-4b51-ab0f-8fd0bc391532", + "component": "test:fake-project-for-tests:src/main/java/foo/Simplest.java", + "componentId": 36129, + "project": "test:fake-project-for-tests", + "rule": "squid:S00105", + "status": "OPEN", + "severity": "MINOR", + "message": "Replace all tab characters in this file by sequences of white-spaces.", + "debt": "2min", + "creationDate": "2014-12-09T10:47:33+0100", + "updateDate": "2014-12-09T10:47:33+0100", + "fUpdateAge": "3 hours", + "actions": [ + "comment", + "assign", + "assign_to_me", + "plan", + "set_severity" + ], + "transitions": [ + "confirm", + "resolve", + "falsepositive" + ] + }, + { + "key": "c18ae6fa-fac7-46b5-bd71-feef7906b05a", + "component": "test:fake-project-for-tests:src/main/java/foo/Simplest.java", + "componentId": 36129, + "project": "test:fake-project-for-tests", + "rule": "squid:S1118", + "status": "OPEN", + "severity": "MAJOR", + "message": "Add a private constructor to hide the implicit public one.", + "line": 6, + "debt": "30min", + "creationDate": "2014-12-09T10:47:33+0100", + "updateDate": "2014-12-09T10:47:33+0100", + "fUpdateAge": "3 hours", + "actions": [ + "comment", + "assign", + "assign_to_me", + "plan", + "set_severity" + ], + "transitions": [ + "confirm", + "resolve", + "falsepositive" + ] + }, + { + "key": "816282ec-0ca9-4d03-b8bc-22b515496fe4", + "component": "test:fake-project-for-tests:src/main/java/foo/Simplest.java", + "componentId": 36129, + "project": "test:fake-project-for-tests", + "rule": "squid:IndentationCheck", + "status": "OPEN", + "severity": "MAJOR", + "message": "Make this line start at column 3.", + "line": 8, + "debt": "1min", + "creationDate": "2014-12-09T10:47:33+0100", + "updateDate": "2014-12-09T10:47:33+0100", + "fUpdateAge": "3 hours", + "actions": [ + "comment", + "assign", + "assign_to_me", + "plan", + "set_severity" + ], + "transitions": [ + "confirm", + "resolve", + "falsepositive" + ] + }, + { + "key": "0229497b-a613-48f3-83ed-2b98983e5e60", + "component": "test:fake-project-for-tests:src/main/java/foo/Simplest.java", + "componentId": 36129, + "project": "test:fake-project-for-tests", + "rule": "squid:IndentationCheck", + "status": "OPEN", + "severity": "MAJOR", + "message": "Make this line start at column 5.", + "line": 10, + "debt": "1min", + "creationDate": "2014-12-09T10:47:33+0100", + "updateDate": "2014-12-09T10:47:33+0100", + "fUpdateAge": "3 hours", + "actions": [ + "comment", + "assign", + "assign_to_me", + "plan", + "set_severity" + ], + "transitions": [ + "confirm", + "resolve", + "falsepositive" + ] + }, + { + "key": "896ccfe0-bb85-4155-be1f-1aaece716539", + "component": "test:fake-project-for-tests:src/main/java/foo/Simplest.java", + "componentId": 36129, + "project": "test:fake-project-for-tests", + "rule": "squid:S106", + "status": "OPEN", + "severity": "MAJOR", + "message": "Replace this usage of System.out or System.err by a logger.", + "line": 12, + "debt": "10min", + "creationDate": "2014-12-09T10:47:33+0100", + "updateDate": "2014-12-09T10:47:33+0100", + "fUpdateAge": "3 hours", + "actions": [ + "comment", + "assign", + "assign_to_me", + "plan", + "set_severity" + ], + "transitions": [ + "confirm", + "resolve", + "falsepositive" + ] + } + ], + "rules": [ + { + "key": "squid:IndentationCheck", + "name": "Source code should be correctly indented", + "lang": "java", + "desc": "<p>\nProper indentation is a simple and effective way to improve the code's readability.\nConsistent indentation among developers also reduces the differences that are committed to source control systems, making code reviews easier.\n</p>\n\n<p>The following code illustrates this rule with an indentation level of 2:</p>\n\n<pre>\nclass Foo {\n public int a; // Compliant\npublic int b; // Non-Compliant\n}\n</pre>", + "status": "READY", + "langName": "Java" + }, + { + "key": "squid:S1118", + "name": "Utility classes should not have a public constructor", + "lang": "java", + "desc": "<p>\nUtility classes, which are a collection of static members, are not meant to be instantiated.\nThey should therefore not have public constructors.\n</p>\n\n<p>\nJava adds an implicit public constructor to every class which does not define at least one explicitly.\nHence, at least one non-public constructor should be defined.\n</p>\n\n<p>The following code:</p>\n\n<pre>\nclass StringUtils { // Non-Compliant\n\n public static String concatenate(String s1, String s2) {\n return s1 + s2;\n }\n\n}\n</pre>\n\n<p>should be refactored into:</p>\n\n<pre>\nclass StringUtils { // Compliant\n\n private StringUtils() {\n }\n\n public static String concatenate(String s1, String s2) {\n return s1 + s2;\n }\n\n}\n</pre>", + "status": "READY", + "langName": "Java" + }, + { + "key": "squid:S00105", + "name": "Tabulation characters should not be used", + "lang": "java", + "desc": "<p>\nDevelopers should not need to configure the tab width of their text editors in order to be able to read source code.\nSo the use of tabulation character must be banned.\n</p>", + "status": "READY", + "langName": "Java" + }, + { + "key": "squid:S106", + "name": "System.out and System.err should not be used as loggers", + "lang": "java", + "desc": "<p>Two important requirements must be fulfilled when logging messages:</p>\n\n<ul>\n <li>The user must be able to easily retrieve the logs.</li>\n <li>The format of all messages must be uniform to enable users to easily browse them.</li>\n</ul>\n\n<p>\nIf a program directly writes to the standard output, there is absolutely no way to comply with these requirements.\nThat's why defining and using a dedicated logger is highly recommended.\n</p>\n\n<p>\nThe following code snippet illustrates this rule:\n</p>\n\n<pre>\nSystem.out.println(\"My Message\"); // Non-Compliant\n\nlogger.log(\"My Message\"); // Compliant\n</pre>", + "status": "READY", + "langName": "Java" + } + ], + "users": [ + { + "login": "admin", + "name": "Admin Admin", + "active": true, + "email": "admin@sonarsource.com" + } + ], + "languages": [ + { + "key": "js", + "name": "JavaScript" + }, + { + "key": "java", + "name": "Java" + } + ], + "maxResultsReached": false, + "paging": { + "pageIndex": 1, + "pageSize": 100, + "total": 5, + "fTotal": "5", + "pages": 1 + } +} |