Skip to content

Commit

Permalink
Merge branch 'master' into autocomplete
Browse files Browse the repository at this point in the history
  • Loading branch information
timja authored Jul 15, 2024
2 parents 3ab76f1 + 678f1ab commit 92b0d6f
Show file tree
Hide file tree
Showing 83 changed files with 2,495 additions and 630 deletions.
2 changes: 1 addition & 1 deletion ath.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set -o xtrace
cd "$(dirname "$0")"

# https://github.com/jenkinsci/acceptance-test-harness/releases
export ATH_VERSION=5883.vdea_99c1762a_d
export ATH_VERSION=5895.v44475b_ca_0c78

if [[ $# -eq 0 ]]; then
export JDK=17
Expand Down
2 changes: 1 addition & 1 deletion bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ THE SOFTWARE.
<!-- provided by jcl-over-slf4j -->
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.3.2</version>
<version>1.3.3</version>
<scope>provided</scope>
</dependency>
</dependencies>
Expand Down
45 changes: 43 additions & 2 deletions core/src/main/java/hudson/model/AdministrativeMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,18 @@ public void doDisable(StaplerRequest req, StaplerResponse rsp) throws IOExceptio

/**
* Required permission to view this admin monitor.
* By default {@link Jenkins#ADMINISTER}, but {@link Jenkins#SYSTEM_READ} is also supported.
* By default {@link Jenkins#ADMINISTER}, but {@link Jenkins#SYSTEM_READ} or {@link Jenkins#MANAGE} are also supported.
* <p>
* Changing this permission check to return {@link Jenkins#SYSTEM_READ} will make the active
* administrative monitor appear on {@code manage.jelly} and on the globally visible
* {@link jenkins.management.AdministrativeMonitorsDecorator} to users without Administer permission.
* {@link #doDisable(StaplerRequest, StaplerResponse)} will still always require Administer permission.
* </p>
* <p>
* This method only allows for a single permission to be returned. If more complex permission checks are required,
* override {@link #checkRequiredPermission()} and {@link #hasRequiredPermission()} instead.
* </p>
* <p>
* Implementers need to ensure that {@code doAct} and other web methods perform necessary permission checks:
* Users with System Read permissions are expected to be limited to read-only access.
* Form UI elements that change system state, e.g. toggling a feature on or off, need to be hidden from users
Expand All @@ -201,13 +205,50 @@ public Permission getRequiredPermission() {
return Jenkins.ADMINISTER;
}

/**
* Checks if the current user has the minimum required permission to view this administrative monitor.
* <p>
* Subclasses may override this method and {@link #hasRequiredPermission()} instead of {@link #getRequiredPermission()} to perform more complex permission checks,
* for example, checking either {@link Jenkins#MANAGE} or {@link Jenkins#SYSTEM_READ}.
* </p>
* @see #getRequiredPermission()
* @see #hasRequiredPermission()
*/
public void checkRequiredPermission() {
Jenkins.get().checkPermission(getRequiredPermission());
}

/**
* Checks if the current user has the minimum required permission to view this administrative monitor.
* <p>
* Subclasses may override this method and {@link #checkRequiredPermission} instead of {@link #getRequiredPermission()} to perform more complex permission checks,
* for example, checking either {@link Jenkins#MANAGE} or {@link Jenkins#SYSTEM_READ}.
* </p>
* @see #getRequiredPermission()
* @see #checkRequiredPermission()
*/
public boolean hasRequiredPermission() {
return Jenkins.get().hasPermission(getRequiredPermission());
}

/**
* Checks if the current user has the minimum required permission to view any administrative monitor.
*
* @return true if the current user has the minimum required permission to view any administrative monitor.
*
* @since TODO
*/
public static boolean hasPermissionToDisplay() {
return Jenkins.get().hasAnyPermission(Jenkins.SYSTEM_READ, Jenkins.MANAGE);
}

/**
* Ensure that URLs in this administrative monitor are only accessible to users with {@link #getRequiredPermission()}.
*/
@Override
@Restricted(NoExternalUse.class)
public Object getTarget() {
Jenkins.get().checkPermission(getRequiredPermission());
checkRequiredPermission();
return this;
}

Expand Down
6 changes: 6 additions & 0 deletions core/src/main/java/hudson/model/MyViewsProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import hudson.Extension;
import hudson.Util;
import hudson.model.Descriptor.FormException;
import hudson.model.userproperty.UserPropertyCategory;
import hudson.security.ACL;
import hudson.util.FormValidation;
import hudson.views.MyViewsTabBar;
Expand Down Expand Up @@ -246,6 +247,11 @@ public String getDisplayName() {
public UserProperty newInstance(User user) {
return new MyViewsProperty();
}

@Override
public @NonNull UserPropertyCategory getUserPropertyCategory() {
return UserPropertyCategory.get(UserPropertyCategory.Preferences.class);
}
}

@Override
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/java/hudson/model/PaneStatusProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import static java.lang.String.format;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.model.userproperty.UserPropertyCategory;
import hudson.util.PersistedList;
import java.io.IOException;
import javax.servlet.http.HttpSession;
Expand Down Expand Up @@ -56,6 +58,10 @@ public boolean isEnabled() {
return false;
}

@Override
public @NonNull UserPropertyCategory getUserPropertyCategory() {
return UserPropertyCategory.get(UserPropertyCategory.Invisible.class);
}
}

private static class PaneStatusPropertiesSessionFallback extends PaneStatusProperties {
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/java/hudson/model/TimeZoneProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.Util;
import hudson.model.userproperty.UserPropertyCategory;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
import hudson.util.ListBoxModel.Option;
Expand Down Expand Up @@ -106,6 +107,10 @@ public FormValidation doCheckTimeZoneName(@QueryParameter String timeZoneName) {
}
}

@Override
public @NonNull UserPropertyCategory getUserPropertyCategory() {
return UserPropertyCategory.get(UserPropertyCategory.Account.class);
}
}

@CheckForNull
Expand Down
69 changes: 23 additions & 46 deletions core/src/main/java/hudson/model/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,11 @@
import hudson.XmlFile;
import hudson.init.InitMilestone;
import hudson.init.Initializer;
import hudson.model.Descriptor.FormException;
import hudson.model.listeners.SaveableListener;
import hudson.security.ACL;
import hudson.security.AccessControlled;
import hudson.security.SecurityRealm;
import hudson.security.UserMayOrMayNotExistException2;
import hudson.util.FormApply;
import hudson.util.FormValidation;
import hudson.util.RunList;
import hudson.util.XStream2;
Expand Down Expand Up @@ -77,7 +75,6 @@
import jenkins.security.LastGrantedAuthoritiesProperty;
import jenkins.security.UserDetailsCache;
import jenkins.util.SystemProperties;
import net.sf.json.JSONObject;
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
Expand All @@ -87,7 +84,6 @@
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.interceptor.RequirePOST;
import org.kohsuke.stapler.verb.POST;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
Expand Down Expand Up @@ -342,6 +338,29 @@ public synchronized void addProperty(@NonNull UserProperty p) throws IOException
save();
}

/**
* Expand {@link #addProperty(UserProperty)} for multiple properties to be done at once.
* Expected to be used by the categorized configuration pages to update part of the properties.
* The properties not included in the list will be let untouched.
* It will call the {@link UserProperty#setUser(User)} method and at the end, {@link #save()} once.
*
* @since TODO
*/
public synchronized void addProperties(@NonNull List<UserProperty> multipleProperties) throws IOException {
List<UserProperty> newProperties = new ArrayList<>(this.properties);
for (UserProperty property : multipleProperties) {
UserProperty oldProp = getProperty(property.getClass());
if (oldProp != null) {
newProperties.remove(oldProp);
}
newProperties.add(property);
property.setUser(this);
}

this.properties = newProperties;
this.save();
}

/**
* List of all {@link UserProperty}s exposed primarily for the remoting API.
*/
Expand Down Expand Up @@ -859,48 +878,6 @@ public Api getApi() {
return new Api(this);
}

/**
* Accepts submission from the configuration page.
*/
@POST
public void doConfigSubmit(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, FormException {
checkPermission(Jenkins.ADMINISTER);

JSONObject json = req.getSubmittedForm();
String oldFullName = this.fullName;
fullName = json.getString("fullName");
description = json.getString("description");

List<UserProperty> props = new ArrayList<>();
int i = 0;
for (UserPropertyDescriptor d : UserProperty.all()) {
UserProperty p = getProperty(d.clazz);

JSONObject o = json.optJSONObject("userProperty" + i++);
if (o != null) {
if (p != null) {
p = p.reconfigure(req, o);
} else {
p = d.newInstance(req, o);
}
}

if (p != null) {
p.setUser(this);
props.add(p);
}
}
this.properties = props;

save();

if (oldFullName != null && !oldFullName.equals(this.fullName)) {
UserDetailsCache.get().invalidate(oldFullName);
}

FormApply.success(".").generateResponse(req, rsp, this);
}

/**
* Deletes this user from Hudson.
*/
Expand Down
26 changes: 26 additions & 0 deletions core/src/main/java/hudson/model/UserProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@

package hudson.model;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.DescriptorExtensionList;
import hudson.ExtensionPoint;
import hudson.model.Descriptor.FormException;
import hudson.model.userproperty.UserPropertyCategory;
import java.util.ArrayList;
import java.util.List;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
Expand Down Expand Up @@ -58,6 +62,10 @@ public abstract class UserProperty implements ReconfigurableDescribable<UserProp
*/
protected transient User user;

/**
* This method is used to inform the property about its owner.
* It could be called multiple times, even without change, thus it should be idempotent.
*/
protected void setUser(User u) {
this.user = u;
}
Expand All @@ -75,6 +83,24 @@ public static DescriptorExtensionList<UserProperty, UserPropertyDescriptor> all(
return Jenkins.get().getDescriptorList(UserProperty.class);
}

/**
* Returns all the registered {@link UserPropertyCategory} descriptors for a given category.
*
* @since TODO
*/
public static List<UserPropertyDescriptor> allByCategoryClass(@NonNull Class<? extends UserPropertyCategory> categoryClass) {
DescriptorExtensionList<UserProperty, UserPropertyDescriptor> all = all();

List<UserPropertyDescriptor> onlyForTheCategory = new ArrayList<>(all.size());
for (UserPropertyDescriptor descriptor : all) {
if (descriptor.getUserPropertyCategory().getClass().equals(categoryClass)) {
onlyForTheCategory.add(descriptor);
}
}

return onlyForTheCategory;
}

@Override
public UserProperty reconfigure(StaplerRequest req, JSONObject form) throws FormException {
return form == null ? null : getDescriptor().newInstance(req, form);
Expand Down
53 changes: 53 additions & 0 deletions core/src/main/java/hudson/model/UserPropertyDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@

package hudson.model;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.model.userproperty.UserPropertyCategory;
import java.util.Optional;
import org.jenkinsci.Symbol;

/**
* {@link Descriptor} for {@link UserProperty}.
*
Expand Down Expand Up @@ -73,4 +79,51 @@ protected UserPropertyDescriptor() {
public boolean isEnabled() {
return true;
}

/**
* Define the category for this user property descriptor.
*
* @return never null, always the same value for a given instance of {@link Descriptor}.
*
* @since TODO
*/
public @NonNull UserPropertyCategory getUserPropertyCategory() {
// As this method is expected to be overloaded by subclasses
// the logic here is just done to support plugins with older core version
String categoryAsString = this.getUserPropertyCategoryAsString();
if (categoryAsString != null) {
Optional<UserPropertyCategory> firstIfFound = UserPropertyCategory.all().stream()
.filter(cat -> {
Symbol symbolAnnotation = cat.getClass().getAnnotation(Symbol.class);
if (symbolAnnotation != null) {
for (String symbolValue : symbolAnnotation.value()) {
if (symbolValue.equalsIgnoreCase(categoryAsString)) {
return true;
}
}
}
return false;
})
.findFirst();
if (firstIfFound.isPresent()) {
return firstIfFound.get();
}
}
return UserPropertyCategory.get(UserPropertyCategory.Unclassified.class);
}

/**
* Method proposed to prevent plugins to rely on too recent core version
* while keeping the possibility to use the categories.
*
* @deprecated This should only be used when the core requirement is below the version this method was added
*
* @return String name corresponding to the symbol of {@link #getUserPropertyCategory()}
*
* @since TODO
*/
@Deprecated
protected @CheckForNull String getUserPropertyCategoryAsString() {
return null;
}
}
Loading

0 comments on commit 92b0d6f

Please sign in to comment.