CcmNG: Backend for Theme editing
git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@5693 8810af33-2d31-482b-a856-94f89814c4df
Former-commit-id: 5880af452e
pull/2/head
parent
d4e9a5f27f
commit
d1fbf22512
|
|
@ -187,6 +187,19 @@
|
|||
<artifactId>shiro-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
|
|
|
|||
|
|
@ -79,6 +79,9 @@ public final class KernelConfig {
|
|||
@Setting
|
||||
private String importPath = "";
|
||||
|
||||
@Setting
|
||||
private String jwtSecret = "";
|
||||
|
||||
public static KernelConfig getConfig() {
|
||||
final ConfigurationManager confManager = CdiUtil.createCdiUtil()
|
||||
.findBean(ConfigurationManager.class);
|
||||
|
|
@ -234,6 +237,14 @@ public final class KernelConfig {
|
|||
this.importPath = importPath;
|
||||
}
|
||||
|
||||
public String getJwtSecret() {
|
||||
return jwtSecret;
|
||||
}
|
||||
|
||||
public void setJwtSecret(final String jwtSecret) {
|
||||
this.jwtSecret = jwtSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
|
|
@ -249,6 +260,7 @@ public final class KernelConfig {
|
|||
hash = 61 * hash + Objects.hashCode(systemEmailAddress);
|
||||
hash = 61 * hash + Objects.hashCode(exportPath);
|
||||
hash = 61 * hash + Objects.hashCode(importPath);
|
||||
hash = 61 * hash * Objects.hashCode(jwtSecret);
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
|
@ -304,7 +316,11 @@ public final class KernelConfig {
|
|||
return false;
|
||||
}
|
||||
|
||||
return Objects.equals(importPath, other.getImportPath());
|
||||
if (!Objects.equals(importPath, other.getImportPath())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Objects.equals(jwtSecret, other.getJwtSecret());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -143,6 +145,7 @@ public class CcmCore implements CcmModule {
|
|||
|
||||
@Override
|
||||
public void init(final InitEvent event) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -317,7 +317,10 @@ public class CcmFiles {
|
|||
throws FileAccessException,
|
||||
InsufficientPermissionsException {
|
||||
|
||||
return getFileSystemAdapter().existsFile(getDataPath(path));
|
||||
final String dataPath = getDataPath(path);
|
||||
final boolean result = getFileSystemAdapter().existsFile(dataPath);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -381,7 +381,8 @@ public class NIOFileSystemAdapter implements FileSystemAdapter {
|
|||
final Path nioPath = Paths.get(path);
|
||||
|
||||
if (!Files.isDirectory(nioPath)) {
|
||||
throw new FileAccessException(path);
|
||||
throw new FileAccessException(String.format(
|
||||
"%s is not a directory.", path));
|
||||
}
|
||||
|
||||
final Stream<Path> paths;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (C) 2018 LibreCCM Foundation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
package org.libreccm.security;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.ws.rs.ApplicationPath;
|
||||
import javax.ws.rs.core.Application;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
@ApplicationPath("/jwt")
|
||||
public class CcmJwtAuthentication extends Application{
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> getClasses() {
|
||||
|
||||
final Set<Class<?>> classes = new HashSet<>();
|
||||
classes.add(JwtProvider.class);
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright (C) 2018 LibreCCM Foundation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
package org.libreccm.security;
|
||||
|
||||
import com.arsdigita.kernel.KernelConfig;
|
||||
|
||||
import io.jsonwebtoken.JwtBuilder;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.UsernamePasswordToken;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import org.libreccm.configuration.ConfigurationManager;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.security.Key;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonObject;
|
||||
import javax.json.JsonObjectBuilder;
|
||||
import javax.json.JsonReader;
|
||||
import javax.transaction.Transactional;
|
||||
import javax.ws.rs.POST;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
@RequestScoped
|
||||
@Path("/")
|
||||
public class JwtProvider {
|
||||
|
||||
@Inject
|
||||
private ConfigurationManager confManager;
|
||||
|
||||
@Inject
|
||||
private Shiro shiro;
|
||||
|
||||
@POST
|
||||
@Path("/")
|
||||
@Transactional(Transactional.TxType.REQUIRED)
|
||||
public Response getJsonWebToken(final String requestCredentials) {
|
||||
|
||||
if (requestCredentials == null) {
|
||||
return Response
|
||||
.status(Response.Status.BAD_REQUEST)
|
||||
.entity("No credentials provided")
|
||||
.build();
|
||||
}
|
||||
|
||||
final JsonObjectBuilder jsonObjBuilder = Json.createObjectBuilder();
|
||||
final StringReader credentialsReader = new StringReader(
|
||||
requestCredentials);
|
||||
final JsonReader jsonReader = Json.createReader(credentialsReader);
|
||||
final JsonObject credentials = jsonReader.readObject();
|
||||
|
||||
final String userName = credentials.getString("username", null);
|
||||
final String password = credentials.getString("password", null);
|
||||
|
||||
if (userName == null
|
||||
|| userName.isEmpty()
|
||||
|| userName.matches("\\s*")) {
|
||||
|
||||
return Response
|
||||
.status(Response.Status.BAD_REQUEST)
|
||||
.entity("No user name was provided")
|
||||
.build();
|
||||
}
|
||||
|
||||
if (password == null
|
||||
|| password.isEmpty()
|
||||
|| password.matches("\\s*")) {
|
||||
|
||||
return Response
|
||||
.status(Response.Status.BAD_REQUEST)
|
||||
.entity("No password was provided")
|
||||
.build();
|
||||
}
|
||||
|
||||
final UsernamePasswordToken token = new UsernamePasswordToken(
|
||||
userName, password);
|
||||
final Subject subject = shiro.getSubject();
|
||||
|
||||
final KernelConfig kernelConfig = confManager
|
||||
.findConfiguration(KernelConfig.class);
|
||||
if (kernelConfig.getJwtSecret() == null
|
||||
|| kernelConfig.getJwtSecret().isEmpty()
|
||||
|| kernelConfig.getJwtSecret().matches("\\s*")) {
|
||||
|
||||
shiro.getSystemUser().execute(this::generateSecret);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
subject.login(token);
|
||||
|
||||
final SignatureAlgorithm signAlgo = SignatureAlgorithm.HS512;
|
||||
final Key key = new SecretKeySpec(
|
||||
Base64.getDecoder().decode(kernelConfig.getJwtSecret()),
|
||||
signAlgo.getJcaName());
|
||||
final JwtBuilder jwtBuilder = Jwts
|
||||
.builder()
|
||||
.setSubject((String) subject.getPrincipal())
|
||||
.signWith(key);
|
||||
|
||||
return Response
|
||||
.ok(jwtBuilder.compact())
|
||||
.build();
|
||||
} catch (AuthenticationException ex) {
|
||||
return Response
|
||||
.status(Response.Status.FORBIDDEN)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
private void generateSecret() {
|
||||
final Random random = new SecureRandom();
|
||||
final byte[] randomBytes = new byte[64];
|
||||
random.nextBytes(randomBytes);
|
||||
|
||||
final String secret = Base64
|
||||
.getEncoder()
|
||||
.encodeToString(randomBytes);
|
||||
|
||||
final KernelConfig kernelConfig = confManager
|
||||
.findConfiguration(KernelConfig.class);
|
||||
|
||||
kernelConfig.setJwtSecret(secret);
|
||||
confManager.saveConfiguration(kernelConfig);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
|
@ -125,8 +126,7 @@ public class FileSystemThemeProvider implements ThemeProvider {
|
|||
public Optional<ThemeInfo> getThemeInfo(final String theme,
|
||||
final ThemeVersion version) {
|
||||
|
||||
final String themePath = createThemePath(theme, version);
|
||||
return readInfo(themePath);
|
||||
return readInfo(theme);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -228,15 +228,23 @@ public class FileSystemThemeProvider implements ThemeProvider {
|
|||
final String path) {
|
||||
|
||||
final String themePath = createThemePath(theme, version);
|
||||
final String filePath = String.join(themePath, path, "/");
|
||||
final String filePath = String.join("/", themePath, path);
|
||||
|
||||
try {
|
||||
|
||||
if (ccmFiles.isDirectory(filePath)) {
|
||||
return ccmFiles
|
||||
.listFiles(filePath)
|
||||
.stream()
|
||||
.map(currentPath -> buildThemeFileInfo(currentPath))
|
||||
.map(currentPath -> buildThemeFileInfo(
|
||||
String.join("/", theme, currentPath)))
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
final List<ThemeFileInfo> result = new ArrayList<>();
|
||||
final ThemeFileInfo fileInfo = buildThemeFileInfo(filePath);
|
||||
result.add(fileInfo);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} catch (FileAccessException
|
||||
| FileDoesNotExistException
|
||||
|
|
@ -246,12 +254,32 @@ public class FileSystemThemeProvider implements ThemeProvider {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ThemeFileInfo> getThemeFileInfo(
|
||||
final String theme, final ThemeVersion version, final String path) {
|
||||
|
||||
final String themePath = createThemePath(theme, version);
|
||||
final String filePath = String.join("/", themePath, path);
|
||||
|
||||
try {
|
||||
if (ccmFiles.existsFile(filePath)) {
|
||||
|
||||
return Optional.of(buildThemeFileInfo(filePath));
|
||||
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
} catch (FileAccessException | InsufficientPermissionsException ex) {
|
||||
throw new UnexpectedErrorException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<InputStream> getThemeFileAsStream(
|
||||
final String theme, final ThemeVersion version, final String path) {
|
||||
|
||||
final String themePath = createThemePath(theme, version);
|
||||
final String filePath = String.join(themePath, path, "/");
|
||||
final String filePath = String.join("/", themePath, path);
|
||||
|
||||
try {
|
||||
if (ccmFiles.existsFile(path)) {
|
||||
|
|
@ -272,7 +300,7 @@ public class FileSystemThemeProvider implements ThemeProvider {
|
|||
final String path) {
|
||||
|
||||
final String themePath = createThemePath(theme, ThemeVersion.DRAFT);
|
||||
final String filePath = String.join(themePath, path, "/");
|
||||
final String filePath = String.join("/", themePath, path);
|
||||
|
||||
try {
|
||||
|
||||
|
|
@ -289,7 +317,7 @@ public class FileSystemThemeProvider implements ThemeProvider {
|
|||
public void deleteThemeFile(final String theme, final String path) {
|
||||
|
||||
final String themePath = createThemePath(theme, ThemeVersion.DRAFT);
|
||||
final String filePath = String.join(themePath, path, "/");
|
||||
final String filePath = String.join("/", themePath, path);
|
||||
|
||||
try {
|
||||
ccmFiles.deleteFile(filePath, true);
|
||||
|
|
@ -383,15 +411,15 @@ public class FileSystemThemeProvider implements ThemeProvider {
|
|||
}
|
||||
}
|
||||
|
||||
private Optional<ThemeInfo> readInfo(final String themePath) {
|
||||
private Optional<ThemeInfo> readInfo(final String themeName) {
|
||||
|
||||
final ThemeManifest manifest;
|
||||
try {
|
||||
|
||||
final String jsonPath = String.format(
|
||||
DRAFT_THEMES_PATH + "/" + THEME_JSON, themePath);
|
||||
DRAFT_THEMES_PATH + "/" + THEME_JSON, themeName);
|
||||
final String xmlPath = String.format(
|
||||
DRAFT_THEMES_PATH + "/" + THEME_XML, themePath);
|
||||
DRAFT_THEMES_PATH + "/" + THEME_XML, themeName);
|
||||
|
||||
if (ccmFiles.existsFile(jsonPath)) {
|
||||
final InputStream inputStream = ccmFiles
|
||||
|
|
|
|||
|
|
@ -321,9 +321,37 @@ public class StaticThemeProvider implements ThemeProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Optional<InputStream> getThemeFileAsStream(final String theme,
|
||||
final ThemeVersion version,
|
||||
final String path) {
|
||||
public Optional<ThemeFileInfo> getThemeFileInfo(
|
||||
final String theme, final ThemeVersion version, final String path) {
|
||||
|
||||
Objects.requireNonNull(theme);
|
||||
Objects.requireNonNull(version);
|
||||
Objects.requireNonNull(path);
|
||||
|
||||
final List<String> pathTokens = Arrays.asList(path.split("/"));
|
||||
|
||||
final String indexFilePath = String.format("/"
|
||||
+ THEMES_PACKAGE
|
||||
+ "/%s/theme-index.json",
|
||||
theme);
|
||||
final InputStream stream = getClass()
|
||||
.getResourceAsStream(indexFilePath);
|
||||
final JsonReader reader = Json.createReader(stream);
|
||||
final JsonObject indexObj = reader.readObject();
|
||||
final JsonArray currentDir = indexObj.getJsonArray("files");
|
||||
|
||||
final Optional<JsonObject> targetFile = findFile(pathTokens,
|
||||
currentDir);
|
||||
if (targetFile.isPresent()) {
|
||||
return Optional.of(generateFileInfo(targetFile.get()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<InputStream> getThemeFileAsStream(
|
||||
final String theme, final ThemeVersion version, final String path) {
|
||||
|
||||
Objects.requireNonNull(theme);
|
||||
Objects.requireNonNull(path);
|
||||
|
|
|
|||
|
|
@ -137,6 +137,21 @@ public interface ThemeProvider extends Serializable {
|
|||
ThemeVersion version,
|
||||
String path);
|
||||
|
||||
/**
|
||||
* Retrieve the metadata for a particular file in the theme.
|
||||
*
|
||||
* @param theme The name of the theme.
|
||||
* @param version The version of the theme
|
||||
* @param path The path of the file, relative to the theme directory.
|
||||
*
|
||||
* @return An {@link Optional} containing a {@link ThemeInfo} object with
|
||||
* the metadata of the file is its exists. Otherwise an empty
|
||||
* {@link Optional}.
|
||||
*/
|
||||
Optional<ThemeFileInfo> getThemeFileInfo(String theme,
|
||||
ThemeVersion version,
|
||||
String path);
|
||||
|
||||
/**
|
||||
* Retrieve a file from a theme. We use an {@link InputStream} here because
|
||||
* that is the most universal interface in the Java API which works for all
|
||||
|
|
@ -210,9 +225,9 @@ public interface ThemeProvider extends Serializable {
|
|||
void publishTheme(String theme);
|
||||
|
||||
/**
|
||||
* Unpublishes (deletes) the live version of a theme. For
|
||||
* implementations which do not support draft/live themes the implementation
|
||||
* of this method should be a noop, but not throw an exception.
|
||||
* Unpublishes (deletes) the live version of a theme. For implementations
|
||||
* which do not support draft/live themes the implementation of this method
|
||||
* should be a noop, but not throw an exception.
|
||||
*
|
||||
* @param theme The theme to publish.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -182,11 +182,29 @@ public class DatabaseThemeProvider implements ThemeProvider {
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ThemeFileInfo> getThemeFileInfo(
|
||||
final String themeName, final ThemeVersion version, final String path) {
|
||||
|
||||
final Theme theme = themeRepository
|
||||
.findThemeByName(path, version)
|
||||
.orElseThrow(() -> new IllegalArgumentException(String
|
||||
.format("No Theme \"%s\" in the database.", themeName)));
|
||||
|
||||
final Optional<ThemeFile> themeFile = fileRepository
|
||||
.findByPath(theme, path, version);
|
||||
|
||||
if (themeFile.isPresent()) {
|
||||
return Optional.of(createThemeFileInfo(themeFile.get()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(Transactional.TxType.REQUIRED)
|
||||
public Optional<InputStream> getThemeFileAsStream(final String themeName,
|
||||
final ThemeVersion version,
|
||||
final String path) {
|
||||
public Optional<InputStream> getThemeFileAsStream(
|
||||
final String themeName, final ThemeVersion version, final String path) {
|
||||
|
||||
final Optional<Theme> theme = themeRepository
|
||||
.findThemeByName(themeName, version);
|
||||
|
|
|
|||
|
|
@ -20,12 +20,16 @@ package org.libreccm.theming.manager;
|
|||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.libreccm.security.AuthorizationRequired;
|
||||
import org.libreccm.security.RequiresPrivilege;
|
||||
import org.libreccm.theming.ThemeFileInfo;
|
||||
import org.libreccm.theming.ThemeInfo;
|
||||
import org.libreccm.theming.ThemeProvider;
|
||||
import org.libreccm.theming.ThemeVersion;
|
||||
import org.libreccm.theming.ThemingPrivileges;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Serializable;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -52,7 +56,6 @@ import javax.ws.rs.WebApplicationException;
|
|||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
|
|
@ -75,7 +78,7 @@ public class Themes implements Serializable {
|
|||
@GET
|
||||
@Path("/providers")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
//@AuthorizationRequired
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public String getThemeProviders() {
|
||||
|
||||
|
|
@ -103,7 +106,7 @@ public class Themes implements Serializable {
|
|||
@GET
|
||||
@Path("/themes")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
//@AuthorizationRequired
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public List<ThemeInfo> getAvailableThemes() {
|
||||
|
||||
|
|
@ -120,7 +123,7 @@ public class Themes implements Serializable {
|
|||
@GET
|
||||
@Path("/themes/{theme}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
//@AuthorizationRequired
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||
public ThemeInfo getTheme(@PathParam("theme") final String themeName) {
|
||||
|
||||
|
|
@ -140,7 +143,7 @@ public class Themes implements Serializable {
|
|||
@Path("/themes/{theme}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@SuppressWarnings("unchecked")
|
||||
//@AuthorizationRequired
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public ThemeInfo createTheme(
|
||||
@PathParam("theme") final String themeName,
|
||||
|
|
@ -176,7 +179,7 @@ public class Themes implements Serializable {
|
|||
|
||||
@DELETE
|
||||
@Path("/themes/{theme}")
|
||||
//@AuthorizationRequired
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public void deleteTheme(@PathParam("theme") final String themeName) {
|
||||
|
||||
|
|
@ -195,7 +198,7 @@ public class Themes implements Serializable {
|
|||
|
||||
@POST
|
||||
@Path("/themes/{theme}/live")
|
||||
//@AuthorizationRequired
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public void publishTheme(@PathParam("theme") final String themeName) {
|
||||
|
||||
|
|
@ -213,7 +216,7 @@ public class Themes implements Serializable {
|
|||
|
||||
@DELETE
|
||||
@Path("/themes/{theme}/live")
|
||||
//@AuthorizationRequired
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public void unPublishTheme(@PathParam("theme") final String themeName) {
|
||||
|
||||
|
|
@ -226,7 +229,134 @@ public class Themes implements Serializable {
|
|||
} else {
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/themes/{theme}/files/")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public Response getThemeRootDir(@PathParam("theme") final String themeName) {
|
||||
|
||||
final Optional<ThemeProvider> provider = findProvider(themeName);
|
||||
|
||||
if (provider.isPresent()) {
|
||||
|
||||
final Optional<ThemeFileInfo> fileInfo = provider
|
||||
.get()
|
||||
.getThemeFileInfo(themeName, ThemeVersion.DRAFT, "/");
|
||||
|
||||
if (fileInfo.isPresent()) {
|
||||
|
||||
if (fileInfo.get().isDirectory()) {
|
||||
|
||||
final List<ThemeFileInfo> fileInfos = provider
|
||||
.get()
|
||||
.listThemeFiles(themeName, ThemeVersion.DRAFT, "/");
|
||||
|
||||
return Response
|
||||
.ok(fileInfos)
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.build();
|
||||
|
||||
} else {
|
||||
|
||||
throw new WebApplicationException(
|
||||
String.format(
|
||||
"File \"/\" in theme %s is not a directory.",
|
||||
themeName),
|
||||
Response.Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
} else {
|
||||
return Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(String.format(
|
||||
"File \"/\" does not exist in theme %s.",
|
||||
themeName))
|
||||
.build();
|
||||
}
|
||||
|
||||
} else {
|
||||
return Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(String.format("Theme \"%s\" does not exist.",
|
||||
themeName))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/themes/{theme}/files/{path:.+}")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public Response getThemeFile(@PathParam("theme") final String themeName,
|
||||
@PathParam("path") final String path) {
|
||||
|
||||
final Optional<ThemeProvider> provider = findProvider(themeName);
|
||||
|
||||
if (provider.isPresent()) {
|
||||
|
||||
final Optional<ThemeFileInfo> fileInfo = provider
|
||||
.get()
|
||||
.getThemeFileInfo(themeName, ThemeVersion.DRAFT, path);
|
||||
|
||||
if (fileInfo.isPresent()) {
|
||||
|
||||
final ThemeFileInfo themeFileInfo = fileInfo.get();
|
||||
|
||||
if (themeFileInfo.isDirectory()) {
|
||||
|
||||
final List<ThemeFileInfo> fileInfos = provider
|
||||
.get()
|
||||
.listThemeFiles(themeName, ThemeVersion.DRAFT, path);
|
||||
|
||||
return Response
|
||||
.ok(fileInfos)
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.build();
|
||||
|
||||
} else {
|
||||
final Optional<InputStream> inputStream = provider
|
||||
.get()
|
||||
.getThemeFileAsStream(themeName,
|
||||
ThemeVersion.DRAFT,
|
||||
path);
|
||||
|
||||
if (inputStream.isPresent()) {
|
||||
|
||||
final InputStream inStream = inputStream.get();
|
||||
return Response
|
||||
.ok(inStream)
|
||||
.type(themeFileInfo.getMimeType())
|
||||
.build();
|
||||
} else {
|
||||
return Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(String.format(
|
||||
"File \"%s\" does not exist in theme %s.",
|
||||
path,
|
||||
themeName))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(String.format(
|
||||
"File \"%s\" does not exist in theme %s.",
|
||||
path,
|
||||
themeName))
|
||||
.build();
|
||||
}
|
||||
|
||||
} else {
|
||||
return Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(String.format("Theme \"%s\" does not exist.",
|
||||
themeName))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
private String getProviderName(final ThemeProvider provider) {
|
||||
|
|
|
|||
17
pom.xml
17
pom.xml
|
|
@ -463,6 +463,23 @@
|
|||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Json Web Token support -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>0.10.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.10.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>0.10.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- PrimeFaces for JSF prototype -->
|
||||
<dependency>
|
||||
<groupId>org.primefaces</groupId>
|
||||
|
|
|
|||
Loading…
Reference in New Issue