Line data Source code
1 : // Copyright (C) 2015 The Android Open Source Project
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : package com.google.gerrit.server.restapi.config;
16 :
17 : import static java.util.stream.Collectors.toList;
18 :
19 : import com.google.common.base.CharMatcher;
20 : import com.google.common.base.Strings;
21 : import com.google.common.collect.Lists;
22 : import com.google.gerrit.common.Nullable;
23 : import com.google.gerrit.entities.ContributorAgreement;
24 : import com.google.gerrit.extensions.common.AccountDefaultDisplayName;
25 : import com.google.gerrit.extensions.common.AccountsInfo;
26 : import com.google.gerrit.extensions.common.AuthInfo;
27 : import com.google.gerrit.extensions.common.ChangeConfigInfo;
28 : import com.google.gerrit.extensions.common.DownloadInfo;
29 : import com.google.gerrit.extensions.common.DownloadSchemeInfo;
30 : import com.google.gerrit.extensions.common.GerritInfo;
31 : import com.google.gerrit.extensions.common.PluginConfigInfo;
32 : import com.google.gerrit.extensions.common.ReceiveInfo;
33 : import com.google.gerrit.extensions.common.ServerInfo;
34 : import com.google.gerrit.extensions.common.SshdInfo;
35 : import com.google.gerrit.extensions.common.SuggestInfo;
36 : import com.google.gerrit.extensions.common.UserConfigInfo;
37 : import com.google.gerrit.extensions.config.CloneCommand;
38 : import com.google.gerrit.extensions.config.DownloadCommand;
39 : import com.google.gerrit.extensions.config.DownloadScheme;
40 : import com.google.gerrit.extensions.restapi.Response;
41 : import com.google.gerrit.extensions.restapi.RestReadView;
42 : import com.google.gerrit.extensions.webui.WebUiPlugin;
43 : import com.google.gerrit.server.EnableSignedPush;
44 : import com.google.gerrit.server.account.AccountVisibilityProvider;
45 : import com.google.gerrit.server.account.Realm;
46 : import com.google.gerrit.server.avatar.AvatarProvider;
47 : import com.google.gerrit.server.change.ArchiveFormatInternal;
48 : import com.google.gerrit.server.change.MergeabilityComputationBehavior;
49 : import com.google.gerrit.server.config.AllProjectsName;
50 : import com.google.gerrit.server.config.AllUsersName;
51 : import com.google.gerrit.server.config.AnonymousCowardName;
52 : import com.google.gerrit.server.config.AuthConfig;
53 : import com.google.gerrit.server.config.ConfigResource;
54 : import com.google.gerrit.server.config.ConfigUtil;
55 : import com.google.gerrit.server.config.GerritServerConfig;
56 : import com.google.gerrit.server.config.SitePaths;
57 : import com.google.gerrit.server.documentation.QueryDocumentationExecutor;
58 : import com.google.gerrit.server.index.change.ChangeField;
59 : import com.google.gerrit.server.index.change.ChangeIndexCollection;
60 : import com.google.gerrit.server.permissions.PermissionBackendException;
61 : import com.google.gerrit.server.plugincontext.PluginItemContext;
62 : import com.google.gerrit.server.plugincontext.PluginMapContext;
63 : import com.google.gerrit.server.plugincontext.PluginSetContext;
64 : import com.google.gerrit.server.project.ProjectCache;
65 : import com.google.gerrit.server.restapi.change.AllowedFormats;
66 : import com.google.gerrit.server.submit.MergeSuperSet;
67 : import com.google.inject.Inject;
68 : import java.nio.file.Files;
69 : import java.util.ArrayList;
70 : import java.util.Arrays;
71 : import java.util.Collection;
72 : import java.util.HashMap;
73 : import java.util.List;
74 : import java.util.concurrent.TimeUnit;
75 : import org.eclipse.jgit.lib.Config;
76 :
77 : public class GetServerInfo implements RestReadView<ConfigResource> {
78 : private final Config config;
79 : private final AccountVisibilityProvider accountVisibilityProvider;
80 : private final AccountDefaultDisplayName accountDefaultDisplayName;
81 : private final AuthConfig authConfig;
82 : private final Realm realm;
83 : private final PluginMapContext<DownloadScheme> downloadSchemes;
84 : private final PluginMapContext<DownloadCommand> downloadCommands;
85 : private final PluginMapContext<CloneCommand> cloneCommands;
86 : private final PluginSetContext<WebUiPlugin> plugins;
87 : private final AllowedFormats archiveFormats;
88 : private final AllProjectsName allProjectsName;
89 : private final AllUsersName allUsersName;
90 : private final String anonymousCowardName;
91 : private final PluginItemContext<AvatarProvider> avatar;
92 : private final boolean enableSignedPush;
93 : private final QueryDocumentationExecutor docSearcher;
94 : private final ProjectCache projectCache;
95 : private final AgreementJson agreementJson;
96 : private final ChangeIndexCollection indexes;
97 : private final SitePaths sitePaths;
98 :
99 : @Inject
100 : public GetServerInfo(
101 : @GerritServerConfig Config config,
102 : AccountVisibilityProvider accountVisibilityProvider,
103 : AccountDefaultDisplayName accountDefaultDisplayName,
104 : AuthConfig authConfig,
105 : Realm realm,
106 : PluginMapContext<DownloadScheme> downloadSchemes,
107 : PluginMapContext<DownloadCommand> downloadCommands,
108 : PluginMapContext<CloneCommand> cloneCommands,
109 : PluginSetContext<WebUiPlugin> webUiPlugins,
110 : AllowedFormats archiveFormats,
111 : AllProjectsName allProjectsName,
112 : AllUsersName allUsersName,
113 : @AnonymousCowardName String anonymousCowardName,
114 : PluginItemContext<AvatarProvider> avatar,
115 : @EnableSignedPush boolean enableSignedPush,
116 : QueryDocumentationExecutor docSearcher,
117 : ProjectCache projectCache,
118 : AgreementJson agreementJson,
119 : ChangeIndexCollection indexes,
120 149 : SitePaths sitePaths) {
121 149 : this.config = config;
122 149 : this.accountVisibilityProvider = accountVisibilityProvider;
123 149 : this.accountDefaultDisplayName = accountDefaultDisplayName;
124 149 : this.authConfig = authConfig;
125 149 : this.realm = realm;
126 149 : this.downloadSchemes = downloadSchemes;
127 149 : this.downloadCommands = downloadCommands;
128 149 : this.cloneCommands = cloneCommands;
129 149 : this.plugins = webUiPlugins;
130 149 : this.archiveFormats = archiveFormats;
131 149 : this.allProjectsName = allProjectsName;
132 149 : this.allUsersName = allUsersName;
133 149 : this.anonymousCowardName = anonymousCowardName;
134 149 : this.avatar = avatar;
135 149 : this.enableSignedPush = enableSignedPush;
136 149 : this.docSearcher = docSearcher;
137 149 : this.projectCache = projectCache;
138 149 : this.agreementJson = agreementJson;
139 149 : this.indexes = indexes;
140 149 : this.sitePaths = sitePaths;
141 149 : }
142 :
143 : @Override
144 : public Response<ServerInfo> apply(ConfigResource rsrc) throws PermissionBackendException {
145 3 : ServerInfo info = new ServerInfo();
146 3 : info.accounts = getAccountsInfo();
147 3 : info.auth = getAuthInfo();
148 3 : info.change = getChangeInfo();
149 3 : info.download = getDownloadInfo();
150 3 : info.gerrit = getGerritInfo();
151 3 : info.noteDbEnabled = true;
152 3 : info.plugin = getPluginInfo();
153 3 : info.defaultTheme = getDefaultTheme();
154 3 : info.sshd = getSshdInfo();
155 3 : info.suggest = getSuggestInfo();
156 :
157 3 : info.user = getUserInfo();
158 3 : info.receive = getReceiveInfo();
159 3 : info.submitRequirementDashboardColumns = getSubmitRequirementDashboardColumns();
160 3 : return Response.ok(info);
161 : }
162 :
163 : private AccountsInfo getAccountsInfo() {
164 3 : AccountsInfo info = new AccountsInfo();
165 3 : info.visibility = accountVisibilityProvider.get();
166 3 : info.defaultDisplayName = accountDefaultDisplayName;
167 3 : return info;
168 : }
169 :
170 : private AuthInfo getAuthInfo() throws PermissionBackendException {
171 3 : AuthInfo info = new AuthInfo();
172 3 : info.authType = authConfig.getAuthType();
173 3 : info.useContributorAgreements = toBoolean(authConfig.isUseContributorAgreements());
174 3 : info.editableAccountFields = new ArrayList<>(realm.getEditableFields());
175 3 : info.switchAccountUrl = authConfig.getSwitchAccountUrl();
176 3 : info.gitBasicAuthPolicy = authConfig.getGitBasicAuthPolicy();
177 :
178 3 : if (info.useContributorAgreements != null) {
179 2 : Collection<ContributorAgreement> agreements =
180 2 : projectCache.getAllProjects().getConfig().getContributorAgreements().values();
181 2 : if (!agreements.isEmpty()) {
182 1 : info.contributorAgreements = Lists.newArrayListWithCapacity(agreements.size());
183 1 : for (ContributorAgreement agreement : agreements) {
184 1 : info.contributorAgreements.add(agreementJson.format(agreement));
185 1 : }
186 : }
187 : }
188 :
189 3 : switch (info.authType) {
190 : case LDAP:
191 : case LDAP_BIND:
192 0 : info.registerUrl = authConfig.getRegisterUrl();
193 0 : info.registerText = authConfig.getRegisterText();
194 0 : info.editFullNameUrl = authConfig.getEditFullNameUrl();
195 0 : break;
196 :
197 : case CUSTOM_EXTENSION:
198 0 : info.registerUrl = authConfig.getRegisterUrl();
199 0 : info.registerText = authConfig.getRegisterText();
200 0 : info.editFullNameUrl = authConfig.getEditFullNameUrl();
201 0 : info.httpPasswordUrl = authConfig.getHttpPasswordUrl();
202 0 : break;
203 :
204 : case HTTP:
205 : case HTTP_LDAP:
206 1 : info.loginUrl = authConfig.getLoginUrl();
207 1 : info.loginText = authConfig.getLoginText();
208 1 : break;
209 :
210 : case CLIENT_SSL_CERT_LDAP:
211 : case DEVELOPMENT_BECOME_ANY_ACCOUNT:
212 : case OAUTH:
213 : case OPENID:
214 : case OPENID_SSO:
215 : break;
216 : }
217 3 : return info;
218 : }
219 :
220 : private ChangeConfigInfo getChangeInfo() {
221 3 : ChangeConfigInfo info = new ChangeConfigInfo();
222 3 : info.allowBlame = toBoolean(config.getBoolean("change", "allowBlame", true));
223 3 : boolean hasAssigneeInIndex =
224 3 : indexes.getSearchIndex().getSchema().hasField(ChangeField.ASSIGNEE_SPEC);
225 3 : info.showAssigneeInChangesTable =
226 3 : toBoolean(
227 3 : config.getBoolean("change", "showAssigneeInChangesTable", false) && hasAssigneeInIndex);
228 3 : info.updateDelay =
229 3 : (int) ConfigUtil.getTimeUnit(config, "change", null, "updateDelay", 300, TimeUnit.SECONDS);
230 3 : info.submitWholeTopic = toBoolean(MergeSuperSet.wholeTopicEnabled(config));
231 3 : info.disablePrivateChanges =
232 3 : toBoolean(this.config.getBoolean("change", null, "disablePrivateChanges", false));
233 3 : info.mergeabilityComputationBehavior =
234 3 : MergeabilityComputationBehavior.fromConfig(config).name();
235 3 : info.enableAttentionSet =
236 3 : toBoolean(this.config.getBoolean("change", null, "enableAttentionSet", true));
237 3 : info.enableAssignee =
238 3 : toBoolean(this.config.getBoolean("change", null, "enableAssignee", false));
239 3 : return info;
240 : }
241 :
242 : private DownloadInfo getDownloadInfo() {
243 3 : DownloadInfo info = new DownloadInfo();
244 3 : info.schemes = new HashMap<>();
245 3 : downloadSchemes.runEach(
246 : extension -> {
247 0 : DownloadScheme scheme = extension.get();
248 0 : if (scheme.isEnabled() && scheme.getUrl("${project}") != null) {
249 0 : info.schemes.put(extension.getExportName(), getDownloadSchemeInfo(scheme));
250 : }
251 0 : });
252 3 : info.archives =
253 3 : archiveFormats.getAllowed().stream()
254 3 : .map(ArchiveFormatInternal::getShortName)
255 3 : .collect(toList());
256 3 : return info;
257 : }
258 :
259 : private DownloadSchemeInfo getDownloadSchemeInfo(DownloadScheme scheme) {
260 0 : DownloadSchemeInfo info = new DownloadSchemeInfo();
261 0 : info.url = scheme.getUrl("${project}");
262 0 : info.isAuthRequired = toBoolean(scheme.isAuthRequired());
263 0 : info.isAuthSupported = toBoolean(scheme.isAuthSupported());
264 :
265 0 : info.commands = new HashMap<>();
266 0 : downloadCommands.runEach(
267 : extension -> {
268 0 : String commandName = extension.getExportName();
269 0 : DownloadCommand command = extension.get();
270 0 : String c = command.getCommand(scheme, "${project}", "${ref}");
271 0 : if (c != null) {
272 0 : info.commands.put(commandName, c);
273 : }
274 0 : });
275 :
276 0 : info.cloneCommands = new HashMap<>();
277 0 : cloneCommands.runEach(
278 : extension -> {
279 0 : String commandName = extension.getExportName();
280 0 : CloneCommand command = extension.getProvider().get();
281 0 : String c = command.getCommand(scheme, "${project-path}/${project-base-name}");
282 0 : if (c != null) {
283 0 : c =
284 0 : c.replaceAll(
285 : "\\$\\{project-path\\}/\\$\\{project-base-name\\}", "\\$\\{project\\}");
286 0 : info.cloneCommands.put(commandName, c);
287 : }
288 0 : });
289 :
290 0 : return info;
291 : }
292 :
293 : private GerritInfo getGerritInfo() {
294 3 : GerritInfo info = new GerritInfo();
295 3 : info.allProjects = allProjectsName.get();
296 3 : info.allUsers = allUsersName.get();
297 3 : info.reportBugUrl = config.getString("gerrit", null, "reportBugUrl");
298 3 : info.docUrl = getDocUrl();
299 3 : info.docSearch = docSearcher.isAvailable();
300 3 : info.editGpgKeys =
301 3 : toBoolean(enableSignedPush && config.getBoolean("gerrit", null, "editGpgKeys", true));
302 3 : info.primaryWeblinkName = config.getString("gerrit", null, "primaryWeblinkName");
303 3 : info.instanceId = config.getString("gerrit", null, "instanceId");
304 3 : return info;
305 : }
306 :
307 : @Nullable
308 : private String getDocUrl() {
309 3 : String docUrl = config.getString("gerrit", null, "docUrl");
310 3 : if (Strings.isNullOrEmpty(docUrl)) {
311 3 : return null;
312 : }
313 0 : return CharMatcher.is('/').trimTrailingFrom(docUrl) + '/';
314 : }
315 :
316 : private PluginConfigInfo getPluginInfo() {
317 3 : PluginConfigInfo info = new PluginConfigInfo();
318 3 : info.hasAvatars = toBoolean(avatar.hasImplementation());
319 3 : info.jsResourcePaths = new ArrayList<>();
320 3 : plugins.runEach(
321 : plugin -> {
322 1 : String path =
323 1 : String.format(
324 1 : "plugins/%s/%s", plugin.getPluginName(), plugin.getJavaScriptResourcePath());
325 1 : info.jsResourcePaths.add(path);
326 1 : });
327 3 : return info;
328 : }
329 :
330 : private static final String DEFAULT_THEME = "/static/" + SitePaths.THEME_FILENAME;
331 : private static final String DEFAULT_THEME_JS = "/static/" + SitePaths.THEME_JS_FILENAME;
332 :
333 : @Nullable
334 : private String getDefaultTheme() {
335 3 : if (config.getString("theme", null, "enableDefault") == null) {
336 : // If not explicitly enabled or disabled, check for the existence of the theme file.
337 3 : return Files.exists(sitePaths.site_theme_js)
338 0 : ? DEFAULT_THEME_JS
339 3 : : Files.exists(sitePaths.site_theme) ? DEFAULT_THEME : null;
340 : }
341 0 : if (config.getBoolean("theme", null, "enableDefault", true)) {
342 : // Return non-null theme path without checking for file existence. Even if the file doesn't
343 : // exist under the site path, it may be served from a CDN (in which case it's up to the admin
344 : // to also pass a proper asset path to the index Soy template).
345 0 : return DEFAULT_THEME_JS;
346 : }
347 0 : return null;
348 : }
349 :
350 : @Nullable
351 : private SshdInfo getSshdInfo() {
352 3 : String[] addr = config.getStringList("sshd", null, "listenAddress");
353 3 : if (addr.length == 1 && isOff(addr[0])) {
354 2 : return null;
355 : }
356 1 : return new SshdInfo();
357 : }
358 :
359 : private static boolean isOff(String listenHostname) {
360 3 : return "off".equalsIgnoreCase(listenHostname)
361 1 : || "none".equalsIgnoreCase(listenHostname)
362 3 : || "no".equalsIgnoreCase(listenHostname);
363 : }
364 :
365 : private SuggestInfo getSuggestInfo() {
366 3 : SuggestInfo info = new SuggestInfo();
367 3 : info.from = config.getInt("suggest", "from", 0);
368 3 : return info;
369 : }
370 :
371 : private UserConfigInfo getUserInfo() {
372 3 : UserConfigInfo info = new UserConfigInfo();
373 3 : info.anonymousCowardName = anonymousCowardName;
374 3 : return info;
375 : }
376 :
377 : private ReceiveInfo getReceiveInfo() {
378 3 : ReceiveInfo info = new ReceiveInfo();
379 3 : info.enableSignedPush = enableSignedPush;
380 3 : return info;
381 : }
382 :
383 : private List<String> getSubmitRequirementDashboardColumns() {
384 3 : return Arrays.asList(config.getStringList("dashboard", null, "submitRequirementColumns"));
385 : }
386 :
387 : @Nullable
388 : private static Boolean toBoolean(boolean v) {
389 3 : return v ? Boolean.TRUE : null;
390 : }
391 : }
|