diff --git a/api/src/main/java/run/halo/app/plugin/BasePlugin.java b/api/src/main/java/run/halo/app/plugin/BasePlugin.java
index f513514fec..d010998c56 100644
--- a/api/src/main/java/run/halo/app/plugin/BasePlugin.java
+++ b/api/src/main/java/run/halo/app/plugin/BasePlugin.java
@@ -3,6 +3,8 @@
import lombok.extern.slf4j.Slf4j;
import org.pf4j.Plugin;
import org.pf4j.PluginWrapper;
+import org.springframework.lang.NonNull;
+import org.springframework.util.Assert;
/**
* This class will be extended by all plugins and serve as the common class between a plugin and
@@ -14,12 +16,46 @@
@Slf4j
public class BasePlugin extends Plugin {
+ protected PluginContext context;
+
@Deprecated
public BasePlugin(PluginWrapper wrapper) {
super(wrapper);
log.info("Initialized plugin {}", wrapper.getPluginId());
}
+ /**
+ * Constructor a plugin with the given plugin context.
+ * TODO Mark {@link PluginContext} as final to prevent modification.
+ *
+ * @param pluginContext plugin context must not be null.
+ */
+ public BasePlugin(PluginContext pluginContext) {
+ this.context = pluginContext;
+ }
+
+ /**
+ * use {@link #BasePlugin(PluginContext)} instead of.
+ *
+ * @deprecated since 2.10.0
+ */
public BasePlugin() {
}
+
+ /**
+ * Compatible with old constructors, if the plugin is not use
+ * {@link #BasePlugin(PluginContext)} constructor then base plugin factory will use this
+ * method to set plugin context.
+ *
+ * @param context plugin context must not be null.
+ */
+ final void setContext(PluginContext context) {
+ Assert.notNull(context, "Plugin context must not be null");
+ this.context = context;
+ }
+
+ @NonNull
+ public PluginContext getContext() {
+ return context;
+ }
}
diff --git a/api/src/main/java/run/halo/app/plugin/PluginContext.java b/api/src/main/java/run/halo/app/plugin/PluginContext.java
new file mode 100644
index 0000000000..01bae2f967
--- /dev/null
+++ b/api/src/main/java/run/halo/app/plugin/PluginContext.java
@@ -0,0 +1,27 @@
+package run.halo.app.plugin;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.pf4j.RuntimeMode;
+
+/**
+ *
This class will provide a context for the plugin, which will be used to store some
+ * information about the plugin.
+ * An instance of this class is provided to plugins in their constructor.
+ * It's safe for plugins to keep a reference to the instance for later use.
+ * This class facilitates communication with application and plugin manager.
+ * Pf4j recommends that you use a custom PluginContext instead of PluginWrapper.
+ * Use application custom PluginContext instead of PluginWrapper
+ *
+ * @author guqing
+ * @since 2.10.0
+ */
+@Getter
+@RequiredArgsConstructor
+public class PluginContext {
+ private final String name;
+
+ private final String version;
+
+ private final RuntimeMode runtimeMode;
+}
diff --git a/application/build.gradle b/application/build.gradle
index 9eba09bcfa..c64abd253d 100644
--- a/application/build.gradle
+++ b/application/build.gradle
@@ -1,5 +1,5 @@
plugins {
- id 'org.springframework.boot' version '3.1.3'
+ id 'org.springframework.boot' version '3.1.4'
id 'io.spring.dependency-management' version '1.1.0'
id "com.gorylenko.gradle-git-properties" version "2.3.2"
id "checkstyle"
@@ -74,10 +74,10 @@ tasks.register('downloadPluginPresets', Download) {
delete 'src/main/resources/presets/plugins'
}
src([
- 'https://github.com/halo-dev/plugin-comment-widget/releases/download/v1.7.0/plugin-comment-widget-1.7.0.jar',
- 'https://github.com/halo-dev/plugin-search-widget/releases/download/v1.1.0/plugin-search-widget-1.1.0.jar',
- 'https://github.com/halo-dev/plugin-sitemap/releases/download/v1.0.2/plugin-sitemap-1.0.2.jar',
- 'https://github.com/halo-dev/plugin-feed/releases/download/v1.1.1/plugin-feed-1.1.1.jar'
+ 'https://github.com/halo-dev/plugin-comment-widget/releases/download/v1.8.0/plugin-comment-widget-1.8.0.jar',
+ 'https://github.com/halo-dev/plugin-search-widget/releases/download/v1.2.0/plugin-search-widget-1.2.0.jar',
+ 'https://github.com/halo-dev/plugin-sitemap/releases/download/v1.1.1/plugin-sitemap-1.1.1.jar',
+ 'https://github.com/halo-dev/plugin-feed/releases/download/v1.2.0/plugin-feed-1.2.0.jar'
])
dest 'src/main/resources/presets/plugins'
}
diff --git a/application/src/main/java/run/halo/app/core/extension/reconciler/PluginReconciler.java b/application/src/main/java/run/halo/app/core/extension/reconciler/PluginReconciler.java
index 92d0750bf7..5361b08a8f 100644
--- a/application/src/main/java/run/halo/app/core/extension/reconciler/PluginReconciler.java
+++ b/application/src/main/java/run/halo/app/core/extension/reconciler/PluginReconciler.java
@@ -60,6 +60,7 @@
import run.halo.app.infra.utils.PathUtils;
import run.halo.app.infra.utils.YamlUnstructuredLoader;
import run.halo.app.plugin.HaloPluginManager;
+import run.halo.app.plugin.HaloPluginWrapper;
import run.halo.app.plugin.PluginConst;
import run.halo.app.plugin.PluginExtensionLoaderUtils;
import run.halo.app.plugin.PluginStartingError;
@@ -184,11 +185,12 @@ Optional lookupPluginSetting(String name, String settingName) {
Assert.notNull(name, "Plugin name must not be null");
Assert.notNull(settingName, "Setting name must not be null");
PluginWrapper pluginWrapper = getPluginWrapper(name);
+ var runtimeMode = getRuntimeMode(name);
var resourceLoader =
new DefaultResourceLoader(pluginWrapper.getPluginClassLoader());
return PluginExtensionLoaderUtils.lookupExtensions(pluginWrapper.getPluginPath(),
- pluginWrapper.getRuntimeMode())
+ runtimeMode)
.stream()
.map(resourceLoader::getResource)
.filter(Resource::exists)
@@ -215,6 +217,7 @@ boolean waitForSettingCreation(Plugin plugin) {
return false;
}
+ var runtimeMode = getRuntimeMode(pluginName);
Optional settingOption = lookupPluginSetting(pluginName, settingName)
.map(setting -> {
// This annotation is added to prevent it from being deleted when stopped.
@@ -802,11 +805,19 @@ static String initialReverseProxyName(String pluginName) {
}
private boolean isDevelopmentMode(String name) {
- PluginWrapper pluginWrapper = haloPluginManager.getPlugin(name);
- RuntimeMode runtimeMode = haloPluginManager.getRuntimeMode();
- if (pluginWrapper != null) {
- runtimeMode = pluginWrapper.getRuntimeMode();
+ return RuntimeMode.DEVELOPMENT.equals(getRuntimeMode(name));
+ }
+
+ private RuntimeMode getRuntimeMode(String name) {
+ var pluginWrapper = haloPluginManager.getPlugin(name);
+ if (pluginWrapper == null) {
+ return haloPluginManager.getRuntimeMode();
+ }
+ if (pluginWrapper instanceof HaloPluginWrapper haloPluginWrapper) {
+ return haloPluginWrapper.getRuntimeMode();
}
- return RuntimeMode.DEVELOPMENT.equals(runtimeMode);
+ return Files.isDirectory(pluginWrapper.getPluginPath())
+ ? RuntimeMode.DEVELOPMENT
+ : RuntimeMode.DEPLOYMENT;
}
}
diff --git a/application/src/main/java/run/halo/app/plugin/BasePluginFactory.java b/application/src/main/java/run/halo/app/plugin/BasePluginFactory.java
index c769ab8e29..211cdee0d7 100644
--- a/application/src/main/java/run/halo/app/plugin/BasePluginFactory.java
+++ b/application/src/main/java/run/halo/app/plugin/BasePluginFactory.java
@@ -23,13 +23,17 @@ public Plugin create(PluginWrapper pluginWrapper) {
return getPluginContext(pluginWrapper)
.map(context -> {
try {
- return context.getBean(BasePlugin.class);
+ var basePlugin = context.getBean(BasePlugin.class);
+ var pluginContext = context.getBean(PluginContext.class);
+ basePlugin.setContext(pluginContext);
+ return basePlugin;
} catch (NoSuchBeanDefinitionException e) {
log.info(
"No bean named 'basePlugin' found in the context create default instance");
DefaultListableBeanFactory beanFactory =
context.getDefaultListableBeanFactory();
- BasePlugin pluginInstance = new BasePlugin();
+ var pluginContext = beanFactory.getBean(PluginContext.class);
+ BasePlugin pluginInstance = new BasePlugin(pluginContext);
beanFactory.registerSingleton(Plugin.class.getName(), pluginInstance);
return pluginInstance;
}
diff --git a/application/src/main/java/run/halo/app/plugin/HaloPluginManager.java b/application/src/main/java/run/halo/app/plugin/HaloPluginManager.java
index 7abcb1d5f4..41a61a86e7 100644
--- a/application/src/main/java/run/halo/app/plugin/HaloPluginManager.java
+++ b/application/src/main/java/run/halo/app/plugin/HaloPluginManager.java
@@ -108,6 +108,17 @@ protected PluginDescriptorFinder createPluginDescriptorFinder() {
return new YamlPluginDescriptorFinder();
}
+ @Override
+ protected PluginWrapper createPluginWrapper(PluginDescriptor pluginDescriptor, Path pluginPath,
+ ClassLoader pluginClassLoader) {
+ // create the plugin wrapper
+ log.debug("Creating wrapper for plugin '{}'", pluginPath);
+ HaloPluginWrapper pluginWrapper =
+ new HaloPluginWrapper(this, pluginDescriptor, pluginPath, pluginClassLoader);
+ pluginWrapper.setPluginFactory(getPluginFactory());
+ return pluginWrapper;
+ }
+
@Override
protected void firePluginStateEvent(PluginStateEvent event) {
rootApplicationContext.publishEvent(
diff --git a/application/src/main/java/run/halo/app/plugin/HaloPluginWrapper.java b/application/src/main/java/run/halo/app/plugin/HaloPluginWrapper.java
new file mode 100644
index 0000000000..f784400ab4
--- /dev/null
+++ b/application/src/main/java/run/halo/app/plugin/HaloPluginWrapper.java
@@ -0,0 +1,34 @@
+package run.halo.app.plugin;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.pf4j.PluginDescriptor;
+import org.pf4j.PluginManager;
+import org.pf4j.PluginWrapper;
+import org.pf4j.RuntimeMode;
+
+/**
+ * A wrapper over plugin instance for Halo.
+ *
+ * @author guqing
+ * @since 2.10.0
+ */
+public class HaloPluginWrapper extends PluginWrapper {
+
+ private final RuntimeMode runtimeMode;
+
+ /**
+ * Creates a new plugin wrapper to manage the specified plugin.
+ */
+ public HaloPluginWrapper(PluginManager pluginManager, PluginDescriptor descriptor,
+ Path pluginPath, ClassLoader pluginClassLoader) {
+ super(pluginManager, descriptor, pluginPath, pluginClassLoader);
+ this.runtimeMode = Files.isDirectory(pluginPath)
+ ? RuntimeMode.DEVELOPMENT : RuntimeMode.DEPLOYMENT;
+ }
+
+ @Override
+ public RuntimeMode getRuntimeMode() {
+ return runtimeMode;
+ }
+}
diff --git a/application/src/main/java/run/halo/app/plugin/PluginApplicationInitializer.java b/application/src/main/java/run/halo/app/plugin/PluginApplicationInitializer.java
index d437b61981..45c9d367f6 100644
--- a/application/src/main/java/run/halo/app/plugin/PluginApplicationInitializer.java
+++ b/application/src/main/java/run/halo/app/plugin/PluginApplicationInitializer.java
@@ -9,6 +9,7 @@
import java.util.Set;
import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
+import org.pf4j.PluginRuntimeException;
import org.pf4j.PluginWrapper;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.env.PropertySourceLoader;
@@ -88,6 +89,8 @@ private PluginApplicationContext createPluginApplicationContext(String pluginId)
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
stopWatch.stop();
+ beanFactory.registerSingleton("pluginContext", createPluginContext(plugin));
+ // TODO deprecated
beanFactory.registerSingleton("pluginWrapper", haloPluginManager.getPlugin(pluginId));
populateSettingFetcher(pluginId, beanFactory);
@@ -131,6 +134,15 @@ private void initApplicationContext(String pluginId) {
stopWatch.getTotalTimeMillis(), stopWatch.prettyPrint());
}
+ PluginContext createPluginContext(PluginWrapper pluginWrapper) {
+ if (pluginWrapper instanceof HaloPluginWrapper haloPluginWrapper) {
+ return new PluginContext(haloPluginWrapper.getPluginId(),
+ pluginWrapper.getDescriptor().getVersion(),
+ haloPluginWrapper.getRuntimeMode());
+ }
+ throw new PluginRuntimeException("PluginWrapper must be instance of HaloPluginWrapper");
+ }
+
private void populateSettingFetcher(String pluginName,
DefaultListableBeanFactory listableBeanFactory) {
ReactiveExtensionClient extensionClient =
diff --git a/application/src/main/java/run/halo/app/plugin/PluginAutoConfiguration.java b/application/src/main/java/run/halo/app/plugin/PluginAutoConfiguration.java
index 40d4df246f..bd74031438 100644
--- a/application/src/main/java/run/halo/app/plugin/PluginAutoConfiguration.java
+++ b/application/src/main/java/run/halo/app/plugin/PluginAutoConfiguration.java
@@ -5,6 +5,7 @@
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import lombok.extern.slf4j.Slf4j;
@@ -109,33 +110,7 @@ protected PluginLoader createPluginLoader() {
}
} else {
return new CompoundPluginLoader()
- .add(new DevelopmentPluginLoader(this) {
-
- @Override
- protected PluginClassLoader createPluginClassLoader(Path pluginPath,
- PluginDescriptor pluginDescriptor) {
- return new PluginClassLoader(pluginManager, pluginDescriptor,
- getClass().getClassLoader(), ClassLoadingStrategy.APD);
- }
-
- @Override
- public ClassLoader loadPlugin(Path pluginPath,
- PluginDescriptor pluginDescriptor) {
- if (pluginProperties.getClassesDirectories() != null) {
- for (String classesDirectory :
- pluginProperties.getClassesDirectories()) {
- pluginClasspath.addClassesDirectories(classesDirectory);
- }
- }
- if (pluginProperties.getLibDirectories() != null) {
- for (String libDirectory :
- pluginProperties.getLibDirectories()) {
- pluginClasspath.addJarsDirectories(libDirectory);
- }
- }
- return super.loadPlugin(pluginPath, pluginDescriptor);
- }
- }, this::isDevelopment)
+ .add(createDevelopmentPluginLoader(this), this::isDevelopment)
.add(new JarPluginLoader(this) {
@Override
public ClassLoader loadPlugin(Path pluginPath,
@@ -145,9 +120,8 @@ public ClassLoader loadPlugin(Path pluginPath,
getClass().getClassLoader(), ClassLoadingStrategy.APD);
pluginClassLoader.addFile(pluginPath.toFile());
return pluginClassLoader;
-
}
- }, this::isNotDevelopment);
+ });
}
}
@@ -167,9 +141,8 @@ protected PluginRepository createPluginRepository() {
.setFixedPaths(pluginProperties.getFixedPluginPath());
return new CompoundPluginRepository()
.add(developmentPluginRepository, this::isDevelopment)
- .add(new JarPluginRepository(getPluginsRoots()), this::isNotDevelopment)
- .add(new DefaultPluginRepository(getPluginsRoots()),
- this::isNotDevelopment);
+ .add(new JarPluginRepository(getPluginsRoots()))
+ .add(new DefaultPluginRepository(getPluginsRoots()));
}
};
@@ -181,6 +154,41 @@ protected PluginRepository createPluginRepository() {
return pluginManager;
}
+ DevelopmentPluginLoader createDevelopmentPluginLoader(PluginManager pluginManager) {
+ return new DevelopmentPluginLoader(pluginManager) {
+ @Override
+ protected PluginClassLoader createPluginClassLoader(Path pluginPath,
+ PluginDescriptor pluginDescriptor) {
+ return new PluginClassLoader(pluginManager, pluginDescriptor,
+ getClass().getClassLoader(), ClassLoadingStrategy.APD);
+ }
+
+ @Override
+ public ClassLoader loadPlugin(Path pluginPath,
+ PluginDescriptor pluginDescriptor) {
+ if (pluginProperties.getClassesDirectories() != null) {
+ for (String classesDirectory :
+ pluginProperties.getClassesDirectories()) {
+ pluginClasspath.addClassesDirectories(classesDirectory);
+ }
+ }
+ if (pluginProperties.getLibDirectories() != null) {
+ for (String libDirectory :
+ pluginProperties.getLibDirectories()) {
+ pluginClasspath.addJarsDirectories(libDirectory);
+ }
+ }
+ return super.loadPlugin(pluginPath, pluginDescriptor);
+ }
+
+ @Override
+ public boolean isApplicable(Path pluginPath) {
+ return Files.exists(pluginPath)
+ && Files.isDirectory(pluginPath);
+ }
+ };
+ }
+
String getSystemVersion() {
return systemVersionSupplier.get().getNormalVersion();
}
diff --git a/application/src/main/java/run/halo/app/plugin/SpringExtensionFactory.java b/application/src/main/java/run/halo/app/plugin/SpringExtensionFactory.java
index 4bf0c5b22f..6477f48115 100644
--- a/application/src/main/java/run/halo/app/plugin/SpringExtensionFactory.java
+++ b/application/src/main/java/run/halo/app/plugin/SpringExtensionFactory.java
@@ -6,11 +6,12 @@
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.pf4j.Extension;
import org.pf4j.ExtensionFactory;
-import org.pf4j.Plugin;
import org.pf4j.PluginManager;
+import org.pf4j.PluginRuntimeException;
import org.pf4j.PluginWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
@@ -52,46 +53,18 @@
* @since 2.0.0
*/
@Slf4j
+@RequiredArgsConstructor
public class SpringExtensionFactory implements ExtensionFactory {
- public static final boolean AUTOWIRE_BY_DEFAULT = true;
-
/**
* The plugin manager is used for retrieving a plugin from a given extension class and as a
* fallback supplier of an application context.
*/
protected final PluginManager pluginManager;
- /**
- * Indicates if springs autowiring possibilities should be used.
- */
- protected final boolean autowire;
-
- public SpringExtensionFactory(PluginManager pluginManager) {
- this(pluginManager, AUTOWIRE_BY_DEFAULT);
- }
-
- public SpringExtensionFactory(final PluginManager pluginManager, final boolean autowire) {
- this.pluginManager = pluginManager;
- this.autowire = autowire;
- if (!autowire) {
- log.warn(
- "Autowiring is disabled although the only reason for existence of this special "
- + "factory is"
- +
- " supporting spring and its application context.");
- }
- }
-
@Override
@Nullable
public T create(Class extensionClass) {
- if (!this.autowire) {
- log.warn("Create instance of '" + nameOf(extensionClass)
- + "' without using springs possibilities as"
- + " autowiring is disabled.");
- return createWithoutSpring(extensionClass);
- }
Optional contextOptional =
getPluginApplicationContextBy(extensionClass);
if (contextOptional.isPresent()) {
@@ -154,52 +127,38 @@ private Object[] nullParameters(final Constructor> constructor) {
protected Optional getPluginApplicationContextBy(
final Class extensionClass) {
- final Plugin plugin = Optional.ofNullable(this.pluginManager.whichPlugin(extensionClass))
+ return Optional.ofNullable(this.pluginManager.whichPlugin(extensionClass))
.map(PluginWrapper::getPlugin)
- .orElse(null);
-
- final PluginApplicationContext applicationContext;
-
- if (plugin instanceof BasePlugin) {
- log.debug(
- " Extension class ' " + nameOf(extensionClass) + "' belongs to halo-plugin '"
- + nameOf(plugin)
- + "' and will be autowired by using its application context.");
- applicationContext = ExtensionContextRegistry.getInstance()
- .getByPluginId(plugin.getWrapper().getPluginId());
- return Optional.of(applicationContext);
- } else if (this.pluginManager instanceof HaloPluginManager && plugin != null) {
- log.debug(" Extension class ' " + nameOf(extensionClass)
- + "' belongs to a non halo-plugin (or main application)"
- + " '" + nameOf(plugin)
- + ", but the used Halo plugin-manager is a spring-plugin-manager. Therefore"
- + " the extension class will be autowired by using the managers application "
- + "contexts");
- String pluginId = plugin.getWrapper().getPluginId();
- applicationContext = ((HaloPluginManager) this.pluginManager)
- .getPluginApplicationContext(pluginId);
- } else {
- log.warn(" No application contexts can be used for instantiating extension class '"
- + nameOf(extensionClass) + "'."
- + " This extension neither belongs to a halo-plugin (id: '" + nameOf(plugin)
- + "') nor is the used"
- + " plugin manager a spring-plugin-manager (used manager: '"
- + nameOf(this.pluginManager.getClass()) + "')."
- + " At perspective of PF4J this seems highly uncommon in combination with a factory"
- + " which only reason for existence"
- + " is using spring (and its application context) and should at least be reviewed. "
- + "In fact no autowiring can be"
- + " applied although autowire flag was set to 'true'. Instantiating will fallback "
- + "to standard Java reflection.");
- applicationContext = null;
- }
-
- return Optional.ofNullable(applicationContext);
+ .map(plugin -> {
+ if (plugin instanceof BasePlugin basePlugin) {
+ return basePlugin;
+ }
+ throw new PluginRuntimeException(
+ "The plugin must be an instance of BasePlugin");
+ })
+ .map(plugin -> {
+ var pluginName = plugin.getContext().getName();
+ if (this.pluginManager instanceof HaloPluginManager haloPluginManager) {
+ log.debug(" Extension class ' " + nameOf(extensionClass)
+ + "' belongs to a non halo-plugin (or main application)"
+ + " '" + nameOf(plugin)
+ + ", but the used Halo plugin-manager is a spring-plugin-manager. Therefore"
+ + " the extension class will be autowired by using the managers "
+ + "application "
+ + "contexts");
+ return haloPluginManager.getPluginApplicationContext(pluginName);
+ }
+ log.debug(
+ " Extension class ' " + nameOf(extensionClass) + "' belongs to halo-plugin '"
+ + nameOf(plugin)
+ + "' and will be autowired by using its application context.");
+ return ExtensionContextRegistry.getInstance().getByPluginId(pluginName);
+ });
}
- private String nameOf(final Plugin plugin) {
+ private String nameOf(final BasePlugin plugin) {
return Objects.nonNull(plugin)
- ? plugin.getWrapper().getPluginId()
+ ? plugin.getContext().getName()
: "system";
}
diff --git a/application/src/main/java/run/halo/app/security/authentication/pat/PatEndpoint.java b/application/src/main/java/run/halo/app/security/authentication/pat/PatEndpoint.java
index efc49c35d7..cb0de74d26 100644
--- a/application/src/main/java/run/halo/app/security/authentication/pat/PatEndpoint.java
+++ b/application/src/main/java/run/halo/app/security/authentication/pat/PatEndpoint.java
@@ -26,7 +26,7 @@ public PatEndpoint(UserScopedPatHandler patHandler) {
@Override
public RouterFunction endpoint() {
var tag = groupVersion().toString() + "/" + PersonalAccessToken.KIND;
- return route().nest(path("/users/-/personalaccesstokens"),
+ return route().nest(path("/personalaccesstokens"),
() -> route()
.POST(patHandler::create,
builder -> builder
diff --git a/application/src/main/java/run/halo/app/security/authentication/pat/impl/UserScopedPatHandlerImpl.java b/application/src/main/java/run/halo/app/security/authentication/pat/impl/UserScopedPatHandlerImpl.java
index 600cf1e296..855334726b 100644
--- a/application/src/main/java/run/halo/app/security/authentication/pat/impl/UserScopedPatHandlerImpl.java
+++ b/application/src/main/java/run/halo/app/security/authentication/pat/impl/UserScopedPatHandlerImpl.java
@@ -1,6 +1,5 @@
package run.halo.app.security.authentication.pat.impl;
-import static org.apache.commons.lang3.StringUtils.startsWith;
import static run.halo.app.extension.Comparators.compareCreationTimestamp;
import static run.halo.app.security.authentication.pat.PatServerWebExchangeMatcher.PAT_TOKEN_PREFIX;
@@ -12,7 +11,6 @@
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
-import java.util.stream.Collectors;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
@@ -48,8 +46,6 @@
@Service
public class UserScopedPatHandlerImpl implements UserScopedPatHandler {
- private static final String ROLE_PREFIX = AuthorityUtils.ROLE_PREFIX;
-
private static final String ACCESS_TOKEN_ANNO_NAME = "security.halo.run/access-token";
private static final NotFoundException PAT_NOT_FOUND_EX =
@@ -257,22 +253,6 @@ private Mono hasSufficientRoles(
return roleService.contains(grantedRoles, requestRoles);
}
- private static boolean containsIllegalRoles(
- Collection extends GrantedAuthority> grantedAuthorities,
- List roles) {
- if (CollectionUtils.isEmpty(roles)) {
- return false;
- }
- var roleSet = roles.stream()
- .map(role -> ROLE_PREFIX + role)
- .collect(Collectors.toSet());
- var grantedRoleSet = grantedAuthorities.stream()
- .map(GrantedAuthority::getAuthority)
- .filter(authority -> startsWith(authority, ROLE_PREFIX))
- .collect(Collectors.toSet());
- return !grantedRoleSet.containsAll(roleSet);
- }
-
private Mono getPat(String name, String username) {
return client.get(PersonalAccessToken.class, name)
.filter(pat -> Objects.equals(pat.getSpec().getUsername(), username)
diff --git a/application/src/main/resources/extensions/role-template-authenticated.yaml b/application/src/main/resources/extensions/role-template-authenticated.yaml
index 854ea0df39..7f2d9e05e5 100644
--- a/application/src/main/resources/extensions/role-template-authenticated.yaml
+++ b/application/src/main/resources/extensions/role-template-authenticated.yaml
@@ -105,13 +105,11 @@ metadata:
halo.run/role-template: "true"
halo.run/hidden: "true"
rules:
- - apiGroups: [ "api.console.security.halo.run" ]
- resources: [ "users/personalaccesstokens" ]
- resourceNames: [ "-" ]
+ - apiGroups: [ "api.security.halo.run" ]
+ resources: [ "personalaccesstokens" ]
verbs: [ "*" ]
- - apiGroups: [ "api.console.security.halo.run" ]
- resources: [ "users/personalaccesstokens/actions" ]
- resourceNames: [ "-" ]
+ - apiGroups: [ "api.security.halo.run" ]
+ resources: [ "personalaccesstokens/actions" ]
verbs: [ "update" ]
---
apiVersion: v1alpha1
diff --git a/application/src/test/java/run/halo/app/core/extension/reconciler/PluginReconcilerTest.java b/application/src/test/java/run/halo/app/core/extension/reconciler/PluginReconcilerTest.java
index 811e2c9271..a2e46134e5 100644
--- a/application/src/test/java/run/halo/app/core/extension/reconciler/PluginReconcilerTest.java
+++ b/application/src/test/java/run/halo/app/core/extension/reconciler/PluginReconcilerTest.java
@@ -50,6 +50,7 @@
import run.halo.app.infra.utils.FileUtils;
import run.halo.app.infra.utils.JsonUtils;
import run.halo.app.plugin.HaloPluginManager;
+import run.halo.app.plugin.HaloPluginWrapper;
import run.halo.app.plugin.PluginConst;
import run.halo.app.plugin.PluginStartingError;
@@ -69,7 +70,7 @@ class PluginReconcilerTest {
ExtensionClient extensionClient;
@Mock
- PluginWrapper pluginWrapper;
+ HaloPluginWrapper pluginWrapper;
@Mock
ApplicationEventPublisher eventPublisher;
diff --git a/application/src/test/java/run/halo/app/security/authentication/pat/PatTest.java b/application/src/test/java/run/halo/app/security/authentication/pat/PatTest.java
index c1e0f91b48..3d0b5b5078 100644
--- a/application/src/test/java/run/halo/app/security/authentication/pat/PatTest.java
+++ b/application/src/test/java/run/halo/app/security/authentication/pat/PatTest.java
@@ -26,7 +26,7 @@ void generatePat() {
spec.setRoles(List.of("super-role"));
spec.setName("Fake PAT");
webClient.post()
- .uri("/apis/api.security.halo.run/v1alpha1/users/-/personalaccesstokens")
+ .uri("/apis/api.security.halo.run/v1alpha1/personalaccesstokens")
.bodyValue(requestPat)
.exchange()
.expectStatus().isOk()
diff --git a/console/packages/api-client/src/.openapi-generator/FILES b/console/packages/api-client/src/.openapi-generator/FILES
index 54693000ea..3330ada84e 100644
--- a/console/packages/api-client/src/.openapi-generator/FILES
+++ b/console/packages/api-client/src/.openapi-generator/FILES
@@ -14,7 +14,6 @@ api/api-console-halo-run-v1alpha1-system-api.ts
api/api-console-halo-run-v1alpha1-theme-api.ts
api/api-console-halo-run-v1alpha1-user-api.ts
api/api-console-migration-halo-run-v1alpha1-migration-api.ts
-api/api-console-security-halo-run-v1alpha1-personal-access-token-api.ts
api/api-content-halo-run-v1alpha1-category-api.ts
api/api-content-halo-run-v1alpha1-post-api.ts
api/api-content-halo-run-v1alpha1-single-page-api.ts
@@ -26,6 +25,7 @@ api/api-halo-run-v1alpha1-stats-api.ts
api/api-halo-run-v1alpha1-tracker-api.ts
api/api-halo-run-v1alpha1-user-api.ts
api/api-plugin-halo-run-v1alpha1-plugin-api.ts
+api/api-security-halo-run-v1alpha1-personal-access-token-api.ts
api/auth-halo-run-v1alpha1-auth-provider-api.ts
api/auth-halo-run-v1alpha1-user-connection-api.ts
api/content-halo-run-v1alpha1-category-api.ts
diff --git a/console/packages/api-client/src/api.ts b/console/packages/api-client/src/api.ts
index 23a8c25752..da76420222 100644
--- a/console/packages/api-client/src/api.ts
+++ b/console/packages/api-client/src/api.ts
@@ -25,7 +25,6 @@ export * from "./api/api-console-halo-run-v1alpha1-system-api";
export * from "./api/api-console-halo-run-v1alpha1-theme-api";
export * from "./api/api-console-halo-run-v1alpha1-user-api";
export * from "./api/api-console-migration-halo-run-v1alpha1-migration-api";
-export * from "./api/api-console-security-halo-run-v1alpha1-personal-access-token-api";
export * from "./api/api-content-halo-run-v1alpha1-category-api";
export * from "./api/api-content-halo-run-v1alpha1-post-api";
export * from "./api/api-content-halo-run-v1alpha1-single-page-api";
@@ -37,6 +36,7 @@ export * from "./api/api-halo-run-v1alpha1-stats-api";
export * from "./api/api-halo-run-v1alpha1-tracker-api";
export * from "./api/api-halo-run-v1alpha1-user-api";
export * from "./api/api-plugin-halo-run-v1alpha1-plugin-api";
+export * from "./api/api-security-halo-run-v1alpha1-personal-access-token-api";
export * from "./api/auth-halo-run-v1alpha1-auth-provider-api";
export * from "./api/auth-halo-run-v1alpha1-user-connection-api";
export * from "./api/content-halo-run-v1alpha1-category-api";
diff --git a/console/packages/api-client/src/api/api-console-security-halo-run-v1alpha1-personal-access-token-api.ts b/console/packages/api-client/src/api/api-security-halo-run-v1alpha1-personal-access-token-api.ts
similarity index 62%
rename from console/packages/api-client/src/api/api-console-security-halo-run-v1alpha1-personal-access-token-api.ts
rename to console/packages/api-client/src/api/api-security-halo-run-v1alpha1-personal-access-token-api.ts
index 2bb9e7e0bf..7af1134b90 100644
--- a/console/packages/api-client/src/api/api-console-security-halo-run-v1alpha1-personal-access-token-api.ts
+++ b/console/packages/api-client/src/api/api-security-halo-run-v1alpha1-personal-access-token-api.ts
@@ -40,10 +40,10 @@ import {
// @ts-ignore
import { PersonalAccessToken } from "../models";
/**
- * ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi - axios parameter creator
+ * ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi - axios parameter creator
* @export
*/
-export const ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiAxiosParamCreator =
+export const ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiAxiosParamCreator =
function (configuration?: Configuration) {
return {
/**
@@ -59,7 +59,7 @@ export const ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiAxiosParamCr
// verify required parameter 'name' is not null or undefined
assertParamExists("deletePat", "name", name);
const localVarPath =
- `/apis/api.security.halo.run/v1alpha1/users/-/personalaccesstokens/{name}`.replace(
+ `/apis/api.security.halo.run/v1alpha1/personalaccesstokens/{name}`.replace(
`{${"name"}}`,
encodeURIComponent(String(name))
);
@@ -116,7 +116,7 @@ export const ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiAxiosParamCr
"personalAccessToken",
personalAccessToken
);
- const localVarPath = `/apis/api.security.halo.run/v1alpha1/users/-/personalaccesstokens`;
+ const localVarPath = `/apis/api.security.halo.run/v1alpha1/personalaccesstokens`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
@@ -174,7 +174,7 @@ export const ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiAxiosParamCr
// verify required parameter 'name' is not null or undefined
assertParamExists("obtainPat", "name", name);
const localVarPath =
- `/apis/api.security.halo.run/v1alpha1/users/-/personalaccesstokens/{name}`.replace(
+ `/apis/api.security.halo.run/v1alpha1/personalaccesstokens/{name}`.replace(
`{${"name"}}`,
encodeURIComponent(String(name))
);
@@ -223,7 +223,7 @@ export const ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiAxiosParamCr
obtainPats: async (
options: AxiosRequestConfig = {}
): Promise => {
- const localVarPath = `/apis/api.security.halo.run/v1alpha1/users/-/personalaccesstokens`;
+ const localVarPath = `/apis/api.security.halo.run/v1alpha1/personalaccesstokens`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
@@ -274,7 +274,7 @@ export const ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiAxiosParamCr
// verify required parameter 'name' is not null or undefined
assertParamExists("restorePat", "name", name);
const localVarPath =
- `/apis/api.security.halo.run/v1alpha1/users/-/personalaccesstokens/{name}/actions/restoration`.replace(
+ `/apis/api.security.halo.run/v1alpha1/personalaccesstokens/{name}/actions/restoration`.replace(
`{${"name"}}`,
encodeURIComponent(String(name))
);
@@ -328,7 +328,7 @@ export const ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiAxiosParamCr
// verify required parameter 'name' is not null or undefined
assertParamExists("revokePat", "name", name);
const localVarPath =
- `/apis/api.security.halo.run/v1alpha1/users/-/personalaccesstokens/{name}/actions/revocation`.replace(
+ `/apis/api.security.halo.run/v1alpha1/personalaccesstokens/{name}/actions/revocation`.replace(
`{${"name"}}`,
encodeURIComponent(String(name))
);
@@ -373,181 +373,182 @@ export const ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiAxiosParamCr
};
/**
- * ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi - functional programming interface
+ * ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi - functional programming interface
* @export
*/
-export const ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiFp =
- function (configuration?: Configuration) {
- const localVarAxiosParamCreator =
- ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiAxiosParamCreator(
+export const ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiFp = function (
+ configuration?: Configuration
+) {
+ const localVarAxiosParamCreator =
+ ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiAxiosParamCreator(
+ configuration
+ );
+ return {
+ /**
+ * Delete a PAT
+ * @param {string} name
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ async deletePat(
+ name: string,
+ options?: AxiosRequestConfig
+ ): Promise<
+ (axios?: AxiosInstance, basePath?: string) => AxiosPromise
+ > {
+ const localVarAxiosArgs = await localVarAxiosParamCreator.deletePat(
+ name,
+ options
+ );
+ return createRequestFunction(
+ localVarAxiosArgs,
+ globalAxios,
+ BASE_PATH,
configuration
);
- return {
- /**
- * Delete a PAT
- * @param {string} name
- * @param {*} [options] Override http request option.
- * @throws {RequiredError}
- */
- async deletePat(
- name: string,
- options?: AxiosRequestConfig
- ): Promise<
- (axios?: AxiosInstance, basePath?: string) => AxiosPromise
- > {
- const localVarAxiosArgs = await localVarAxiosParamCreator.deletePat(
- name,
- options
- );
- return createRequestFunction(
- localVarAxiosArgs,
- globalAxios,
- BASE_PATH,
- configuration
- );
- },
- /**
- * Generate a PAT.
- * @param {PersonalAccessToken} personalAccessToken
- * @param {*} [options] Override http request option.
- * @throws {RequiredError}
- */
- async generatePat(
- personalAccessToken: PersonalAccessToken,
- options?: AxiosRequestConfig
- ): Promise<
- (
- axios?: AxiosInstance,
- basePath?: string
- ) => AxiosPromise
- > {
- const localVarAxiosArgs = await localVarAxiosParamCreator.generatePat(
- personalAccessToken,
- options
- );
- return createRequestFunction(
- localVarAxiosArgs,
- globalAxios,
- BASE_PATH,
- configuration
- );
- },
- /**
- * Obtain a PAT.
- * @param {string} name
- * @param {*} [options] Override http request option.
- * @throws {RequiredError}
- */
- async obtainPat(
- name: string,
- options?: AxiosRequestConfig
- ): Promise<
- (axios?: AxiosInstance, basePath?: string) => AxiosPromise
- > {
- const localVarAxiosArgs = await localVarAxiosParamCreator.obtainPat(
- name,
- options
- );
- return createRequestFunction(
- localVarAxiosArgs,
- globalAxios,
- BASE_PATH,
- configuration
- );
- },
- /**
- * Obtain PAT list.
- * @param {*} [options] Override http request option.
- * @throws {RequiredError}
- */
- async obtainPats(
- options?: AxiosRequestConfig
- ): Promise<
- (
- axios?: AxiosInstance,
- basePath?: string
- ) => AxiosPromise>
- > {
- const localVarAxiosArgs = await localVarAxiosParamCreator.obtainPats(
- options
- );
- return createRequestFunction(
- localVarAxiosArgs,
- globalAxios,
- BASE_PATH,
- configuration
- );
- },
- /**
- * Restore a PAT.
- * @param {string} name
- * @param {*} [options] Override http request option.
- * @throws {RequiredError}
- */
- async restorePat(
- name: string,
- options?: AxiosRequestConfig
- ): Promise<
- (axios?: AxiosInstance, basePath?: string) => AxiosPromise
- > {
- const localVarAxiosArgs = await localVarAxiosParamCreator.restorePat(
- name,
- options
- );
- return createRequestFunction(
- localVarAxiosArgs,
- globalAxios,
- BASE_PATH,
- configuration
- );
- },
- /**
- * Revoke a PAT
- * @param {string} name
- * @param {*} [options] Override http request option.
- * @throws {RequiredError}
- */
- async revokePat(
- name: string,
- options?: AxiosRequestConfig
- ): Promise<
- (axios?: AxiosInstance, basePath?: string) => AxiosPromise
- > {
- const localVarAxiosArgs = await localVarAxiosParamCreator.revokePat(
- name,
- options
- );
- return createRequestFunction(
- localVarAxiosArgs,
- globalAxios,
- BASE_PATH,
- configuration
- );
- },
- };
+ },
+ /**
+ * Generate a PAT.
+ * @param {PersonalAccessToken} personalAccessToken
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ async generatePat(
+ personalAccessToken: PersonalAccessToken,
+ options?: AxiosRequestConfig
+ ): Promise<
+ (
+ axios?: AxiosInstance,
+ basePath?: string
+ ) => AxiosPromise
+ > {
+ const localVarAxiosArgs = await localVarAxiosParamCreator.generatePat(
+ personalAccessToken,
+ options
+ );
+ return createRequestFunction(
+ localVarAxiosArgs,
+ globalAxios,
+ BASE_PATH,
+ configuration
+ );
+ },
+ /**
+ * Obtain a PAT.
+ * @param {string} name
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ async obtainPat(
+ name: string,
+ options?: AxiosRequestConfig
+ ): Promise<
+ (axios?: AxiosInstance, basePath?: string) => AxiosPromise
+ > {
+ const localVarAxiosArgs = await localVarAxiosParamCreator.obtainPat(
+ name,
+ options
+ );
+ return createRequestFunction(
+ localVarAxiosArgs,
+ globalAxios,
+ BASE_PATH,
+ configuration
+ );
+ },
+ /**
+ * Obtain PAT list.
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ async obtainPats(
+ options?: AxiosRequestConfig
+ ): Promise<
+ (
+ axios?: AxiosInstance,
+ basePath?: string
+ ) => AxiosPromise>
+ > {
+ const localVarAxiosArgs = await localVarAxiosParamCreator.obtainPats(
+ options
+ );
+ return createRequestFunction(
+ localVarAxiosArgs,
+ globalAxios,
+ BASE_PATH,
+ configuration
+ );
+ },
+ /**
+ * Restore a PAT.
+ * @param {string} name
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ async restorePat(
+ name: string,
+ options?: AxiosRequestConfig
+ ): Promise<
+ (axios?: AxiosInstance, basePath?: string) => AxiosPromise
+ > {
+ const localVarAxiosArgs = await localVarAxiosParamCreator.restorePat(
+ name,
+ options
+ );
+ return createRequestFunction(
+ localVarAxiosArgs,
+ globalAxios,
+ BASE_PATH,
+ configuration
+ );
+ },
+ /**
+ * Revoke a PAT
+ * @param {string} name
+ * @param {*} [options] Override http request option.
+ * @throws {RequiredError}
+ */
+ async revokePat(
+ name: string,
+ options?: AxiosRequestConfig
+ ): Promise<
+ (axios?: AxiosInstance, basePath?: string) => AxiosPromise
+ > {
+ const localVarAxiosArgs = await localVarAxiosParamCreator.revokePat(
+ name,
+ options
+ );
+ return createRequestFunction(
+ localVarAxiosArgs,
+ globalAxios,
+ BASE_PATH,
+ configuration
+ );
+ },
};
+};
/**
- * ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi - factory interface
+ * ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi - factory interface
* @export
*/
-export const ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiFactory =
+export const ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiFactory =
function (
configuration?: Configuration,
basePath?: string,
axios?: AxiosInstance
) {
const localVarFp =
- ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiFp(configuration);
+ ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiFp(configuration);
return {
/**
* Delete a PAT
- * @param {ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiDeletePatRequest} requestParameters Request parameters.
+ * @param {ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiDeletePatRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
deletePat(
- requestParameters: ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiDeletePatRequest,
+ requestParameters: ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiDeletePatRequest,
options?: AxiosRequestConfig
): AxiosPromise {
return localVarFp
@@ -556,12 +557,12 @@ export const ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiFactory =
},
/**
* Generate a PAT.
- * @param {ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiGeneratePatRequest} requestParameters Request parameters.
+ * @param {ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiGeneratePatRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
generatePat(
- requestParameters: ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiGeneratePatRequest,
+ requestParameters: ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiGeneratePatRequest,
options?: AxiosRequestConfig
): AxiosPromise {
return localVarFp
@@ -570,12 +571,12 @@ export const ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiFactory =
},
/**
* Obtain a PAT.
- * @param {ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiObtainPatRequest} requestParameters Request parameters.
+ * @param {ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiObtainPatRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
obtainPat(
- requestParameters: ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiObtainPatRequest,
+ requestParameters: ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiObtainPatRequest,
options?: AxiosRequestConfig
): AxiosPromise {
return localVarFp
@@ -596,12 +597,12 @@ export const ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiFactory =
},
/**
* Restore a PAT.
- * @param {ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiRestorePatRequest} requestParameters Request parameters.
+ * @param {ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiRestorePatRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
restorePat(
- requestParameters: ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiRestorePatRequest,
+ requestParameters: ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiRestorePatRequest,
options?: AxiosRequestConfig
): AxiosPromise {
return localVarFp
@@ -610,12 +611,12 @@ export const ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiFactory =
},
/**
* Revoke a PAT
- * @param {ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiRevokePatRequest} requestParameters Request parameters.
+ * @param {ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiRevokePatRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
revokePat(
- requestParameters: ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiRevokePatRequest,
+ requestParameters: ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiRevokePatRequest,
options?: AxiosRequestConfig
): AxiosPromise {
return localVarFp
@@ -626,94 +627,94 @@ export const ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiFactory =
};
/**
- * Request parameters for deletePat operation in ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi.
+ * Request parameters for deletePat operation in ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi.
* @export
- * @interface ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiDeletePatRequest
+ * @interface ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiDeletePatRequest
*/
-export interface ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiDeletePatRequest {
+export interface ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiDeletePatRequest {
/**
*
* @type {string}
- * @memberof ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiDeletePat
+ * @memberof ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiDeletePat
*/
readonly name: string;
}
/**
- * Request parameters for generatePat operation in ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi.
+ * Request parameters for generatePat operation in ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi.
* @export
- * @interface ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiGeneratePatRequest
+ * @interface ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiGeneratePatRequest
*/
-export interface ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiGeneratePatRequest {
+export interface ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiGeneratePatRequest {
/**
*
* @type {PersonalAccessToken}
- * @memberof ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiGeneratePat
+ * @memberof ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiGeneratePat
*/
readonly personalAccessToken: PersonalAccessToken;
}
/**
- * Request parameters for obtainPat operation in ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi.
+ * Request parameters for obtainPat operation in ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi.
* @export
- * @interface ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiObtainPatRequest
+ * @interface ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiObtainPatRequest
*/
-export interface ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiObtainPatRequest {
+export interface ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiObtainPatRequest {
/**
*
* @type {string}
- * @memberof ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiObtainPat
+ * @memberof ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiObtainPat
*/
readonly name: string;
}
/**
- * Request parameters for restorePat operation in ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi.
+ * Request parameters for restorePat operation in ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi.
* @export
- * @interface ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiRestorePatRequest
+ * @interface ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiRestorePatRequest
*/
-export interface ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiRestorePatRequest {
+export interface ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiRestorePatRequest {
/**
*
* @type {string}
- * @memberof ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiRestorePat
+ * @memberof ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiRestorePat
*/
readonly name: string;
}
/**
- * Request parameters for revokePat operation in ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi.
+ * Request parameters for revokePat operation in ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi.
* @export
- * @interface ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiRevokePatRequest
+ * @interface ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiRevokePatRequest
*/
-export interface ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiRevokePatRequest {
+export interface ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiRevokePatRequest {
/**
*
* @type {string}
- * @memberof ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiRevokePat
+ * @memberof ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiRevokePat
*/
readonly name: string;
}
/**
- * ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi - object-oriented interface
+ * ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi - object-oriented interface
* @export
- * @class ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi
+ * @class ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi
* @extends {BaseAPI}
*/
-export class ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi extends BaseAPI {
+export class ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi extends BaseAPI {
/**
* Delete a PAT
- * @param {ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiDeletePatRequest} requestParameters Request parameters.
+ * @param {ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiDeletePatRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
- * @memberof ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi
+ * @memberof ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi
*/
public deletePat(
- requestParameters: ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiDeletePatRequest,
+ requestParameters: ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiDeletePatRequest,
options?: AxiosRequestConfig
) {
- return ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiFp(
+ return ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiFp(
this.configuration
)
.deletePat(requestParameters.name, options)
@@ -722,16 +723,16 @@ export class ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi extends Bas
/**
* Generate a PAT.
- * @param {ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiGeneratePatRequest} requestParameters Request parameters.
+ * @param {ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiGeneratePatRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
- * @memberof ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi
+ * @memberof ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi
*/
public generatePat(
- requestParameters: ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiGeneratePatRequest,
+ requestParameters: ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiGeneratePatRequest,
options?: AxiosRequestConfig
) {
- return ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiFp(
+ return ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiFp(
this.configuration
)
.generatePat(requestParameters.personalAccessToken, options)
@@ -740,16 +741,16 @@ export class ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi extends Bas
/**
* Obtain a PAT.
- * @param {ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiObtainPatRequest} requestParameters Request parameters.
+ * @param {ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiObtainPatRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
- * @memberof ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi
+ * @memberof ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi
*/
public obtainPat(
- requestParameters: ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiObtainPatRequest,
+ requestParameters: ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiObtainPatRequest,
options?: AxiosRequestConfig
) {
- return ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiFp(
+ return ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiFp(
this.configuration
)
.obtainPat(requestParameters.name, options)
@@ -760,10 +761,10 @@ export class ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi extends Bas
* Obtain PAT list.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
- * @memberof ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi
+ * @memberof ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi
*/
public obtainPats(options?: AxiosRequestConfig) {
- return ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiFp(
+ return ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiFp(
this.configuration
)
.obtainPats(options)
@@ -772,16 +773,16 @@ export class ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi extends Bas
/**
* Restore a PAT.
- * @param {ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiRestorePatRequest} requestParameters Request parameters.
+ * @param {ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiRestorePatRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
- * @memberof ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi
+ * @memberof ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi
*/
public restorePat(
- requestParameters: ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiRestorePatRequest,
+ requestParameters: ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiRestorePatRequest,
options?: AxiosRequestConfig
) {
- return ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiFp(
+ return ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiFp(
this.configuration
)
.restorePat(requestParameters.name, options)
@@ -790,16 +791,16 @@ export class ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi extends Bas
/**
* Revoke a PAT
- * @param {ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiRevokePatRequest} requestParameters Request parameters.
+ * @param {ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiRevokePatRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option.
* @throws {RequiredError}
- * @memberof ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi
+ * @memberof ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi
*/
public revokePat(
- requestParameters: ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiRevokePatRequest,
+ requestParameters: ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiRevokePatRequest,
options?: AxiosRequestConfig
) {
- return ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApiFp(
+ return ApiSecurityHaloRunV1alpha1PersonalAccessTokenApiFp(
this.configuration
)
.revokePat(requestParameters.name, options)
diff --git a/console/src/formkit/inputs/category-select/CategorySelect.vue b/console/src/formkit/inputs/category-select/CategorySelect.vue
index 9e4d52b966..d6845c3302 100644
--- a/console/src/formkit/inputs/category-select/CategorySelect.vue
+++ b/console/src/formkit/inputs/category-select/CategorySelect.vue
@@ -293,6 +293,7 @@ const handleDelete = () => {
v-if="text.trim() && !searchResults?.length"
v-permission="['system:posts:manage']"
class="group flex cursor-pointer items-center justify-between rounded bg-gray-100 p-2"
+ @click="handleCreateCategory"
>
{{
diff --git a/console/src/formkit/inputs/category-select/index.ts b/console/src/formkit/inputs/category-select/index.ts
index 2fa7c7c5ef..4b107fdb73 100644
--- a/console/src/formkit/inputs/category-select/index.ts
+++ b/console/src/formkit/inputs/category-select/index.ts
@@ -44,4 +44,5 @@ export const categorySelect: FormKitTypeDefinition = {
library: {
CategorySelect: CategorySelect,
},
+ schemaMemoKey: "custom-category-select",
};
diff --git a/console/src/formkit/inputs/tag-select/index.ts b/console/src/formkit/inputs/tag-select/index.ts
index 9369ed21cf..3a8447c26b 100644
--- a/console/src/formkit/inputs/tag-select/index.ts
+++ b/console/src/formkit/inputs/tag-select/index.ts
@@ -44,4 +44,5 @@ export const tagSelect: FormKitTypeDefinition = {
library: {
TagSelect: TagSelect,
},
+ schemaMemoKey: "custom-tag-select",
};
diff --git a/console/src/modules/system/auth-providers/components/AuthProviderListItem.vue b/console/src/modules/system/auth-providers/components/AuthProviderListItem.vue
index 371d5172fb..9b0cbf2217 100644
--- a/console/src/modules/system/auth-providers/components/AuthProviderListItem.vue
+++ b/console/src/modules/system/auth-providers/components/AuthProviderListItem.vue
@@ -3,6 +3,7 @@ import { apiClient } from "@/utils/api-client";
import type { ListedAuthProvider } from "@halo-dev/api-client";
import {
Dialog,
+ IconSettings,
Toast,
VAvatar,
VEntity,
@@ -93,6 +94,19 @@ const handleChangeStatus = async () => {
+
+
+
+
+
+
+
diff --git a/console/src/modules/system/users/UserDetail.vue b/console/src/modules/system/users/UserDetail.vue
index 73b4b62b46..33915d7df3 100644
--- a/console/src/modules/system/users/UserDetail.vue
+++ b/console/src/modules/system/users/UserDetail.vue
@@ -1,168 +1,199 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ user?.user.spec.displayName }}
+
+
+ @{{ user?.user.metadata.name }}
+
+
+
+
-
-
-
- {{
- role.metadata.annotations?.[rbacAnnotations.DISPLAY_NAME] ||
- role.metadata.name
- }}
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- {{ authProvider.displayName }}
-
-
-
-
- {{ $t("core.user.detail.operations.unbind.button") }}
-
-
- {{ $t("core.user.detail.operations.bind.button") }}
-
-
-
-
-
-
-
-
-
+
+
+ {{ $t("core.common.buttons.edit") }}
+
+
+
+ {{ $t("core.user.detail.actions.update_profile.title") }}
+
+
+ {{ $t("core.user.detail.actions.change_password.title") }}
+
+
+
+
+
+
+
+
diff --git a/console/src/modules/system/users/components/UserAvatar.vue b/console/src/modules/system/users/components/UserAvatar.vue
new file mode 100644
index 0000000000..137e737a24
--- /dev/null
+++ b/console/src/modules/system/users/components/UserAvatar.vue
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.upload") }}
+
+
+ {{ $t("core.common.buttons.delete") }}
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.submit") }}
+
+
+ {{ $t("core.common.buttons.cancel") }}
+
+
+
+
+
diff --git a/console/src/modules/system/users/layouts/UserProfileLayout.vue b/console/src/modules/system/users/layouts/UserProfileLayout.vue
deleted file mode 100644
index eaeaf367cb..0000000000
--- a/console/src/modules/system/users/layouts/UserProfileLayout.vue
+++ /dev/null
@@ -1,355 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ $t("core.common.buttons.upload") }}
-
-
- {{ $t("core.common.buttons.delete") }}
-
-
-
-
-
-
-
- {{ user?.user.spec.displayName }}
-
-
- @{{ user?.user.metadata.name }}
-
-
-
-
-
-
- {{ $t("core.common.buttons.edit") }}
-
-
-
- {{ $t("core.user.detail.actions.update_profile.title") }}
-
-
- {{ $t("core.user.detail.actions.change_password.title") }}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ $t("core.common.buttons.submit") }}
-
-
- {{ $t("core.common.buttons.cancel") }}
-
-
-
-
-
-
diff --git a/console/src/modules/system/users/module.ts b/console/src/modules/system/users/module.ts
index f7f60e94d7..d437af010b 100644
--- a/console/src/modules/system/users/module.ts
+++ b/console/src/modules/system/users/module.ts
@@ -1,11 +1,9 @@
import { definePlugin } from "@halo-dev/console-shared";
import BasicLayout from "@/layouts/BasicLayout.vue";
import BlankLayout from "@/layouts/BlankLayout.vue";
-import UserProfileLayout from "./layouts/UserProfileLayout.vue";
import UserStatsWidget from "./widgets/UserStatsWidget.vue";
import UserList from "./UserList.vue";
import UserDetail from "./UserDetail.vue";
-import PersonalAccessTokens from "./PersonalAccessTokens.vue";
import Login from "./Login.vue";
import { IconUserSettings } from "@halo-dev/components";
import { markRaw } from "vue";
@@ -61,25 +59,17 @@ export default definePlugin({
},
{
path: ":name",
- component: UserProfileLayout,
+ component: BasicLayout,
name: "User",
children: [
{
- path: "detail",
+ path: "",
name: "UserDetail",
component: UserDetail,
meta: {
title: "core.user.detail.title",
},
},
- {
- path: "tokens",
- name: "PersonalAccessTokens",
- component: PersonalAccessTokens,
- meta: {
- title: "个人令牌",
- },
- },
],
},
],
diff --git a/console/src/modules/system/users/tabs/Detail.vue b/console/src/modules/system/users/tabs/Detail.vue
new file mode 100644
index 0000000000..73b4b62b46
--- /dev/null
+++ b/console/src/modules/system/users/tabs/Detail.vue
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ role.metadata.annotations?.[rbacAnnotations.DISPLAY_NAME] ||
+ role.metadata.name
+ }}
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ {{ authProvider.displayName }}
+
+
+
+
+ {{ $t("core.user.detail.operations.unbind.button") }}
+
+
+ {{ $t("core.user.detail.operations.bind.button") }}
+
+
+
+
+
+
+
+
+
+
diff --git a/console/src/modules/system/users/PersonalAccessTokens.vue b/console/src/modules/system/users/tabs/PersonalAccessTokens.vue
similarity index 93%
rename from console/src/modules/system/users/PersonalAccessTokens.vue
rename to console/src/modules/system/users/tabs/PersonalAccessTokens.vue
index 55a3275470..9b9f24624a 100644
--- a/console/src/modules/system/users/PersonalAccessTokens.vue
+++ b/console/src/modules/system/users/tabs/PersonalAccessTokens.vue
@@ -10,9 +10,9 @@ import { ref } from "vue";
import { apiClient } from "@/utils/api-client";
import type { PersonalAccessToken } from "@halo-dev/api-client";
import { useQuery } from "@tanstack/vue-query";
-import PersonalAccessTokenCreationModal from "./components/PersonalAccessTokenCreationModal.vue";
+import PersonalAccessTokenCreationModal from "../components/PersonalAccessTokenCreationModal.vue";
import { nextTick } from "vue";
-import PersonalAccessTokenListItem from "./components/PersonalAccessTokenListItem.vue";
+import PersonalAccessTokenListItem from "../components/PersonalAccessTokenListItem.vue";
const {
data: pats,
diff --git a/console/src/utils/api-client.ts b/console/src/utils/api-client.ts
index 7c0d5dacc0..0a709fe1c9 100644
--- a/console/src/utils/api-client.ts
+++ b/console/src/utils/api-client.ts
@@ -40,7 +40,7 @@ import {
ApiHaloRunV1alpha1UserApi,
MigrationHaloRunV1alpha1BackupApi,
ApiConsoleMigrationHaloRunV1alpha1MigrationApi,
- ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi,
+ ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi,
SecurityHaloRunV1alpha1PersonalAccessTokenApi,
} from "@halo-dev/api-client";
import type { AxiosError, AxiosInstance } from "axios";
@@ -221,7 +221,7 @@ function setupApiClient(axios: AxiosInstance) {
axios
),
system: new ApiConsoleHaloRunV1alpha1SystemApi(undefined, baseURL, axios),
- pat: new ApiConsoleSecurityHaloRunV1alpha1PersonalAccessTokenApi(
+ pat: new ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi(
undefined,
baseURL,
axios
diff --git a/platform/application/build.gradle b/platform/application/build.gradle
index fa1b40d6df..6f728cac86 100644
--- a/platform/application/build.gradle
+++ b/platform/application/build.gradle
@@ -1,7 +1,7 @@
import org.springframework.boot.gradle.plugin.SpringBootPlugin
plugins {
- id 'org.springframework.boot' version '3.1.3' apply false
+ id 'org.springframework.boot' version '3.1.4' apply false
id 'java-platform'
id 'halo.publish'
id 'signing'
@@ -13,7 +13,7 @@ description = 'Platform of application.'
ext {
commonsLang3 = "3.12.0"
base62 = "0.1.3"
- pf4j = '3.9.0'
+ pf4j = '3.10.0'
javaDiffUtils = "4.12"
guava = "32.0.1-jre"
jsoup = '1.15.3'