From 9b3847343d9753f55ca258624b23b2393cfd327e Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Thu, 5 Aug 2021 15:30:01 +0200 Subject: [PATCH 1/8] PR92 - implemented ApiKey/Token authentication --- .../org/deegree/services/config/ApiKey.java | 182 ++++++++++++++++++ .../config/servlet/ConfigServlet.java | 6 +- 2 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java diff --git a/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java b/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java new file mode 100644 index 0000000000..4cf10c7230 --- /dev/null +++ b/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java @@ -0,0 +1,182 @@ +package org.deegree.services.config; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.util.Base64; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.List; +import java.util.Random; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.codec.digest.DigestUtils; +import org.deegree.commons.config.DeegreeWorkspace; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handle access to an API key file containing a token + * + * @author Stephan Reichhelm + * @see org.deegree.console.security.PasswordFile + */ +public class ApiKey { + + private static final Logger LOG = LoggerFactory.getLogger( ApiKey.class ); + + private static final String API_TOKEN_FILE = "config.apikey"; + + /** + * Token to be checked + * + * Every value matches this token if the value is "*". No value matches this token if the value is null or an empty + * string. + */ + class Token { + + final boolean allowAll; + + private final String key; + + public Token( String value ) { + this.allowAll = value != null && "*".equals( value.trim() ); + this.key = value != null && value.trim().length() > 0 ? value.trim() : value; + } + + public Token() { + this.allowAll = false; + this.key = null; + } + + public boolean matches( String value ) { + if ( allowAll ) + return true; + + if ( key == null ) + return false; + + return key.matches( value != null ? value.trim() : value ); + } + + public boolean isAnyAllowed() { + return allowAll; + } + } + + private Path getPasswordFile() { + String workspace = DeegreeWorkspace.getWorkspaceRoot(); + return Paths.get( workspace, API_TOKEN_FILE ); + } + + private String generateRandomApiKey() { + try { + MessageDigest md = DigestUtils.getSha1Digest(); + // add some random data + Random rnd = new Random(); + byte[] data = new byte[128]; + rnd.nextBytes( data ); + // add random data + md.update( data ); + md.update( new Date().toString().getBytes() ); + byte[] digest = md.digest(); + + return Hex.encodeHexString( digest ); + } catch ( Exception ex ) { + LOG.warn( "Could not generate random key with SHA-1: {}", ex.getMessage() ); + LOG.trace( "Exception", ex ); + } + return null; + } + + public Token getCurrentToken() + throws SecurityException { + Path file = getPasswordFile(); + Token token = null; + + try { + if ( Files.isReadable( file ) ) { + List lines = Files.readAllLines( file ); + if ( lines.size() != 1 ) { + LOG.warn( "API Key file ({}) has incorrect format.", file ); + throw new IOException( "API Key file has incorrect format." ); + } + token = new Token( lines.get( 0 ) ); + } else if ( !Files.exists( file ) ) { + // create new one, if no file exists + String apikey = generateRandomApiKey(); + Files.write( file, Collections.singleton( apikey ) ); + token = new Token( apikey ); + LOG.warn( "Create API Key file ({}) with the generated value of \"{}\"", file, apikey ); + } else { + LOG.info( "API Key file ({}) is not a regular file or not readable, access prohibited " ); + } + } catch ( IOException ioe ) { + throw new SecurityException( "API key file could not be accessed", ioe ); + } + + if ( token == null ) { + token = new Token(); + } + + return token; + } + + public void validate( HttpServletRequest req ) + throws SecurityException { + String tmp, value = null; + // check for headers + if ( value == null ) { + value = req.getHeader( "X-API-Key" ); + } + if ( value == null ) { + tmp = req.getHeader( "Authorization" ); + if ( tmp != null && tmp.toLowerCase().startsWith( "bearer " ) ) { + value = tmp.substring( 7 ); + } else if ( tmp != null && tmp.toLowerCase().startsWith( "basic " ) ) { + tmp = tmp.substring( 6 ); + final byte[] decoded = Base64.getDecoder().decode( tmp ); + final String credentials = new String( decoded, StandardCharsets.UTF_8 ); + // credentials = username:password + final String[] values = credentials.split( ":", 2 ); + if ( values.length == 2 && values[1] != null ) { + value = values[1]; + } + } + } + + // check for parameter + if ( value == null ) { + Enumeration keys = req.getParameterNames(); + while ( keys.hasMoreElements() ) { + String key = (String) keys.nextElement(); + if ( "token".equalsIgnoreCase( key ) || "api_key".equalsIgnoreCase( key ) ) { + value = req.getParameter( key ); + break; + } + } + } + + // initialize early to allow creation of apikey/token + Token token = getCurrentToken(); + + if ( token.isAnyAllowed() ) { + // no API Key required + return; + } + + if ( value == null || value.trim().length() == 0 ) { + throw new SecurityException( "Please specify API Key" ); + } + + if ( !token.matches( value ) ) { + throw new SecurityException( "Invalid API Key specified" ); + } + } +} diff --git a/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/servlet/ConfigServlet.java b/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/servlet/ConfigServlet.java index 9850348c33..6c0aea60e5 100644 --- a/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/servlet/ConfigServlet.java +++ b/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/servlet/ConfigServlet.java @@ -56,6 +56,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; +import org.deegree.services.config.ApiKey; import org.slf4j.Logger; /** @@ -70,6 +71,8 @@ public class ConfigServlet extends HttpServlet { private static final long serialVersionUID = -4412872621677620591L; private static final Logger LOG = getLogger( ConfigServlet.class ); + + private static ApiKey token = new ApiKey(); @Override public void init() @@ -124,6 +127,8 @@ protected void doGet( HttpServletRequest req, HttpServletResponse resp ) private void dispatch( String path, HttpServletRequest req, HttpServletResponse resp ) throws IOException, ServletException { + token.validate( req ); + if ( path.toLowerCase().startsWith( "/download" ) ) { download( path.substring( 9 ), resp ); } @@ -186,5 +191,4 @@ protected void doPut( HttpServletRequest req, HttpServletResponse resp ) dispatch( path, req, resp ); } } - } From 41bef41465810fdbb5091c1b6a16d797836cdb29 Mon Sep 17 00:00:00 2001 From: Stephan Reichhelm Date: Thu, 5 Aug 2021 15:30:01 +0200 Subject: [PATCH 2/8] PR92 - implemented ApiKey/Token authentication --- .../org/deegree/services/config/ApiKey.java | 182 ++++++++++++++++++ .../config/servlet/ConfigServlet.java | 6 +- 2 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java diff --git a/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java b/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java new file mode 100644 index 0000000000..4cf10c7230 --- /dev/null +++ b/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java @@ -0,0 +1,182 @@ +package org.deegree.services.config; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.util.Base64; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.List; +import java.util.Random; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.codec.digest.DigestUtils; +import org.deegree.commons.config.DeegreeWorkspace; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handle access to an API key file containing a token + * + * @author Stephan Reichhelm + * @see org.deegree.console.security.PasswordFile + */ +public class ApiKey { + + private static final Logger LOG = LoggerFactory.getLogger( ApiKey.class ); + + private static final String API_TOKEN_FILE = "config.apikey"; + + /** + * Token to be checked + * + * Every value matches this token if the value is "*". No value matches this token if the value is null or an empty + * string. + */ + class Token { + + final boolean allowAll; + + private final String key; + + public Token( String value ) { + this.allowAll = value != null && "*".equals( value.trim() ); + this.key = value != null && value.trim().length() > 0 ? value.trim() : value; + } + + public Token() { + this.allowAll = false; + this.key = null; + } + + public boolean matches( String value ) { + if ( allowAll ) + return true; + + if ( key == null ) + return false; + + return key.matches( value != null ? value.trim() : value ); + } + + public boolean isAnyAllowed() { + return allowAll; + } + } + + private Path getPasswordFile() { + String workspace = DeegreeWorkspace.getWorkspaceRoot(); + return Paths.get( workspace, API_TOKEN_FILE ); + } + + private String generateRandomApiKey() { + try { + MessageDigest md = DigestUtils.getSha1Digest(); + // add some random data + Random rnd = new Random(); + byte[] data = new byte[128]; + rnd.nextBytes( data ); + // add random data + md.update( data ); + md.update( new Date().toString().getBytes() ); + byte[] digest = md.digest(); + + return Hex.encodeHexString( digest ); + } catch ( Exception ex ) { + LOG.warn( "Could not generate random key with SHA-1: {}", ex.getMessage() ); + LOG.trace( "Exception", ex ); + } + return null; + } + + public Token getCurrentToken() + throws SecurityException { + Path file = getPasswordFile(); + Token token = null; + + try { + if ( Files.isReadable( file ) ) { + List lines = Files.readAllLines( file ); + if ( lines.size() != 1 ) { + LOG.warn( "API Key file ({}) has incorrect format.", file ); + throw new IOException( "API Key file has incorrect format." ); + } + token = new Token( lines.get( 0 ) ); + } else if ( !Files.exists( file ) ) { + // create new one, if no file exists + String apikey = generateRandomApiKey(); + Files.write( file, Collections.singleton( apikey ) ); + token = new Token( apikey ); + LOG.warn( "Create API Key file ({}) with the generated value of \"{}\"", file, apikey ); + } else { + LOG.info( "API Key file ({}) is not a regular file or not readable, access prohibited " ); + } + } catch ( IOException ioe ) { + throw new SecurityException( "API key file could not be accessed", ioe ); + } + + if ( token == null ) { + token = new Token(); + } + + return token; + } + + public void validate( HttpServletRequest req ) + throws SecurityException { + String tmp, value = null; + // check for headers + if ( value == null ) { + value = req.getHeader( "X-API-Key" ); + } + if ( value == null ) { + tmp = req.getHeader( "Authorization" ); + if ( tmp != null && tmp.toLowerCase().startsWith( "bearer " ) ) { + value = tmp.substring( 7 ); + } else if ( tmp != null && tmp.toLowerCase().startsWith( "basic " ) ) { + tmp = tmp.substring( 6 ); + final byte[] decoded = Base64.getDecoder().decode( tmp ); + final String credentials = new String( decoded, StandardCharsets.UTF_8 ); + // credentials = username:password + final String[] values = credentials.split( ":", 2 ); + if ( values.length == 2 && values[1] != null ) { + value = values[1]; + } + } + } + + // check for parameter + if ( value == null ) { + Enumeration keys = req.getParameterNames(); + while ( keys.hasMoreElements() ) { + String key = (String) keys.nextElement(); + if ( "token".equalsIgnoreCase( key ) || "api_key".equalsIgnoreCase( key ) ) { + value = req.getParameter( key ); + break; + } + } + } + + // initialize early to allow creation of apikey/token + Token token = getCurrentToken(); + + if ( token.isAnyAllowed() ) { + // no API Key required + return; + } + + if ( value == null || value.trim().length() == 0 ) { + throw new SecurityException( "Please specify API Key" ); + } + + if ( !token.matches( value ) ) { + throw new SecurityException( "Invalid API Key specified" ); + } + } +} diff --git a/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/servlet/ConfigServlet.java b/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/servlet/ConfigServlet.java index 9850348c33..6c0aea60e5 100644 --- a/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/servlet/ConfigServlet.java +++ b/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/servlet/ConfigServlet.java @@ -56,6 +56,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; +import org.deegree.services.config.ApiKey; import org.slf4j.Logger; /** @@ -70,6 +71,8 @@ public class ConfigServlet extends HttpServlet { private static final long serialVersionUID = -4412872621677620591L; private static final Logger LOG = getLogger( ConfigServlet.class ); + + private static ApiKey token = new ApiKey(); @Override public void init() @@ -124,6 +127,8 @@ protected void doGet( HttpServletRequest req, HttpServletResponse resp ) private void dispatch( String path, HttpServletRequest req, HttpServletResponse resp ) throws IOException, ServletException { + token.validate( req ); + if ( path.toLowerCase().startsWith( "/download" ) ) { download( path.substring( 9 ), resp ); } @@ -186,5 +191,4 @@ protected void doPut( HttpServletRequest req, HttpServletResponse resp ) dispatch( path, req, resp ); } } - } From 73fea09c2d0fd32a58fc480b0ac217f3306a38ed Mon Sep 17 00:00:00 2001 From: Lyn Elisa Goltz Date: Thu, 16 Dec 2021 07:03:57 +0100 Subject: [PATCH 3/8] #7893 - added config.apikey to fix WorkspaceIT --- deegree-tests/deegree-workspace-tests/src/main/.gitignore | 1 - .../src/main/webapp/WEB-INF/workspaces/config.apikey | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 deegree-tests/deegree-workspace-tests/src/main/.gitignore create mode 100644 deegree-tests/deegree-workspace-tests/src/main/webapp/WEB-INF/workspaces/config.apikey diff --git a/deegree-tests/deegree-workspace-tests/src/main/.gitignore b/deegree-tests/deegree-workspace-tests/src/main/.gitignore deleted file mode 100644 index 1b93c9eb4d..0000000000 --- a/deegree-tests/deegree-workspace-tests/src/main/.gitignore +++ /dev/null @@ -1 +0,0 @@ -webapp diff --git a/deegree-tests/deegree-workspace-tests/src/main/webapp/WEB-INF/workspaces/config.apikey b/deegree-tests/deegree-workspace-tests/src/main/webapp/WEB-INF/workspaces/config.apikey new file mode 100644 index 0000000000..f59ec20aab --- /dev/null +++ b/deegree-tests/deegree-workspace-tests/src/main/webapp/WEB-INF/workspaces/config.apikey @@ -0,0 +1 @@ +* \ No newline at end of file From e91863c54978ced60338dc1ccde4deb1fff381ae Mon Sep 17 00:00:00 2001 From: Stephan Reichhelm Date: Fri, 12 May 2023 11:44:50 +0200 Subject: [PATCH 4/8] * rework to print information on startup (when ConfigServlet is started) * Change wording to be more precise --- .../org/deegree/services/config/ApiKey.java | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java b/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java index 4cf10c7230..f1836f951c 100644 --- a/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java +++ b/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java @@ -25,7 +25,6 @@ * Handle access to an API key file containing a token * * @author Stephan Reichhelm - * @see org.deegree.console.security.PasswordFile */ public class ApiKey { @@ -99,30 +98,57 @@ public Token getCurrentToken() throws SecurityException { Path file = getPasswordFile(); Token token = null; + final String ls = System.lineSeparator(); + final String marker = "*************************************************************" + ls; try { if ( Files.isReadable( file ) ) { List lines = Files.readAllLines( file ); if ( lines.size() != 1 ) { - LOG.warn( "API Key file ({}) has incorrect format.", file ); - throw new IOException( "API Key file has incorrect format." ); + LOG.warn( "{}API Key file '{}' has an incorrect format (multiple lines). {} " + // + "The REST API will not be accessible. {}", // + ls + ls + marker + marker + marker + ls, // + file, ls, // + ls + marker + marker + marker ); + } else { + token = new Token( lines.get( 0 ) ); } - token = new Token( lines.get( 0 ) ); } else if ( !Files.exists( file ) ) { // create new one, if no file exists String apikey = generateRandomApiKey(); Files.write( file, Collections.singleton( apikey ) ); token = new Token( apikey ); - LOG.warn( "Create API Key file ({}) with the generated value of \"{}\"", file, apikey ); + LOG.warn( "{}An API Key file with an random key was generated at '{}'.{}", // + ls + ls + marker + marker + marker + ls, // + file, ls, // + ls + marker + marker + marker ); } else { - LOG.info( "API Key file ({}) is not a regular file or not readable, access prohibited " ); + LOG.warn( "{}API Key file '{}' is not a regular file or not readable. {} " + // + "The REST API will not be accessible.{}", // + ls + ls + marker + marker + marker + ls, // + file, ls, // + ls + marker + marker + marker ); } } catch ( IOException ioe ) { - throw new SecurityException( "API key file could not be accessed", ioe ); + LOG.warn( "{}API Key file '{}' could not be accessed. {} " + // + "The REST API will not be accessible.{}", // + ls + ls + marker + marker + marker + ls, // + file, ls, // + ls + marker + marker + marker ); + LOG.debug("API key file could not be accessed", ioe); } if ( token == null ) { token = new Token(); + } else if ( token.isAnyAllowed() ) { + LOG.warn( "{}The REST API is currently configured insecure. We strongly recommend to use a key value instead at '{}'.{}", + ls + ls + marker + marker + marker + ls, // + file, // + ls + marker + marker + marker ); + } else { + LOG.info( "***" ); + LOG.info( "*** NOTE: The REST API is secured, so that the key set in file '{}' is required to access it." ); + LOG.info( "***" ); } return token; From edd7db1a73b8af6785f598ba8a466550b619fb2c Mon Sep 17 00:00:00 2001 From: Stephan Reichhelm Date: Fri, 12 May 2023 11:59:39 +0200 Subject: [PATCH 5/8] * disable security-constraint in favor of REST API key --- .../deegree-webservices/src/main/webapp/WEB-INF/web.xml | 3 +++ .../deegree-testservice/src/main/webapp/WEB-INF/web.xml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/deegree-services/deegree-webservices/src/main/webapp/WEB-INF/web.xml b/deegree-services/deegree-webservices/src/main/webapp/WEB-INF/web.xml index 82d861b41a..e73e8f6ec9 100644 --- a/deegree-services/deegree-webservices/src/main/webapp/WEB-INF/web.xml +++ b/deegree-services/deegree-webservices/src/main/webapp/WEB-INF/web.xml @@ -91,6 +91,8 @@ index.xhtml + + diff --git a/deegree-tests/deegree-testservice/src/main/webapp/WEB-INF/web.xml b/deegree-tests/deegree-testservice/src/main/webapp/WEB-INF/web.xml index a5dd974a41..bdbe44c3c1 100644 --- a/deegree-tests/deegree-testservice/src/main/webapp/WEB-INF/web.xml +++ b/deegree-tests/deegree-testservice/src/main/webapp/WEB-INF/web.xml @@ -56,6 +56,8 @@ index.xhtml + + From b27e20e4ae43aab1c2e089e086524355c9425251 Mon Sep 17 00:00:00 2001 From: Stephan Reichhelm Date: Fri, 12 May 2023 12:51:18 +0200 Subject: [PATCH 6/8] * modify documentation to include api key --- .../org/deegree/services/config/ApiKey.java | 11 +++++++---- .../src/main/asciidoc/appendix.adoc | 2 ++ .../src/main/asciidoc/basics.adoc | 5 +++++ .../src/main/asciidoc/restapi.adoc | 17 +++++++++++++---- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java b/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java index f1836f951c..8ef8502e1f 100644 --- a/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java +++ b/deegree-services/deegree-services-config/src/main/java/org/deegree/services/config/ApiKey.java @@ -18,6 +18,7 @@ import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.DigestUtils; import org.deegree.commons.config.DeegreeWorkspace; +import org.deegree.commons.utils.TunableParameter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -141,10 +142,12 @@ public Token getCurrentToken() if ( token == null ) { token = new Token(); } else if ( token.isAnyAllowed() ) { - LOG.warn( "{}The REST API is currently configured insecure. We strongly recommend to use a key value instead at '{}'.{}", - ls + ls + marker + marker + marker + ls, // - file, // - ls + marker + marker + marker ); + if ( TunableParameter.get( "deegree.config.apikey.warn-when-disabled", true ) ) { + LOG.warn( "{}The REST API is currently configured insecure. We strongly recommend to use a key value instead at '{}'.{}", + ls + ls + marker + marker + marker + ls, // + file, // + ls + marker + marker + marker ); + } } else { LOG.info( "***" ); LOG.info( "*** NOTE: The REST API is secured, so that the key set in file '{}' is required to access it." ); diff --git a/deegree-services/deegree-webservices-handbook/src/main/asciidoc/appendix.adoc b/deegree-services/deegree-webservices-handbook/src/main/asciidoc/appendix.adoc index 609a4cb9e8..16e4c5e316 100644 --- a/deegree-services/deegree-webservices-handbook/src/main/asciidoc/appendix.adoc +++ b/deegree-services/deegree-webservices-handbook/src/main/asciidoc/appendix.adoc @@ -57,4 +57,6 @@ f |deegree.gml.property.simple.trim |java.lang.Boolean |true |When deegree reads GML data, by default (`true`) simple property values get their leading and trailing whitespace characters removed. +|deegree.config.apikey.warn-when-disabled |java.lang.Boolean |true |Log warning if security on REST api is disabled by specifying `*` in _config.apikey_. + |=== \ No newline at end of file diff --git a/deegree-services/deegree-webservices-handbook/src/main/asciidoc/basics.adoc b/deegree-services/deegree-webservices-handbook/src/main/asciidoc/basics.adoc index 49ec77a61c..2d867f3c6c 100644 --- a/deegree-services/deegree-webservices-handbook/src/main/asciidoc/basics.adoc +++ b/deegree-services/deegree-webservices-handbook/src/main/asciidoc/basics.adoc @@ -138,6 +138,7 @@ files exist: |console.pw |Password for services console |proxy.xml |Proxy settings |webapps.properties |Selects the active workspace +|config.apikey |Contains the key to protect the REST API |=== Note that only a single workspace can be active at a time. The @@ -156,6 +157,10 @@ every instance can use a different workspace. The file _webapps.properties_ stores the active workspace for every deegree webapp separately. +TIP: If there is no _config.apikey_ file, one will be generated on startup +with an random value. A value of `*` will turn it off but we strongly advise +against doing this in productive environments. + === Structure of the deegree workspace directory The workspace directory is a container for resource files with a diff --git a/deegree-services/deegree-webservices-handbook/src/main/asciidoc/restapi.adoc b/deegree-services/deegree-webservices-handbook/src/main/asciidoc/restapi.adoc index a111b62998..28cf98d121 100644 --- a/deegree-services/deegree-webservices-handbook/src/main/asciidoc/restapi.adoc +++ b/deegree-services/deegree-webservices-handbook/src/main/asciidoc/restapi.adoc @@ -9,10 +9,19 @@ workspaces or resources and start a different workspace. The servlet that handles the REST interface is already running if you use the standard _web.xml_ deployment descriptor. For security reasons -the REST API is only accessible after successful authentication against -the servlet container. When using Apache Tomcat you'll need to add a -user with the role _deegree_ to your Tomcat configuration -_conf/tomcat-users.xml_ file. +the REST API is secured by default with an API key read from the +_config.apikey_ file in deegree workspace directory. + +The API key can be provide in multiple different ways. +* As header value of key `X-API-Key` +* As authorization header of bearer type +* As basic authorization password where username will be ignored +* As parameter `token` +* As parameter `api_key` + +TIP: If there is no _config.apikey_ file, one will be generated on startup +with an random value. A value of `*` will turn it off but we strongly advise +against doing this in productive environments. Once you did that, you can get an overview of available 'commands' by requesting _http://localhost:8080/deegree-webservices/config_. You'll From 6fb2b92838a0d1981bbfaaad5f0cf7437e5219c1 Mon Sep 17 00:00:00 2001 From: Stephan Reichhelm Date: Fri, 12 May 2023 13:23:03 +0200 Subject: [PATCH 7/8] * change to suggested wording --- .../src/main/asciidoc/basics.adoc | 5 +++-- .../src/main/asciidoc/restapi.adoc | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/deegree-services/deegree-webservices-handbook/src/main/asciidoc/basics.adoc b/deegree-services/deegree-webservices-handbook/src/main/asciidoc/basics.adoc index 2d867f3c6c..b26cf3a90c 100644 --- a/deegree-services/deegree-webservices-handbook/src/main/asciidoc/basics.adoc +++ b/deegree-services/deegree-webservices-handbook/src/main/asciidoc/basics.adoc @@ -158,8 +158,9 @@ _webapps.properties_ stores the active workspace for every deegree webapp separately. TIP: If there is no _config.apikey_ file, one will be generated on startup -with an random value. A value of `*` will turn it off but we strongly advise -against doing this in productive environments. +with an random value. Alternatively, a value of `*` in config.apikey will +turn off security for the REST API. We strongly advise against doing this +in productive environments. === Structure of the deegree workspace directory diff --git a/deegree-services/deegree-webservices-handbook/src/main/asciidoc/restapi.adoc b/deegree-services/deegree-webservices-handbook/src/main/asciidoc/restapi.adoc index 28cf98d121..f8bc764f6b 100644 --- a/deegree-services/deegree-webservices-handbook/src/main/asciidoc/restapi.adoc +++ b/deegree-services/deegree-webservices-handbook/src/main/asciidoc/restapi.adoc @@ -20,8 +20,9 @@ The API key can be provide in multiple different ways. * As parameter `api_key` TIP: If there is no _config.apikey_ file, one will be generated on startup -with an random value. A value of `*` will turn it off but we strongly advise -against doing this in productive environments. +with an random value. Alternatively, a value of `*` in config.apikey will +turn off security for the REST API. We strongly advise against doing this +in productive environments. Once you did that, you can get an overview of available 'commands' by requesting _http://localhost:8080/deegree-webservices/config_. You'll From 2c1b5a0d5d4faada41074112d0ba0a0386876217 Mon Sep 17 00:00:00 2001 From: Stephan Reichhelm Date: Fri, 12 May 2023 14:48:02 +0200 Subject: [PATCH 8/8] prevent test from failing because apikey is not used in testcases --- .../src/main/webapp/WEB-INF/workspaces/config.apikey | 1 + 1 file changed, 1 insertion(+) create mode 100644 deegree-tests/deegree-integration-tests/src/main/webapp/WEB-INF/workspaces/config.apikey diff --git a/deegree-tests/deegree-integration-tests/src/main/webapp/WEB-INF/workspaces/config.apikey b/deegree-tests/deegree-integration-tests/src/main/webapp/WEB-INF/workspaces/config.apikey new file mode 100644 index 0000000000..f59ec20aab --- /dev/null +++ b/deegree-tests/deegree-integration-tests/src/main/webapp/WEB-INF/workspaces/config.apikey @@ -0,0 +1 @@ +* \ No newline at end of file