3 * Copyright (C) 2009-2022 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.v00;
22 import java.sql.SQLException;
23 import java.util.Arrays;
24 import java.util.Date;
25 import java.util.List;
26 import org.sonar.api.SonarRuntime;
27 import org.sonar.api.utils.System2;
28 import org.sonar.core.util.UuidFactory;
29 import org.sonar.db.Database;
30 import org.sonar.server.platform.db.migration.step.DataChange;
31 import org.sonar.server.platform.db.migration.step.Upsert;
33 import static java.util.Arrays.stream;
34 import static java.util.Objects.requireNonNull;
35 import static java.util.stream.Collectors.joining;
36 import static java.util.stream.Stream.concat;
37 import static java.util.stream.Stream.of;
39 public class PopulateInitialSchema extends DataChange {
41 private static final String ADMINS_GROUP = "sonar-administrators";
42 private static final String USERS_GROUP = "sonar-users";
43 private static final String ADMIN_USER = "admin";
44 private static final String ADMIN_CRYPTED_PASSWORD = "$2a$12$uCkkXmhW5ThVK8mpBvnXOOJRLd64LJeHTeCkSuB3lfaR2N0AYBaSi";
45 private static final List<String> ADMIN_ROLES = Arrays.asList("admin", "profileadmin", "gateadmin", "provisioning", "applicationcreator", "portfoliocreator");
47 private final System2 system2;
48 private final UuidFactory uuidFactory;
49 private final SonarRuntime sonarRuntime;
51 public PopulateInitialSchema(Database db, System2 system2, UuidFactory uuidFactory, SonarRuntime sonarRuntime) {
53 this.system2 = system2;
54 this.uuidFactory = uuidFactory;
55 this.sonarRuntime = sonarRuntime;
59 public void execute(Context context) throws SQLException {
60 String adminUserUuid = insertAdminUser(context);
61 Groups groups = insertGroups(context);
62 String defaultQGUuid = insertQualityGate(context);
63 insertInternalProperty(context);
64 insertProperties(context, defaultQGUuid);
65 insertGroupRoles(context, groups);
66 insertGroupUsers(context, adminUserUuid, groups);
69 private String insertAdminUser(Context context) throws SQLException {
70 truncateTable(context, "users");
72 long now = system2.now();
73 context.prepareUpsert("insert into users " +
74 "(uuid, login, name, email, external_id, external_login, external_identity_provider, user_local, crypted_password, salt, hash_method, is_root, onboarded, reset_password, " +
75 "created_at, updated_at)" +
77 "(?, ?, 'Administrator', null, 'admin', 'admin', 'sonarqube', ?, ?, null, 'BCRYPT', ?, ?, ?, ?, ?)")
78 .setString(1, uuidFactory.create())
79 .setString(2, ADMIN_USER)
81 .setString(4, ADMIN_CRYPTED_PASSWORD)
90 String res = context.prepareSelect("select uuid from users where login=?")
91 .setString(1, ADMIN_USER)
92 .get(t -> t.getString(1));
93 return requireNonNull(res);
96 private void insertInternalProperty(Context context) throws SQLException {
97 String tableName = "internal_properties";
98 truncateTable(context, tableName);
100 long now = system2.now();
101 Upsert upsert = context.prepareUpsert(createInsertStatement(tableName, "kee", "is_empty", "text_value", "created_at"));
103 .setString(1, "installation.date")
104 .setBoolean(2, false)
105 .setString(3, String.valueOf(system2.now()))
109 .setString(1, "installation.version")
110 .setBoolean(2, false)
111 .setString(3, sonarRuntime.getApiVersion().toString())
119 private void insertProperties(Context context, String defaultQualityGate) throws SQLException {
120 var tableName = "properties";
121 truncateTable(context, tableName);
123 long now = system2.now();
125 .prepareUpsert(createInsertStatement(tableName, "uuid", "prop_key", "is_empty", "text_value", "created_at"));
127 upsert.setString(1, uuidFactory.create())
128 .setString(2, "sonar.forceAuthentication")
129 .setBoolean(3, false)
130 .setString(4, "true")
135 .setString(1, uuidFactory.create())
136 .setString(2, "projects.default.visibility")
137 .setBoolean(3, false)
138 .setString(4, "public")
139 .setLong(5, system2.now())
143 .setString(1, uuidFactory.create())
144 .setString(2, "qualitygate.default")
145 .setBoolean(3, false)
146 .setString(4, defaultQualityGate)
147 .setLong(5, system2.now())
155 private Groups insertGroups(Context context) throws SQLException {
156 truncateTable(context, "groups");
158 Date now = new Date(system2.now());
159 Upsert upsert = context.prepareUpsert(createInsertStatement(
161 "uuid", "name", "description", "created_at", "updated_at"));
163 .setString(1, uuidFactory.create())
164 .setString(2, ADMINS_GROUP)
165 .setString(3, "System administrators")
170 .setString(1, uuidFactory.create())
171 .setString(2, USERS_GROUP)
172 .setString(3, "Any new users created will automatically join this group")
180 return new Groups(getGroupUuid(context, ADMINS_GROUP), getGroupUuid(context, USERS_GROUP));
183 private static String getGroupUuid(Context context, String groupName) throws SQLException {
184 String res = context.prepareSelect("select uuid from groups where name=?")
185 .setString(1, groupName)
186 .get(t -> t.getString(1));
187 return requireNonNull(res);
190 private String insertQualityGate(Context context) throws SQLException {
191 truncateTable(context, "quality_gates");
193 String uuid = uuidFactory.create();
194 Date now = new Date(system2.now());
195 context.prepareUpsert(createInsertStatement("quality_gates", "uuid", "name", "is_built_in", "created_at", "updated_at"))
197 .setString(2, "Sonar way")
206 private static final class Groups {
207 private final String adminGroupUuid;
208 private final String userGroupUuid;
210 private Groups(String adminGroupUuid, String userGroupUuid) {
211 this.adminGroupUuid = adminGroupUuid;
212 this.userGroupUuid = userGroupUuid;
215 public String getAdminGroupUuid() {
216 return adminGroupUuid;
219 public String getUserGroupUuid() {
220 return userGroupUuid;
224 private void insertGroupRoles(Context context, Groups groups) throws SQLException {
225 truncateTable(context, "group_roles");
227 Upsert upsert = context.prepareUpsert(createInsertStatement("group_roles", "uuid","group_uuid", "role"));
228 for (String adminRole : ADMIN_ROLES) {
230 .setString(1, uuidFactory.create())
231 .setString(2, groups.getAdminGroupUuid())
232 .setString(3, adminRole)
235 for (String anyoneRole : Arrays.asList("scan", "provisioning")) {
237 .setString(1, uuidFactory.create())
239 .setString(3, anyoneRole)
247 private static void insertGroupUsers(Context context, String adminUserUuid, Groups groups) throws SQLException {
248 truncateTable(context, "groups_users");
250 Upsert upsert = context.prepareUpsert(createInsertStatement("groups_users", "user_uuid", "group_uuid"));
252 .setString(1, adminUserUuid)
253 .setString(2, groups.getUserGroupUuid())
256 .setString(1, adminUserUuid)
257 .setString(2, groups.getAdminGroupUuid())
264 private static void truncateTable(Context context, String table) throws SQLException {
265 context.prepareUpsert("truncate table " + table).execute().commit();
268 private static String createInsertStatement(String tableName, String firstColumn, String... otherColumns) {
269 return "insert into " + tableName + " " +
270 "(" + concat(of(firstColumn), stream(otherColumns)).collect(joining(",")) + ")" +
272 " (" + concat(of(firstColumn), stream(otherColumns)).map(t -> "?").collect(joining(",")) + ")";