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 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 @@ 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 @@ + + + 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 @@ - - 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 @@ + + 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'