aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@gmail.com>2013-08-14 17:43:52 +0200
committerJulien Lancelot <julien.lancelot@gmail.com>2013-08-14 17:43:52 +0200
commit3554f0dddfbacee2e11c6c73ca084cfca1d2bb2d (patch)
treec2e7589d13472c943be9934a66e4470bf3f925b1
parentadde7cf9216f9a0ccf78409078838d826d816f4a (diff)
downloadsonarqube-3554f0dddfbacee2e11c6c73ca084cfca1d2bb2d.tar.gz
sonarqube-3554f0dddfbacee2e11c6c73ca084cfca1d2bb2d.zip
SONAR-4475 Add method to check that at least one admin exists
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties1
-rw-r--r--sonar-core/src/main/java/org/sonar/core/user/RoleDao.java10
-rw-r--r--sonar-core/src/main/java/org/sonar/core/user/RoleMapper.java4
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/user/RoleMapper.xml24
-rw-r--r--sonar-core/src/test/java/org/sonar/core/user/RoleDaoTest.java15
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/user/RoleDaoTest/should_count_user_with_permission.xml25
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/MessageException.java26
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/utils/MessageExceptionTest.java14
-rw-r--r--sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java7
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/groups_controller.rb4
-rw-r--r--sonar-server/src/main/webapp/WEB-INF/app/controllers/users_controller.rb17
-rw-r--r--sonar-server/src/main/webapp/javascripts/application.js2
-rw-r--r--sonar-server/src/test/java/org/sonar/server/permission/InternalPermissionServiceTest.java19
13 files changed, 154 insertions, 14 deletions
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties b/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties
index eb291c817c1..01df59dce07 100644
--- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/l10n/core.properties
@@ -2233,6 +2233,7 @@ global_permissions.scan.desc=Ability to execute analyses, and to get all setting
global_permissions.dryRunScan=Execute Local Analysis (Dry Run)
global_permissions.dryRunScan.desc=Ability to execute local (dry run) analyses without pushing the results to the server, and to get all settings required to perform a local analysis. This permission does not include the ability to access secured settings such as the scm account password, the jira account password, and so on.<br/>\
This permission is <em>required</em> to execute a local analysis in Eclipse or via the Issues Report plugin.
+global_permissions.error.need_at_lest_one_admin=At least one admin must be defined.
#------------------------------------------------------------------------------
#
diff --git a/sonar-core/src/main/java/org/sonar/core/user/RoleDao.java b/sonar-core/src/main/java/org/sonar/core/user/RoleDao.java
index c8bc741db1b..b936f0e3308 100644
--- a/sonar-core/src/main/java/org/sonar/core/user/RoleDao.java
+++ b/sonar-core/src/main/java/org/sonar/core/user/RoleDao.java
@@ -144,4 +144,14 @@ public class RoleDao implements TaskComponent, ServerComponent {
MyBatis.closeQuietly(session);
}
}
+
+ public int countUserWithPermission(String permission) {
+ SqlSession session = mybatis.openSession();
+ try {
+ RoleMapper mapper = session.getMapper(RoleMapper.class);
+ return mapper.countUserWithPermission(permission);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
}
diff --git a/sonar-core/src/main/java/org/sonar/core/user/RoleMapper.java b/sonar-core/src/main/java/org/sonar/core/user/RoleMapper.java
index 0fcaac7973b..7bd9fb0315e 100644
--- a/sonar-core/src/main/java/org/sonar/core/user/RoleMapper.java
+++ b/sonar-core/src/main/java/org/sonar/core/user/RoleMapper.java
@@ -21,7 +21,6 @@ package org.sonar.core.user;
import org.apache.ibatis.annotations.Param;
-import javax.annotation.Nullable;
import java.util.List;
/**
@@ -49,5 +48,6 @@ public interface RoleMapper {
int countUserRoles(Long resourceId);
- List<Long> countSystemAdministrators(@Nullable @Param("groupName") String groupName);
+ int countUserWithPermission(@Param("permission") String permission);
+
}
diff --git a/sonar-core/src/main/resources/org/sonar/core/user/RoleMapper.xml b/sonar-core/src/main/resources/org/sonar/core/user/RoleMapper.xml
index 3dba348d55a..bcfbe774bd3 100644
--- a/sonar-core/src/main/resources/org/sonar/core/user/RoleMapper.xml
+++ b/sonar-core/src/main/resources/org/sonar/core/user/RoleMapper.xml
@@ -88,4 +88,28 @@
SELECT count(id)
FROM group_roles WHERE resource_id=#{id}
</select>
+
+ <select id="countUserWithPermission" parameterType="map" resultType="Integer">
+ SELECT COUNT(DISTINCT tmp.id)
+ FROM (
+ SELECT u.id as id
+ FROM users AS u
+ INNER JOIN user_roles AS ur ON ur.user_id = u.id
+ <where>
+ AND (ur.role = #{permission} AND ur.resource_id IS NULL)
+ AND u.active = true
+ </where>
+ UNION
+ select u.id as id
+ FROM users as u
+ INNER JOIN groups_users AS gu ON gu.user_id = u.id
+ INNER JOIN group_roles AS gr ON gr.group_id = gu.group_id
+ INNER JOIN groups AS g ON g.id = gu.group_id
+ <where>
+ AND (gr.role = #{permission} AND gr.resource_id IS NULL)
+ AND u.active = true
+ </where>
+ ) AS tmp
+ </select>
+
</mapper>
diff --git a/sonar-core/src/test/java/org/sonar/core/user/RoleDaoTest.java b/sonar-core/src/test/java/org/sonar/core/user/RoleDaoTest.java
index 6d0c9b0830e..427c2aee456 100644
--- a/sonar-core/src/test/java/org/sonar/core/user/RoleDaoTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/user/RoleDaoTest.java
@@ -76,4 +76,19 @@ public class RoleDaoTest extends AbstractDaoTestCase {
checkTable("groupPermissions", "group_roles", "group_id", "role");
}
+ @Test
+ public void should_count_user_with_permission() {
+ setupData("should_count_user_with_permission");
+
+ RoleDao dao = new RoleDao(getMyBatis());
+
+ // 1 user have role 'admin', 1 user owns to the 'admin' group
+ assertThat(dao.countUserWithPermission("admin")).isEqualTo(2);
+ // 1 user have role 'profileadmin'
+ assertThat(dao.countUserWithPermission("profileadmin")).isEqualTo(1);
+ // 1 user owns to the 'user' group
+ assertThat(dao.countUserWithPermission("user")).isEqualTo(1);
+ assertThat(dao.countUserWithPermission("unknown")).isEqualTo(0);
+ }
+
}
diff --git a/sonar-core/src/test/resources/org/sonar/core/user/RoleDaoTest/should_count_user_with_permission.xml b/sonar-core/src/test/resources/org/sonar/core/user/RoleDaoTest/should_count_user_with_permission.xml
new file mode 100644
index 00000000000..8fe58e3c7d3
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/user/RoleDaoTest/should_count_user_with_permission.xml
@@ -0,0 +1,25 @@
+<dataset>
+
+ <!-- users having role -->
+
+ <users id="200" login="admin_user" name="admin_user" active="[true]"/>
+ <users id="201" login="profile_admin_user" name="profile_admin_user" active="[true]"/>
+
+ <user_roles id="1" user_id="200" role="admin"/>
+ <user_roles id="2" user_id="201" role="profileadmin"/>
+
+ <!-- users having group -->
+
+ <users id="202" login="admin_group" name="admin_group" active="[true]"/>
+ <users id="203" login="user_group" name="user_group" active="[true]"/>
+
+ <groups id="100" name="sonar-administrators"/>
+ <groups id="101" name="sonar-users"/>
+
+ <group_roles id="1" group_id="100" role="admin"/>
+ <group_roles id="2" group_id="101" role="user"/>
+
+ <groups_users user_id="202" group_id="100"/>
+ <groups_users user_id="203" group_id="101"/>
+
+</dataset>
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/MessageException.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/MessageException.java
index 25290dac9b2..7a703557b5a 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/MessageException.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/MessageException.java
@@ -19,6 +19,14 @@
*/
package org.sonar.api.utils;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import static com.google.common.collect.Lists.newArrayList;
+
/**
* Runtime exception for "functional" errors. It aims to be displayed to end-users, without any technical information
* like stack traces.
@@ -27,10 +35,19 @@ package org.sonar.api.utils;
*/
public class MessageException extends RuntimeException {
+ private String l10nKey;
+ private Collection<Object> l10nParams;
+
public MessageException(String s) {
super(s);
}
+ public MessageException(@Nullable String message, @Nullable String l10nKey, @Nullable Object[] l10nParams) {
+ super(message);
+ this.l10nKey = l10nKey;
+ this.l10nParams = l10nParams == null ? Collections.emptyList() : Collections.unmodifiableCollection(newArrayList(l10nParams));
+ }
+
/**
* Does not fill in the stack trace
*
@@ -46,5 +63,14 @@ public class MessageException extends RuntimeException {
return getMessage();
}
+ @CheckForNull
+ public String l10nKey() {
+ return l10nKey;
+ }
+
+ @CheckForNull
+ public Collection<Object> l10nParams() {
+ return l10nParams;
+ }
}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/MessageExceptionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/MessageExceptionTest.java
index 0b734416e94..f036069412f 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/utils/MessageExceptionTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/utils/MessageExceptionTest.java
@@ -47,4 +47,18 @@ public class MessageExceptionTest {
assertThat(writer.toString()).isEqualTo(message + System.getProperty("line.separator"));
}
}
+
+ @Test
+ public void should_create_exception_with_status_and_l10n_message_with_param(){
+ MessageException exception = new MessageException(null, "key", new String[]{"value"});
+ assertThat(exception.l10nKey()).isEqualTo("key");
+ assertThat(exception.l10nParams()).containsOnly("value");
+ }
+
+ @Test
+ public void should_create_exception_with_status_and_l10n_message_without_param(){
+ MessageException exception = new MessageException(null, "key", null);
+ assertThat(exception.l10nKey()).isEqualTo("key");
+ assertThat(exception.l10nParams()).isEmpty();
+ }
}
diff --git a/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java b/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java
index 3fdcc998ab8..c6e22f94435 100644
--- a/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java
+++ b/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java
@@ -24,6 +24,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.ServerComponent;
import org.sonar.api.security.DefaultGroups;
+import org.sonar.api.utils.MessageException;
import org.sonar.core.permission.ComponentPermissionFacade;
import org.sonar.core.permission.Permission;
import org.sonar.core.user.*;
@@ -71,6 +72,12 @@ public class InternalPermissionService implements ServerComponent {
}
}
+ public void checkAtLeatOneSysAdminExists(){
+ if (roleDao.countUserWithPermission(Permission.SYSTEM_ADMIN.key()) == 0){
+ throw new MessageException(null, "global_permissions.error.need_at_lest_one_admin", null);
+ }
+ }
+
private void applyPermissionTemplate(String templateKey, String componentId) {
permissionFacade.applyPermissionTemplate(templateKey, Long.parseLong(componentId));
}
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/groups_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/groups_controller.rb
index f2d5fd5d878..1105fa5292b 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/groups_controller.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/groups_controller.rb
@@ -23,7 +23,7 @@ class GroupsController < ApplicationController
before_filter :admin_required
def index
- @groups = Group.find(:all, :order => 'name')
+ @groups = Group.all(:order => 'name')
if params[:id]
@group = Group.find(params[:id])
else
@@ -72,7 +72,7 @@ class GroupsController < ApplicationController
end
def to_index(errors, id)
- if !errors.empty?
+ unless errors.empty?
flash[:error] = errors.full_messages.join("<br/>\n")
end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/users_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/users_controller.rb
index 97807f9a749..98bcbad7e30 100644
--- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/users_controller.rb
+++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/users_controller.rb
@@ -130,17 +130,16 @@ class UsersController < ApplicationController
end
def destroy
- @user = User.find(params[:id])
-
- if current_user.id==@user.id
- flash[:error] = 'Please log in with another user in order to delete yourself.'
-
- else
- Api.users.deactivate(@user.login)
+ begin
+ user = User.find(params[:id])
+ Api.users.deactivate(user.login)
flash[:notice] = 'User is deleted.'
+ rescue Java::OrgSonarServerExceptions::HttpException => exception
+ error = exception.cause
+ flash[:error] = (error.getMessage ? error.getMessage : Api::Utils.message(error.l10nKey, :params => error.l10nParams.to_a))
end
- to_index(@user.errors, nil)
+ redirect_to(:action => 'index', :id => nil)
end
def select_group
@@ -158,7 +157,7 @@ class UsersController < ApplicationController
end
def to_index(errors, id)
- if !errors.empty?
+ unless errors.empty?
flash[:error] = errors.full_messages.join("<br/>\n")
end
diff --git a/sonar-server/src/main/webapp/javascripts/application.js b/sonar-server/src/main/webapp/javascripts/application.js
index 773170b1a38..501fd589a24 100644
--- a/sonar-server/src/main/webapp/javascripts/application.js
+++ b/sonar-server/src/main/webapp/javascripts/application.js
@@ -320,8 +320,8 @@ function openModalWindow(url, options) {
$j('input[type=submit]', obj).removeAttr('disabled');
errorElt.show();
errorElt.html(xhr.responseText);
- // otherwise replace modal window by the returned text
} else {
+ // otherwise replace modal window by the returned text
$j("#modal").html(xhr.responseText);
}
}
diff --git a/sonar-server/src/test/java/org/sonar/server/permission/InternalPermissionServiceTest.java b/sonar-server/src/test/java/org/sonar/server/permission/InternalPermissionServiceTest.java
index 6dd2f92fca6..04d8daceb6a 100644
--- a/sonar-server/src/test/java/org/sonar/server/permission/InternalPermissionServiceTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/permission/InternalPermissionServiceTest.java
@@ -30,6 +30,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.security.DefaultGroups;
+import org.sonar.api.utils.MessageException;
import org.sonar.core.permission.ComponentPermissionFacade;
import org.sonar.core.permission.Permission;
import org.sonar.core.user.*;
@@ -40,6 +41,8 @@ import org.sonar.server.user.MockUserSession;
import java.util.Map;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.*;
@@ -191,6 +194,22 @@ public class InternalPermissionServiceTest {
verify(permissionFacade).applyPermissionTemplate("my_template_key", 3L);
}
+ @Test
+ public void should_check_at_least_one_sys_admin_exists() throws Exception {
+ // One admin exists -> all is ok
+ when(roleDao.countUserWithPermission(anyString())).thenReturn(1);
+ service.checkAtLeatOneSysAdminExists();
+
+ // No admin -> fail
+ try {
+ when(roleDao.countUserWithPermission(anyString())).thenReturn(0);
+ service.checkAtLeatOneSysAdminExists();
+ fail();
+ } catch (Exception e){
+ assertThat(e).isInstanceOf(MessageException.class);
+ }
+ }
+
protected static class MatchesUserRole extends BaseMatcher<UserRoleDto> {
private final UserRoleDto referenceDto;