]> source.dussan.org Git - sonarqube.git/blob
32aa0a5dac14c22ed425c008a7e78281e526095c
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2017 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
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.
10  *
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.
15  *
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.
19  */
20 package org.sonar.server.platform.db.migration.version.v64;
21
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;
28
29 /**
30  * This DB migration assumes the whole PROJECTS table contains only rows with private=false
31  * (set by {@link PopulateColumnProjectsPrivate}) and performs the following:
32  * <ul>
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>
39  * </ul>
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).
42  */
43 public class MakeComponentsPrivateBasedOnPermissions extends DataChange {
44
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";
50
51   public MakeComponentsPrivateBasedOnPermissions(Database db) {
52     super(db);
53   }
54
55   @Override
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);
63   }
64
65   private static void makePrivateComponent(Context context) throws SQLException {
66     MassUpdate massUpdate = context.prepareMassUpdate();
67     massUpdate.select("select uuid, id from projects p where " +
68       " p.scope = ?" +
69       " and p.qualifier in (?, ?)" +
70       " and p.private = ?" +
71       " and not exists (" +
72       "   select" +
73       "     1" +
74       "   from group_roles gr" +
75       "   where " +
76       "     gr.resource_id = p.id" +
77       "     and gr.group_id is null" +
78       "     and gr.role in (?, ?)" +
79       " )" +
80       // trees with only permissions to group must not be made private
81       " and (" +
82       "   exists (" +
83       "     select" +
84       "       1" +
85       "     from " +
86       "       group_roles gr2" +
87       "     where" +
88       "       gr2.resource_id = p.id" +
89       "       and gr2.group_id is not null" +
90       "    )" +
91       "    or exists (" +
92       "      select" +
93       "        1" +
94       "      from " +
95       "        user_roles ur" +
96       "      where" +
97       "        ur.resource_id = p.id" +
98       "    )" +
99       ")")
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);
112   }
113
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) {
118       case 0:
119         update.setBoolean(1, true);
120         update.setString(2, rootUuid);
121         return true;
122       case 1:
123         update.setLong(1, id);
124         return true;
125       default:
126         throw new IllegalArgumentException("Unsupported update index " + updateIndex);
127     }
128   }
129
130   private static void cleanPermissionsOfPublicComponents(Context context) throws SQLException {
131     MassUpdate massUpdate = context.prepareMassUpdate();
132     massUpdate.select("select id from projects p where " +
133       " p.scope = ?" +
134       " and p.qualifier in (?, ?)" +
135       " and p.private = ?" +
136       " and exists (" +
137       "   select" +
138       "     1" +
139       "   from group_roles gr" +
140       "   where " +
141       "     gr.resource_id = p.id" +
142       "     and gr.role in (?, ?)" +
143       "   union" +
144       "   select" +
145       "     1" +
146       "   from user_roles gr" +
147       "   where " +
148       "     gr.resource_id = p.id" +
149       "     and gr.role in (?, ?)" +
150       ")")
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);
163   }
164
165   private static boolean handleCleanPermissionsOfPublicComponents(Select.Row row, SqlStatement update, int updateIndex) throws SQLException {
166     long id = row.getLong(1);
167     switch (updateIndex) {
168       case 0:
169       case 1:
170         update.setLong(1, id);
171         return true;
172       default:
173         throw new IllegalArgumentException("Unsupported update index " + updateIndex);
174     }
175   }
176
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" +
181       " from" +
182       "   user_roles r1" +
183       " inner join projects p on" +
184       "   p.id = r1.resource_id" +
185       "   and p.scope = ?" +
186       "   and p.qualifier in (?, ?)" +
187       "   and p.private = ?" +
188       " where" +
189       "   not exists (" +
190       "     select" +
191       "       1" +
192       "     from" +
193       "       user_roles r2" +
194       "     where " +
195       "       r2.user_id = r1.user_id" +
196       "       and r2.resource_id = r1.resource_id" +
197       "       and r2.role = ?" +
198       ")")
199       .setString(1, SCOPE_PROJECT)
200       .setString(2, QUALIFIER_PROJECT)
201       .setString(3, QUALIFIER_VIEW)
202       .setBoolean(4, true)
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)" +
207       " values" +
208       " (?, ?, ?, ?)");
209     massUpdate.execute((row, update) -> insertUserPermission(row, update, permission));
210   }
211
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);
216
217     update.setString(1, organizationUuid);
218     update.setInt(2, userId);
219     update.setInt(3, resourceId);
220     update.setString(4, permission);
221     return true;
222   }
223
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" +
228       " from" +
229       "   group_roles g1" +
230       " inner join projects p on" +
231       "   p.id = g1.resource_id" +
232       "   and p.scope = ?" +
233       "   and p.qualifier in (?, ?)" +
234       "   and p.private = ?" +
235       " where" +
236       "   g1.group_id is not null" +
237       "   and not exists (" +
238       "     select" +
239       "       1" +
240       "     from" +
241       "       group_roles g2" +
242       "     where " +
243       "       g2.group_id = g1.group_id" +
244       "       and g2.resource_id = g1.resource_id" +
245       "       and g2.role = ?" +
246       ")")
247       .setString(1, SCOPE_PROJECT)
248       .setString(2, QUALIFIER_PROJECT)
249       .setString(3, QUALIFIER_VIEW)
250       .setBoolean(4, true)
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)" +
255       " values" +
256       " (?, ?, ?, ?)");
257     massUpdate.execute((row, update) -> insertGroupPermission(row, update, permission));
258   }
259
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);
264
265     update.setString(1, organizationUuid);
266     update.setInt(2, groupId);
267     update.setInt(3, resourceId);
268     update.setString(4, permission);
269     return true;
270   }
271 }