3 * Copyright (C) 2009-2017 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.platform.db.migration.version.v64;
22 import java.sql.SQLException;
23 import org.sonar.db.Database;
24 import org.sonar.server.platform.db.migration.step.DataChange;
25 import org.sonar.server.platform.db.migration.step.MassUpdate;
26 import org.sonar.server.platform.db.migration.step.Select;
27 import org.sonar.server.platform.db.migration.step.SqlStatement;
30 * This DB migration assumes the whole PROJECTS table contains only rows with private=false
31 * (set by {@link PopulateColumnProjectsPrivate}) and performs the following:
33 * <li>set private=true for any tree of component which root has neither user nor codeviewer permission for group AnyOne
34 * but defines at least one permission directly to a group or user</li>
35 * <li>removes any permission to group AnyOne for root components which are made private</li>
36 * <li>ensures any group or user with direct permission to a private root component also has both permissions
37 * user and codeviewer</li>
38 * <li>deletes any permission user or codeviewer for root components which stays public</li>
40 * This DB migration of course works if PROJECTS table contains rows with private=true, but it will assume they have
41 * been set by a previous run of itself (ie. the DB migration is reentrant).
43 public class MakeComponentsPrivateBasedOnPermissions extends DataChange {
45 private static final String SCOPE_PROJECT = "PRJ";
46 private static final String QUALIFIER_PROJECT = "TRK";
47 private static final String QUALIFIER_VIEW = "VW";
48 private static final String PERMISSION_USER = "user";
49 private static final String PERMISSION_CODEVIEWER = "codeviewer";
51 public MakeComponentsPrivateBasedOnPermissions(Database db) {
56 protected void execute(Context context) throws SQLException {
57 makePrivateComponent(context);
58 cleanPermissionsOfPublicComponents(context);
59 insertUserPermissionOfPrivateRootComponent(context, PERMISSION_USER);
60 insertUserPermissionOfPrivateRootComponent(context, PERMISSION_CODEVIEWER);
61 insertGroupPermissionOfPrivateRootComponent(context, PERMISSION_USER);
62 insertGroupPermissionOfPrivateRootComponent(context, PERMISSION_CODEVIEWER);
65 private static void makePrivateComponent(Context context) throws SQLException {
66 MassUpdate massUpdate = context.prepareMassUpdate();
67 massUpdate.select("select uuid, id from projects p where " +
69 " and p.qualifier in (?, ?)" +
70 " and p.private = ?" +
74 " from group_roles gr" +
76 " gr.resource_id = p.id" +
77 " and gr.group_id is null" +
78 " and gr.role in (?, ?)" +
80 // trees with only permissions to group must not be made private
88 " gr2.resource_id = p.id" +
89 " and gr2.group_id is not null" +
97 " ur.resource_id = p.id" +
100 .setString(1, SCOPE_PROJECT)
101 .setString(2, QUALIFIER_PROJECT)
102 .setString(3, QUALIFIER_VIEW)
103 .setBoolean(4, false)
104 .setString(5, PERMISSION_USER)
105 .setString(6, PERMISSION_CODEVIEWER);
106 massUpdate.rowPluralName("component trees to be made private");
107 // make project private
108 massUpdate.update("update projects set private = ? where project_uuid = ?");
109 // delete any permission given to group "Anyone"
110 massUpdate.update("delete from group_roles where resource_id = ? and group_id is null");
111 massUpdate.execute(MakeComponentsPrivateBasedOnPermissions::handleMakePrivateComponent);
114 private static boolean handleMakePrivateComponent(Select.Row row, SqlStatement update, int updateIndex) throws SQLException {
115 String rootUuid = row.getString(1);
116 long id = row.getLong(2);
117 switch (updateIndex) {
119 update.setBoolean(1, true);
120 update.setString(2, rootUuid);
123 update.setLong(1, id);
126 throw new IllegalArgumentException("Unsupported update index " + updateIndex);
130 private static void cleanPermissionsOfPublicComponents(Context context) throws SQLException {
131 MassUpdate massUpdate = context.prepareMassUpdate();
132 massUpdate.select("select id from projects p where " +
134 " and p.qualifier in (?, ?)" +
135 " and p.private = ?" +
139 " from group_roles gr" +
141 " gr.resource_id = p.id" +
142 " and gr.role in (?, ?)" +
146 " from user_roles gr" +
148 " gr.resource_id = p.id" +
149 " and gr.role in (?, ?)" +
151 .setString(1, SCOPE_PROJECT)
152 .setString(2, QUALIFIER_PROJECT)
153 .setString(3, QUALIFIER_VIEW)
154 .setBoolean(4, false)
155 .setString(5, PERMISSION_USER)
156 .setString(6, PERMISSION_CODEVIEWER)
157 .setString(7, PERMISSION_USER)
158 .setString(8, PERMISSION_CODEVIEWER);
159 massUpdate.rowPluralName("public component trees to clean permissions of");
160 massUpdate.update("delete from group_roles where resource_id = ? and role in ('user', 'codeviewer')");
161 massUpdate.update("delete from user_roles where resource_id = ? and role in ('user', 'codeviewer')");
162 massUpdate.execute(MakeComponentsPrivateBasedOnPermissions::handleCleanPermissionsOfPublicComponents);
165 private static boolean handleCleanPermissionsOfPublicComponents(Select.Row row, SqlStatement update, int updateIndex) throws SQLException {
166 long id = row.getLong(1);
167 switch (updateIndex) {
170 update.setLong(1, id);
173 throw new IllegalArgumentException("Unsupported update index " + updateIndex);
177 private static void insertUserPermissionOfPrivateRootComponent(Context context, String permission) throws SQLException {
178 MassUpdate massUpdate = context.prepareMassUpdate();
179 massUpdate.select("select" +
180 " distinct r1.user_id, p.organization_uuid, p.id" +
183 " inner join projects p on" +
184 " p.id = r1.resource_id" +
186 " and p.qualifier in (?, ?)" +
187 " and p.private = ?" +
195 " r2.user_id = r1.user_id" +
196 " and r2.resource_id = r1.resource_id" +
199 .setString(1, SCOPE_PROJECT)
200 .setString(2, QUALIFIER_PROJECT)
201 .setString(3, QUALIFIER_VIEW)
203 .setString(5, permission);
204 massUpdate.rowPluralName("users of private component tree without " + permission + " permission");
205 massUpdate.update("insert into user_roles" +
206 " (organization_uuid, user_id, resource_id, role)" +
209 massUpdate.execute((row, update) -> insertUserPermission(row, update, permission));
212 private static boolean insertUserPermission(Select.Row row, SqlStatement update, String permission) throws SQLException {
213 int userId = row.getInt(1);
214 String organizationUuid = row.getString(2);
215 int resourceId = row.getInt(3);
217 update.setString(1, organizationUuid);
218 update.setInt(2, userId);
219 update.setInt(3, resourceId);
220 update.setString(4, permission);
224 private static void insertGroupPermissionOfPrivateRootComponent(Context context, String permission) throws SQLException {
225 MassUpdate massUpdate = context.prepareMassUpdate();
226 massUpdate.select("select" +
227 " distinct g1.group_id, p.organization_uuid, p.id" +
230 " inner join projects p on" +
231 " p.id = g1.resource_id" +
233 " and p.qualifier in (?, ?)" +
234 " and p.private = ?" +
236 " g1.group_id is not null" +
237 " and not exists (" +
243 " g2.group_id = g1.group_id" +
244 " and g2.resource_id = g1.resource_id" +
247 .setString(1, SCOPE_PROJECT)
248 .setString(2, QUALIFIER_PROJECT)
249 .setString(3, QUALIFIER_VIEW)
251 .setString(5, permission);
252 massUpdate.rowPluralName("groups of private component tree without " + permission + " permission");
253 massUpdate.update("insert into group_roles" +
254 " (organization_uuid, group_id, resource_id, role)" +
257 massUpdate.execute((row, update) -> insertGroupPermission(row, update, permission));
260 private static boolean insertGroupPermission(Select.Row row, SqlStatement update, String permission) throws SQLException {
261 int groupId = row.getInt(1);
262 String organizationUuid = row.getString(2);
263 int resourceId = row.getInt(3);
265 update.setString(1, organizationUuid);
266 update.setInt(2, groupId);
267 update.setInt(3, resourceId);
268 update.setString(4, permission);