diff --git a/docs/mp/cors/01_introduction.adoc b/docs/mp/cors/01_introduction.adoc index 195497147..ea2da5003 100644 --- a/docs/mp/cors/01_introduction.adoc +++ b/docs/mp/cors/01_introduction.adoc @@ -28,6 +28,7 @@ :quickstart-example: {helidon-tag}/examples/quickstarts/helidon-quickstart-mp :cors-spec: https://www.w3.org/TR/cors/ :helidon-mp-cors-example: {helidon-tag}/examples/microprofile/cors +:common-page-prefix-inc: ../../shared/cors/common_shared.adoc :se-pages-prefix-inc: ../../se/cors :se-intro-page-inc: {se-pages-prefix-inc}/01_introduction.adoc :mp-pages-ref-prefix: mp/cors @@ -42,7 +43,7 @@ even if that web application is served from a different domain. == Overview -include::{se-intro-page-inc}[tag=cors-intro] +include::{common-page-prefix-inc}[tag=cors-intro] == Next Steps To introduce CORS into your Helidon MP application, do any or all of the following: diff --git a/docs/mp/cors/02_using-cors.adoc b/docs/mp/cors/02_using-cors.adoc index eb4f23a8c..276330a80 100644 --- a/docs/mp/cors/02_using-cors.adoc +++ b/docs/mp/cors/02_using-cors.adoc @@ -28,12 +28,10 @@ :quickstart-example: {helidon-tag}/examples/quickstarts/helidon-quickstart-mp :cors-spec: https://www.w3.org/TR/cors/ :helidon-mp-cors-example: {helidon-tag}/examples/microprofile/cors -:se-cors-home-inc: ../../se/cors -:se-cors-dependency-page: 02_using-the-api.adoc :mp-pages-ref-prefix: mp/cors :mp-cors-config-ref: {mp-pages-ref-prefix}/03_configuration-with-cors-mp.adoc -:dependency-inc: {se-cors-home-inc}/{se-cors-dependency-page} :helidon-variant: MP +:common-page-prefix-inc: ../../shared/cors/common_shared.adoc To enable CORS behavior for a resource in your Helidon MP application, you need to make only minimal changes to the JAX-RS resource class that implements your resource. @@ -66,7 +64,7 @@ To add CORS support to your Helidon MP application: -- Add a dependency on the Helidon {helidon-variant} CORS artifact to your Maven `pom.xml` file. -include::{dependency-inc}[tag=add-cors-dependency] +include::{common-page-prefix-inc}[tag=add-cors-dependency] // tag::actual-cors-dependency[] [source,xml,subs="attributes+"] ---- diff --git a/docs/mp/cors/03_configuration-with-cors-mp.adoc b/docs/mp/cors/03_configuration-with-cors-mp.adoc index 9db828696..283fa9e73 100644 --- a/docs/mp/cors/03_configuration-with-cors-mp.adoc +++ b/docs/mp/cors/03_configuration-with-cors-mp.adoc @@ -39,16 +39,23 @@ :mp-cors-builtin-services-ref: {mp-pages-ref-prefix}/04_support-in-builtin-services.adoc :mapped-config-top-key: cors :mapped-config-id-callout: The unique identifier for this mapped CORS config section must be `cors`. +:common-page-prefix-inc: ../../shared/cors/common_shared.adoc +:config-table-methods-column-explainer: the annotation parameters +:config-table-methods-column-header: Annotation Parameter +:basic-table-intro: The table below lists the annotation parameters and the configuration keys that identify the CORS characteristics. Your application code establishes the CORS behavior of your endpoints using the `@CrossOrigin` annotation. You and your users can override that behavior, as well as the CORS behavior of the built-in services, using MicroProfile configuration. +include::{common-page-prefix-inc}[tag=cors-configuration-formats-intro] -== Understanding the Mapped Cross-Origin Configuration Format +include::{common-page-prefix-inc}[tag=basic-cross-origin-config] -There are two formats of CORS cross-origin configuration: basic and mapped. For Helidon MP, you use -the mapped format. +=== Understanding the Mapped Cross-Origin Configuration Format + +In Helidon MP, you use +the mapped cross-origin configuration format. // We want to use // include::{cors-mapped-config-src}[tag=mapped-config] @@ -56,19 +63,13 @@ the mapped format. // text, we'd have to be a little clever in the original callout. And that cleverness works when // the document is rendered in our editors, and even when using asciidoctor to HTML, but not // to our site. -include::{cors-mapped-config-src}[tag=mapped-config-prefix] -<1> The unique identifier for this mapped CORS config section must be `cors`. -include::{cors-mapped-config-src}[tag=mapped-config-suffix] +include::{common-page-prefix-inc}[tag=mapped-config-prefix] +<1> {mapped-config-id-callout} +include::{common-page-prefix-inc}[tag=mapped-config-suffix] -== Specifying Override Values in Configuration [[cors-configuration-formats]] +== Specifying Override Values in Configuration In configuration, you can specify the same CORS-related attributes that you specify using the `@CrossOrigin` annotation. -[[config-key-table]] -:config-table-methods-column-explainer: the annotation parameters -:config-table-methods-column-header: Annotation Parameter -include::{cors-config-table-src}[tag=cors-config-table] - - The following example shows how you can express configuration similar to that shown previously using the mapped cross-origin configuration format. Here, the example uses properties-file syntax diff --git a/docs/mp/cors/04_support-in-builtin-services.adoc b/docs/mp/cors/04_support-in-builtin-services.adoc index c4c2c20b8..398421821 100644 --- a/docs/mp/cors/04_support-in-builtin-services.adoc +++ b/docs/mp/cors/04_support-in-builtin-services.adoc @@ -42,19 +42,22 @@ :cors-se-api-doc: ../../se/cors/02_using-the-api.adoc // Following is resolved in an SE file, so the relative path must lead to the MP file. :cors-mp-using-cors-page-inc: ../../mp/cors/02_using-cors.adoc +:mp-pages-ref-prefix: mp/cors +:mp-cors-config-ref: {mp-pages-ref-prefix}/03_configuration-with-cors-mp.adoc :actual-cors-dependency-src: {cors-mp-using-cors-page-inc} :se-cors-home-inc: ../../se/cors :se-cors-dependency-page: 02_using-the-api.adoc :dependency-inc: {se-cors-home-inc}/{se-cors-dependency-page} +:common-page-prefix-inc: ../../shared/cors/common_shared.adoc Several built-in Helidon services -- health, metrics, and OpenAPI -- have integrated CORS support. You can include these services in your application and control their CORS behavior. -include::{se-built-in-services-page-include}[tag=understanding-cors-support-in-services] +include::{common-page-prefix-inc}[tag=understanding-cors-support-in-services] -include::{se-built-in-services-page-include}[tag=builtin-getting-started] +include::{common-page-prefix-inc}[tag=builtin-getting-started] -include::{se-built-in-services-page-include}[tags=configuring-cors-for-builtin-services;!se-config-example;!se-code-changes-for-builtin-services-config] +include::{common-page-prefix-inc}[tags=configuring-cors-for-builtin-services;!se-config-example;!se-code-changes-for-builtin-services-config] The following example restricts sharing of @@ -73,7 +76,7 @@ metrics: ... ---- -include::{se-built-in-services-page-include}[tag=accessing-shared-resources-intro] +include::{common-page-prefix-inc}[tag=accessing-shared-resources-intro] [source,bash] ---- mvn package @@ -83,4 +86,4 @@ java -jar target/helidon-quickstart-mp.jar ... ---- -include::{se-built-in-services-page-include}[tag=accessing-shared-resources-main] +include::{common-page-prefix-inc}[tag=accessing-shared-resources-main] diff --git a/docs/se/cors/01_introduction.adoc b/docs/se/cors/01_introduction.adoc index 7e79a009c..9726ddb0e 100644 --- a/docs/se/cors/01_introduction.adoc +++ b/docs/se/cors/01_introduction.adoc @@ -32,33 +32,17 @@ :javadoc-base-url-api: {javadoc-base-url}io.helidon.webserver.cors/io/helidon/webserver/cors :javadoc-base-url-webserver: {javadoc-base-url}io.helidon.webserver/io/helidon/webserver :helidon-variant: SE +:common-page-prefix-inc: ../../shared/cors/common_shared.adoc + link:{cors-spec}[Cross-origin resource sharing] (CORS) support in Helidon SE provides a flexible mechanism that allows a Helidon SE application to control how another web application can access its resources, even if that web application is served from a different domain. == Overview -// tag::cors-intro[] -The CORS protocol helps developers control if and how REST resources served by their applications can be shared across origins. -Helidon {helidon-variant} includes an implementation of CORS that you can use to add CORS behavior -to the services you develop. You can define your application's CORS behavior programmatically using the Helidon CORS API alone, or -together with configuration. Helidon also provides three built-in services that add their -own endpoints to your application - health, metrics, and OpenAPI - that have integrated CORS support. -By adding very little code to your application, you control how all the resources in -your application -- the ones you write and the ones provided by the Helidon built-in services -- can be shared across origins. +include::{common-page-prefix-inc}[tag=cors-intro] -== Before You Begin -Before you revise your application to add CORS support, you need to decide what type of cross-origin sharing you want -to allow for each resource your application exposes. -For example, suppose for a given resource you want to allow unrestricted sharing for GET, HEAD, and POST requests -(what CORS refers to as "simple" requests), but permit other types of requests only from the two -origins `foo.com` and `there.com`. -Your application would implement two types of CORS sharing: more relaxed for the -simple requests and stricter for others. -Once you know the type of sharing you want to allow for each of your resources -- including any from built-in -services -- you can change your application accordingly. -// end::cors-intro[] == Next Steps diff --git a/docs/se/cors/02_using-the-api.adoc b/docs/se/cors/02_using-the-api.adoc index bd1e37a7f..1edefb823 100644 --- a/docs/se/cors/02_using-the-api.adoc +++ b/docs/se/cors/02_using-the-api.adoc @@ -34,6 +34,7 @@ :helidon-variant: SE :config-table-methods-column-header: Method :cors-config-table-exclude-keys: +:common-page-prefix-inc: ../../shared/cors/common_shared.adoc Every Helidon SE application explicitly creates routing rules that govern how Helidon delivers each incoming request to the code that needs to respond. The Helidon CORS SE API provides a simple way to include CORS into @@ -73,18 +74,8 @@ application. -- Add a dependency on the Helidon {helidon-variant} CORS artifact to your Maven `pom.xml` file. +include::{common-page-prefix-inc}[tag=add-cors-dependency] -// The add-cors-dependency tag's contents is reused from other SE and MP pages. -// The actual dependency is different for SE and MP, so we tag it separately from the intro text so the -// MP pages can reuse this intro text but use their own "actual" dependency. We could have parameterized -// the groupID and artifactID but this approach allows the actual dependencies themselves to be -// in the source literally rather than parameterized. -// tag::add-cors-dependency[] -The <> page describes how you -should declare dependency management for Helidon applications. -For CORS support in Helidon {helidon-variant}, you must include -the following dependency in your project: -// end::add-cors-dependency[] // tag::actual-cors-dependency[] [source,xml,subs="attributes+"] ---- @@ -121,69 +112,7 @@ Each of these classes has an associated builder that you use in constructing ins The table below describes the methods on the `CrossOriginConfig.Builder` class that map to the headers defined in the CORS protocol. -// The following table is parameterized. -// -// To exclude the first column of the table -- the method or annotation parameter list -- and -// the text that describes it, define the cors-config-table-exclude-methods attribute in the including file. -// The value does not matter. -// -// To exclude the second column -- the config keys -- and the text that describes it, define -// cors-config-table-exclude-keys in the including file. The value does not matter. -// -// To customize the text that explains the first column, set config-table-methods-column-explainer -// to the text you want inserted. -// -// To customize the column heading for the first column, set config-table-methods-column-header to -// the header you want used. -// -// tag::cors-config-table[] - -ifndef::cors-config-table-exclude-methods+cors-config-table-exclude-keys[] -[width="100%",options="header",cols="4*"] -endif::[] -ifdef::cors-config-table-exclude-methods[] -[width="100%",options="header",cols="3*"] -endif::[] -ifdef::cors-config-table-exclude-keys[] -[width="100%",options="header",cols="3*"] -endif::[] - -|==================== -ifndef::cors-config-table-exclude-methods[| {config-table-methods-column-header} ] -ifndef::cors-config-table-exclude-keys[| Configuration Key] -| Default | CORS Header Name - -ifndef::cors-config-table-exclude-methods[|`allowCredentials`] -ifndef::cors-config-table-exclude-keys[|`allow-credentials`] -|`false`|`Access-Control-Allow-Credentials` - -ifndef::cors-config-table-exclude-methods[|`allowHeaders`] -ifndef::cors-config-table-exclude-keys[|`allow-headers`] -|`["*"]`|`Access-Control-Allow-Headers` - -ifndef::cors-config-table-exclude-methods[|`allowMethods`] -ifndef::cors-config-table-exclude-keys[|`allow-methods`] -|`["*"]`|`Access-Control-Allow-Methods` - -ifndef::cors-config-table-exclude-methods[|`allowOrigins`] -ifndef::cors-config-table-exclude-keys[|`allow-origins`] -|`["*"]`|`Access-Control-Allow-Origins` - -ifndef::cors-config-table-exclude-methods[|`exposeHeaders`] -ifndef::cors-config-table-exclude-keys[|`expose-headers`] -|`none`|`Access-Control-Expose-Headers` - -ifndef::cors-config-table-exclude-methods[|`maxAgeSeconds`] -ifndef::cors-config-table-exclude-keys[|`max-age`] -|`3600`|`Access-Control-Max-Age` - -ifndef::cors-config-table-exclude-methods[|`enabled`] -ifndef::cors-config-table-exclude-keys[|`enabled`] -|`true`|n/a| -|==================== - -If the cross-origin configuration is disabled (`enabled` = false), then the Helidon CORS implementation ignores the cross-origin configuration entry. -// end::cors-config-table[] +include::{common-page-prefix-inc}[tag=cors-config-table] [[se-api-routing-example]] == Sample Routing Setup Using the `CrossOriginConfig` API diff --git a/docs/se/cors/03_using-configuration.adoc b/docs/se/cors/03_using-configuration.adoc index 07cde736c..f95f74d11 100644 --- a/docs/se/cors/03_using-configuration.adoc +++ b/docs/se/cors/03_using-configuration.adoc @@ -24,12 +24,14 @@ :description: Helidon CORS Configuration :keywords: helidon, java, cors, configuration :cors-se-intro: 01_introduction.adoc -:cors-se-api-doc: 02_using-the-api.adoc -:cors-config-table-src: {cors-se-api-doc} :mapped-config-top-key: my-cors :helidon-tag: https://github.com/oracle/helidon/tree/{helidon-version} :helidon-se-cors-example: {helidon-tag}/examples/cors :mapped-config-id-callout: Assigns a unique identifier for this mapped CORS config section. +:common-page-prefix-inc: ../../shared/cors/common_shared.adoc +:cors-config-table-exclude-methods: +:!cors-config-table-exclude-keys: +:basic-table-intro: The table below lists the configuration keys that identify the CORS characteristics. You can use configuration in combination with the Helidon CORS SE API to add CORS support to your resources. Loading the CORS information for different types of resource sharing from declarative configuration, @@ -37,73 +39,13 @@ instead of hard-coding it in your procedural Java code, not only simplifies the code but also gives your users a way to override the CORS behavior of your services without requiring the code to change. +include::{common-page-prefix-inc}[tag=cors-configuration-formats-intro] -== Understanding the CORS Configuration Formats [[cors-configuration-formats]] -Support in Helidon for CORS configuration uses two closely-related cross-origin configuration formats: basic and mapped. -Each format corresponds to a class -in the Helidon CORS library. -The basic format corresponds to the link:{javadoc-base-url-api}/CrossOriginConfig.html[`CrossOriginConfig`] -class, and the mapped format corresponds to the -link:{javadoc-base-url-api}/MappedCrossOriginConfig.html[`MappedCrossOriginConfig`] class. - -[[basic-cross-origin-config]] -=== Basic Cross-Origin Configuration -In configuration, Helidon represents basic CORS information as a section, identified by a configuration -key of your choosing, that contains -one or more key/value pairs. Each key-value pair assigns one characteristic of CORS behavior. - -The table below lists the configuration keys that identify the CORS characteristics. - -[[config-key-table]] -:cors-config-table-exclude-methods: -:!cors-config-table-exclude-keys: -include::{cors-config-table-src}[tag=cors-config-table] - -The following example of basic cross-origin configuration, when loaded and used by the application, limits cross-origin resource sharing for `PUT` and -`DELETE` operations to only `foo.com` and `there.com`: - -[source,hocon] ----- -... -restrictive-cors: - allow-origins: ["http://foo.com", "http://there.com"] - allow-methods: ["PUT", "DELETE"] -... ----- +include::{common-page-prefix-inc}[tag=basic-cross-origin-config] === Mapped Cross-Origin Configuration In some cases, you or your users might want to configure CORS behavior based on URL path matching. -// tag::mapped-config[] -// tag::mapped-config-prefix[] -Helidon represents mapped CORS information as a section, identified by a configuration -key of your choosing, that contains: - -* An optional `enabled` setting which defaults to `true` and applies to the whole mapped CORS config section, and - -* An optional `paths` subsection containing zero or more entries, each of which contains: - -** a basic CORS config section, and - -** a `path-pattern` path pattern that maps that basic CORS config section to the resource(s) it affects. - -You can use mapped configuration to your advantage if you want to allow your users to override the CORS behavior set up -in the application code. - -The following example illustrates the mapped cross-origin configuration format. - -[source,hocon,subs="attributes+"] ----- -... -{mapped-config-top-key}: <1> - paths: <2> - - path-pattern: /greeting <3> - allow-origins: ["http://foo.com", "http://there.com", "http://other.com"] <4> - allow-methods: ["PUT", "DELETE"] - - path-pattern: / <5> - allow-methods: ["GET", "HEAD", "OPTIONS", "POST"] <6> -... ----- // We want to use the following to insert the SE or MP callout 1 text; we need to use the blank, plus, // and subs because the MP attribute value contains backticks, and this is the only way we've found // to for the substitution in the callout to work the way we want. And this works when @@ -119,27 +61,9 @@ The following example illustrates the mapped cross-origin configuration format. // for the prefix and suffix and just have the including file include the mapped-config instead of // include the prefix, then provide its own callout 1, then include the suffix. // -// end::mapped-config-prefix[] -<1> {mapped-config-id-callout} -// tag::mapped-config-suffix[] -<2> Collects the sequence of entries, each of which maps a basic CORS config to a path pattern. -<3> Marks the beginning of an entry (the `-` character) and maps the associated basic CORS config -to the `/greeting` subresource (the `path-pattern` key and value). -<4> Begins the basic CORS config section for `/greeting`; it -restricts sharing via `PUT` and `DELETE` to the listed origins. -<5> Marks the beginning of the next entry (the `-` character) and maps the associated basic CORS config to -the top-level resource in the app (the `path-pattern` key and value). -<6> Begins the basic CORS config section for `/`; it permits sharing of resources at the top-level path with all origins -for the indicated HTTP methods. - -Path patterns can be any expression accepted by the link:{javadoc-base-url-webserver}/PathMatcher.html[`PathMatcher`] class. - -NOTE: Be sure to arrange the entries in the order that you want Helidon to check them. -Helidon CORS support searches the cross-origin entries in the order you define them until it finds an entry that -matches an incoming request's path pattern and HTTP method. -// end::mapped-config-suffix[] -// end::mapped-config[] - +include::{common-page-prefix-inc}[tag=mapped-config-prefix] +<1> {mapped-config-id-callout} +include::{common-page-prefix-inc}[tag=mapped-config-suffix] [[using-config-from-app]] == Using CORS Configuration in the Application diff --git a/docs/se/cors/04_support-in-builtin-services.adoc b/docs/se/cors/04_support-in-builtin-services.adoc index 4093af88f..fa89b61ba 100644 --- a/docs/se/cors/04_support-in-builtin-services.adoc +++ b/docs/se/cors/04_support-in-builtin-services.adoc @@ -31,96 +31,25 @@ :quickstart-example: {helidon-tag}/examples/quickstarts/helidon-quickstart-se :javadoc-base-url-api: {javadoc-base-url}io.helidon.webserver.cors/io/helidon/webserver/cors :javadoc-base-url-webserver: {javadoc-base-url}io.helidon.webserver/io/helidon/webserver -:cors-se-intro: 01_introduction.adoc -:cors-se-api-doc: 02_using-the-api.adoc +:cors-se-api-doc: ../../se/cors/02_using-the-api.adoc :cors-config-table-src: {cors-se-api-doc} :cors-dependency-src: {cors-se-api-doc} :actual-cors-dependency-src: {cors-se-api-doc} :health-page: se/health/01_health.adoc :metrics-page: se/metrics/01_metrics.adoc :openapi-page: se/openapi/01_openapi.adoc +:cors-config: ../../se/cors/03_using-configuration.adoc :cors-services-is-se: :helidon-variant: SE +:common-page-prefix-inc: ../../shared/cors/common_shared.adoc Several built-in Helidon services -- health, metrics, and OpenAPI -- have integrated CORS support. You can include these services in your application and control their CORS behavior. -// tag::understanding-cors-support-in-services[] -== Understanding CORS Support in Helidon Services -Helidon lets you easily include <<{health-page},health>>, <<{metrics-page},metrics>>, and -<<{openapi-page},OpenAPI>> services in your Helidon application. -These services add endpoints to your application so that clients can retrieve information about it. -As with the application endpoints you write, these endpoints represent resources that can be shared across origins. +include::{common-page-prefix-inc}[tag=understanding-cors-support-in-services] -For example, several websites related to OpenAPI run a web application in your browser. -You provide the URL for your application to the browser application. -The browser application uses the URL to retrieve the OpenAPI document -that describes the application's endpoints directly from your application. -The browser application then displays a user interface that you use to "drive" your application. That is, -you provide input, have the web application -send requests to your application endpoints, and then view the responses. -This scenario is exactly the situation CORS addresses: an application in the browser from one origin -- the user interface downloaded from the -website -- requests a resource from another origin -- the `/openapi` endpoint which Helidon's OpenAPI built-in -service automatically adds to your application. - -Integrating CORS support into these built-in services allows such third-party web sites and their browser applications -- or -more generally, apps from any other origin -- to work with your Helidon application. - -Because all three of these built-in Helidon services serve only `GET` endpoints, by default the -integrated CORS support in all three services permits -any origin to share their resources using `GET`, `HEAD`, and `OPTIONS` HTTP requests. You can customize the CORS set-up -for these built-in services independently from each other using -ifdef::cors-services-is-se[ either the Helidon API, configuration, or both.] -ifndef::cors-services-is-se[ configuration.] -You can use this override feature to control the CORS behavior of the built-in services even if you do not add CORS behavior -to your own endpoints. - - - -// end::understanding-cors-support-in-services[] - -// tag::builtin-getting-started[] -== Getting Started -To use built-in services with CORS support and customize the -CORS behavior: - -. Add the built-in service or services to your application. The health, metrics, and OpenAPI services automatically -include default CORS support. -. {blank} -+ --- -Add a dependency on the Helidon {helidon-variant} CORS artifact to your Maven `pom.xml` file. - -NOTE: If you want the built-in services to support CORS, then you need to add the CORS dependency even if your own endpoints do not use CORS. - -include::{cors-dependency-src}[tag=add-cors-dependency] -include::{actual-cors-dependency-src}[tag=actual-cors-dependency] --- -. Use -ifdef::cors-services-is-se[the Helidon API or] -configuration to customize the CORS behavior as needed. - -The documentation for the individual built-in services describes how to add each -service to your application, including -adding a Maven -ifdef::cors-services-is-se[dependency and including the service in your application's routing rules.] -ifndef::cors-services-is-se[dependency.] -In your -application's configuration file, the configuration for each service appears under its own key. -|==== -| Helidon Service Documentation | Configuration Key - -| <<{health-page}, health>> | `health` -| <<{metrics-page}, metrics>> | `metrics` -| <<{openapi-page}, OpenAPI>> | `openapi` -|==== - -The link:{quickstart-example}[Helidon {helidon-variant} QuickStart example] -uses these services, so you can use that as a template for your -own application, or use the example project itself to experiment with customizing the CORS -behavior in the built-in services. -// end::builtin-getting-started[] +include::{common-page-prefix-inc}[tag=builtin-getting-started] == Controlling CORS for Built-in Services Using the API Although services such as health, metrics, and OpenAPI are built into Helidon, to use them your application must create @@ -167,59 +96,10 @@ private static Routing createRouting(Config config) { <2> Use the `CrossOriginConfig` instance in constructing the `MetricsSupport` service. <3> Use the `MetricsSupport` object in creating the routing rules. -// tag::configuring-cors-for-builtin-services[] -== Configuring CORS for Built-in Services -You can -ifdef::cors-services-is-se[also ] -use configuration to control whether and how each of the built-in services works with CORS. +include::{common-page-prefix-inc}[tag=configuring-cors-for-builtin-services] -ifdef::cors-services-is-se[] -Your application can pass configuration to the builder for each built-in service. -endif::[] -For the health, metrics, and OpenAPI services, your configuration can include a section for CORS. +include::{common-page-prefix-inc}[tag=accessing-shared-resources-intro] -// Tag the following example so we can exclude it from MP which supplies its own complete example. -// tag::se-config-example[] -The following example restricts sharing of the -`/health` resource, provided by the health built-in service, to only the origin `\http://there.com`. -[source,hocon] ----- -... -health: - cors: - allow-origins: [http://there.com] -... ----- -// end::se-config-example[] - -// tag::se-code-changes-for-builtin-services-config[] -Modify your application to load the `health` config node and use it to construct the `HealthSupport` service. -The following code shows this change in the the QuickStart SE example. -[source,java] ----- -HealthSupport health = HealthSupport.builder() - .config(config.get("health")) // <1> - .addLiveness(HealthChecks.healthChecks()) // Adds a convenient set of checks - .build(); ----- -<1> Use the `health` config section (if present) to configure the health service. -// end::se-code-changes-for-builtin-services-config[] - -You have full control over the CORS configuration for a built-in Helidon service. Use a basic CORS config section -as described in <>. - -// end::configuring-cors-for-builtin-services[] - -// tag::accessing-shared-resources-intro[] -== Accessing the Shared Resources -If you have edited the Helidon SE QuickStart application as described in the previous topics and saved your changes, -you can build and run the application. Once you do so you can execute `curl` commands to demonstrate the behavior changes -in the metric and health services with the addition of the CORS functionality. Note the addition of the -`Origin` header value in the `curl` commands, and the `Access-Control-Allow-Origin` in the successful responses. - -=== Build and Run the Application -Build and run the QuickStart application as usual. -// end::accessing-shared-resources-intro[] [source,bash] ---- mvn package @@ -227,65 +107,5 @@ java -jar target/helidon-quickstart-se.jar ... WEB server is up! http://localhost:8080/greet ---- -// tag::accessing-shared-resources-main[] -=== Retrieve Metrics -The metrics service rejects attempts to access metrics on behalf of a disallowed origin. -[source,bash] ----- -curl -i -H "Origin: http://other.com" http://localhost:8080/metrics -HTTP/1.1 403 Forbidden -Date: Mon, 11 May 2020 11:08:09 -0500 -transfer-encoding: chunked -connection: keep-alive ----- - -But accesses from `foo.com` succeed. -[source,bash] ----- -curl -i -H "Origin: http://foo.com" http://localhost:8080/metrics - -HTTP/1.1 200 OK -Access-Control-Allow-Origin: http://foo.com -Content-Type: text/plain -Date: Mon, 11 May 2020 11:08:16 -0500 -Vary: Origin -connection: keep-alive -content-length: 6065 - -# TYPE base_classloader_loadedClasses_count gauge -# HELP base_classloader_loadedClasses_count Displays the number of classes that are currently loaded in the Java virtual machine. -base_classloader_loadedClasses_count 3568 -... ----- - -=== Retrieve Health -The health service rejects requests from origins not specifically approved. - -[source,bash] ----- -curl -i -H "Origin: http://foo.com" http://localhost:8080/health - -HTTP/1.1 403 Forbidden -Date: Mon, 11 May 2020 12:06:55 -0500 -transfer-encoding: chunked -connection: keep-alive ----- - -And responds successfully only to cross-origin requests from `\http://there.com`. - -[source,bash] ----- -curl -i -H "Origin: http://there.com" http://localhost:8080/health - -HTTP/1.1 200 OK -Access-Control-Allow-Origin: http://there.com -Content-Type: application/json -Date: Mon, 11 May 2020 12:07:32 -0500 -Vary: Origin -connection: keep-alive -content-length: 461 - -{"outcome":"UP",...} ----- -// end::accessing-shared-resources-main[] +include::{common-page-prefix-inc}[tag=accessing-shared-resources-main] diff --git a/docs/shared/cors/common_shared.adoc b/docs/shared/cors/common_shared.adoc new file mode 100644 index 000000000..1708f0ca6 --- /dev/null +++ b/docs/shared/cors/common_shared.adoc @@ -0,0 +1,430 @@ +/////////////////////////////////////////////////////////////////////////////// + Copyright (c) 2020 Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +/////////////////////////////////////////////////////////////////////////////// + + +//Contains content that is shared between multiple CORS pages. +:keywords: helidon, java, cors, se +:helidon-tag: https://github.com/oracle/helidon/tree/{helidon-version} +:quickstart-example: {helidon-tag}/examples/quickstarts/helidon-quickstart-se +:cors-spec: https://www.w3.org/TR/cors/ +:helidon-se-cors-example: {helidon-tag}/examples/cors +:helidon-tag: https://github.com/oracle/helidon/tree/{helidon-version} +:quickstart-example: {helidon-tag}/examples/quickstarts/helidon-quickstart-se +:javadoc-base-url-api: {javadoc-base-url}io.helidon.webserver.cors/io/helidon/webserver/cors +:javadoc-base-url-webserver: {javadoc-base-url}io.helidon.webserver/io/helidon/webserver +:helidon-variant: SE +:common-page-prefix-inc: +:actual-cors-dependency-src:co + += CORS Shared content + +// tag::cors-intro[] +The CORS protocol helps developers control if and how REST resources served by their applications can be shared across origins. +Helidon {helidon-variant} includes an implementation of CORS that you can use to add CORS behavior +to the services you develop. You can define your application's CORS behavior programmatically using the Helidon CORS API alone, or +together with configuration. Helidon also provides three built-in services that add their +own endpoints to your application - health, metrics, and OpenAPI - that have integrated CORS support. +By adding very little code to your application, you control how all the resources in +your application -- the ones you write and the ones provided by the Helidon built-in services -- can be shared across origins. + +== Before You Begin +Before you revise your application to add CORS support, you need to decide what type of cross-origin sharing you want +to allow for each resource your application exposes. +For example, suppose for a given resource you want to allow unrestricted sharing for GET, HEAD, and POST requests +(what CORS refers to as "simple" requests), but permit other types of requests only from the two +origins `foo.com` and `there.com`. +Your application would implement two types of CORS sharing: more relaxed for the +simple requests and stricter for others. + +Once you know the type of sharing you want to allow for each of your resources -- including any from built-in +services -- you can change your application accordingly. +// end::cors-intro[] + +// The add-cors-dependency tag's contents is reused from other SE and MP pages. +// The actual dependency is different for SE and MP, so we tag it separately from the intro text so the +// MP pages can reuse this intro text but use their own "actual" dependency. We could have parameterized +// the groupID and artifactID but this approach allows the actual dependencies themselves to be +// in the source literally rather than parameterized. +// tag::add-cors-dependency[] +The <> page describes how you +should declare dependency management for Helidon applications. +For CORS support in Helidon {helidon-variant}, you must include +the following dependency in your project: +// end::add-cors-dependency[] + +//tag::cors-configuration-formats-intro[] +== Understanding the CORS Configuration Formats [[cors-configuration-formats]] +Support in Helidon for CORS configuration uses two closely-related cross-origin configuration formats: basic and mapped. +Each format corresponds to a class +in the Helidon CORS library. +The basic format corresponds to the link:{javadoc-base-url-api}/CrossOriginConfig.html[`CrossOriginConfig`] +class, and the mapped format corresponds to the +link:{javadoc-base-url-api}/MappedCrossOriginConfig.html[`MappedCrossOriginConfig`] class. +//end::cors-configuration-formats-intro[] + +//tag::basic-cross-origin-config[] +[[basic-cross-origin-config]] +=== Basic Cross-Origin Configuration +In configuration, Helidon represents basic CORS information as a section, identified by a configuration +key of your choosing, that contains +one or more key/value pairs. Each key-value pair assigns one characteristic of CORS behavior. + +{basic-table-intro} + +[[config-key-table]] +include::{common-page-prefix-inc}[tag=cors-config-table] + +The following example of basic cross-origin configuration, when loaded and used by the application, limits cross-origin resource sharing for `PUT` and +`DELETE` operations to only `foo.com` and `there.com`: + +[source,hocon] +---- +... +restrictive-cors: + allow-origins: ["http://foo.com", "http://there.com"] + allow-methods: ["PUT", "DELETE"] +... +---- +//end::basic-cross-origin-config[] + + +// The following table is parameterized. +// +// To exclude the first column of the table -- the method or annotation parameter list -- and +// the text that describes it, define the cors-config-table-exclude-methods attribute in the including file. +// The value does not matter. +// +// To exclude the second column -- the config keys -- and the text that describes it, define +// cors-config-table-exclude-keys in the including file. The value does not matter. +// +// To customize the text that explains the first column, set config-table-methods-column-explainer +// to the text you want inserted. +// +// To customize the column heading for the first column, set config-table-methods-column-header to +// the header you want used. +// +// tag::cors-config-table[] + +ifndef::cors-config-table-exclude-methods+cors-config-table-exclude-keys[] +[width="100%",options="header",cols="4*"] +endif::[] +ifdef::cors-config-table-exclude-methods[] +[width="100%",options="header",cols="3*"] +endif::[] +ifdef::cors-config-table-exclude-keys[] +[width="100%",options="header",cols="3*"] +endif::[] + +|==================== +ifndef::cors-config-table-exclude-methods[| {config-table-methods-column-header} ] +ifndef::cors-config-table-exclude-keys[| Configuration Key] +| Default | CORS Header Name + +ifndef::cors-config-table-exclude-methods[|`allowCredentials`] +ifndef::cors-config-table-exclude-keys[|`allow-credentials`] +|`false`|`Access-Control-Allow-Credentials` + +ifndef::cors-config-table-exclude-methods[|`allowHeaders`] +ifndef::cors-config-table-exclude-keys[|`allow-headers`] +|`["*"]`|`Access-Control-Allow-Headers` + +ifndef::cors-config-table-exclude-methods[|`allowMethods`] +ifndef::cors-config-table-exclude-keys[|`allow-methods`] +|`["*"]`|`Access-Control-Allow-Methods` + +ifndef::cors-config-table-exclude-methods[|`allowOrigins`] +ifndef::cors-config-table-exclude-keys[|`allow-origins`] +|`["*"]`|`Access-Control-Allow-Origins` + +ifndef::cors-config-table-exclude-methods[|`exposeHeaders`] +ifndef::cors-config-table-exclude-keys[|`expose-headers`] +|`none`|`Access-Control-Expose-Headers` + +ifndef::cors-config-table-exclude-methods[|`maxAgeSeconds`] +ifndef::cors-config-table-exclude-keys[|`max-age`] +|`3600`|`Access-Control-Max-Age` + +ifndef::cors-config-table-exclude-methods[|`enabled`] +ifndef::cors-config-table-exclude-keys[|`enabled`] +|`true`|n/a| +|==================== + +If the cross-origin configuration is disabled (`enabled` = false), then the Helidon CORS implementation ignores the cross-origin configuration entry. +// end::cors-config-table[] + +// tag::mapped-config[] +// tag::mapped-config-prefix[] +Helidon represents mapped CORS information as a section, identified by a configuration +key of your choosing, that contains: + +* An optional `enabled` setting which defaults to `true` and applies to the whole mapped CORS config section, and + +* An optional `paths` subsection containing zero or more entries, each of which contains: + +** a basic CORS config section, and + +** a `path-pattern` path pattern that maps that basic CORS config section to the resource(s) it affects. + +You can use mapped configuration to your advantage if you want to allow your users to override the CORS behavior set up +in the application code. + +The following example illustrates the mapped cross-origin configuration format. + +[source,hocon,subs="attributes+"] +---- +... +{mapped-config-top-key}: <1> + paths: <2> + - path-pattern: /greeting <3> + allow-origins: ["http://foo.com", "http://there.com", "http://other.com"] <4> + allow-methods: ["PUT", "DELETE"] + - path-pattern: / <5> + allow-methods: ["GET", "HEAD", "OPTIONS", "POST"] <6> +... +---- +// We want to use the following to insert the SE or MP callout 1 text; we need to use the blank, plus, +// and subs because the MP attribute value contains backticks, and this is the only way we've found +// to for the substitution in the callout to work the way we want. And this works when +// rendered in our editing tools and via the asciidoctor command to HTML but not on our built site. +// +// <1> {blank} +// + +// [subs=attributes+] +// {mapped-config-id-callout} +// +// So instead we have the prefix and suffix tags and the including document provides its own callout 1. +// If at some point the rendering for our site handles this, we can just remove the tag and end +// for the prefix and suffix and just have the including file include the mapped-config instead of +// include the prefix, then provide its own callout 1, then include the suffix. +// +// end::mapped-config-prefix[] +<1> {mapped-config-id-callout} +// tag::mapped-config-suffix[] +<2> Collects the sequence of entries, each of which maps a basic CORS config to a path pattern. +<3> Marks the beginning of an entry (the `-` character) and maps the associated basic CORS config +to the `/greeting` subresource (the `path-pattern` key and value). +<4> Begins the basic CORS config section for `/greeting`; it +restricts sharing via `PUT` and `DELETE` to the listed origins. +<5> Marks the beginning of the next entry (the `-` character) and maps the associated basic CORS config to +the top-level resource in the app (the `path-pattern` key and value). +<6> Begins the basic CORS config section for `/`; it permits sharing of resources at the top-level path with all origins +for the indicated HTTP methods. + +Path patterns can be any expression accepted by the link:{javadoc-base-url-webserver}/PathMatcher.html[`PathMatcher`] class. + +NOTE: Be sure to arrange the entries in the order that you want Helidon to check them. +Helidon CORS support searches the cross-origin entries in the order you define them until it finds an entry that +matches an incoming request's path pattern and HTTP method. + +// end::mapped-config-suffix[] +// end::mapped-config[] + +// tag::understanding-cors-support-in-services[] +== Understanding CORS Support in Helidon Services +Helidon lets you easily include <<{health-page},health>>, <<{metrics-page},metrics>>, and +<<{openapi-page},OpenAPI>> services in your Helidon application. +These services add endpoints to your application so that clients can retrieve information about it. +As with the application endpoints you write, these endpoints represent resources that can be shared across origins. + +For example, several websites related to OpenAPI run a web application in your browser. +You provide the URL for your application to the browser application. +The browser application uses the URL to retrieve the OpenAPI document +that describes the application's endpoints directly from your application. +The browser application then displays a user interface that you use to "drive" your application. That is, +you provide input, have the web application +send requests to your application endpoints, and then view the responses. +This scenario is exactly the situation CORS addresses: an application in the browser from one origin -- the user interface downloaded from the +website -- requests a resource from another origin -- the `/openapi` endpoint which Helidon's OpenAPI built-in +service automatically adds to your application. + +Integrating CORS support into these built-in services allows such third-party web sites and their browser applications -- or +more generally, apps from any other origin -- to work with your Helidon application. + +Because all three of these built-in Helidon services serve only `GET` endpoints, by default the +integrated CORS support in all three services permits +any origin to share their resources using `GET`, `HEAD`, and `OPTIONS` HTTP requests. You can customize the CORS set-up +for these built-in services independently from each other using +ifdef::cors-services-is-se[ either the Helidon API, configuration, or both.] +ifndef::cors-services-is-se[ configuration.] +You can use this override feature to control the CORS behavior of the built-in services even if you do not add CORS behavior +to your own endpoints. + +// end::understanding-cors-support-in-services[] + +// tag::builtin-getting-started[] +== Getting Started +To use built-in services with CORS support and customize the +CORS behavior: + +. Add the built-in service or services to your application. The health, metrics, and OpenAPI services automatically +include default CORS support. +. {blank} ++ +-- +Add a dependency on the Helidon {helidon-variant} CORS artifact to your Maven `pom.xml` file. + +NOTE: If you want the built-in services to support CORS, then you need to add the CORS dependency even if your own endpoints do not use CORS. + +include::{common-page-prefix-inc}[tag=add-cors-dependency] +include::{actual-cors-dependency-src}[tag=actual-cors-dependency] +-- +. Use +ifdef::cors-services-is-se[the Helidon API or] +configuration to customize the CORS behavior as needed. + +The documentation for the individual built-in services describes how to add each +service to your application, including +adding a Maven +ifdef::cors-services-is-se[dependency and including the service in your application's routing rules.] +ifndef::cors-services-is-se[dependency.] +In your +application's configuration file, the configuration for each service appears under its own key. +|==== +| Helidon Service Documentation | Configuration Key + +| <<{health-page}, health>> | `health` +| <<{metrics-page}, metrics>> | `metrics` +| <<{openapi-page}, OpenAPI>> | `openapi` +|==== + +The link:{quickstart-example}[Helidon {helidon-variant} QuickStart example] +uses these services, so you can use that as a template for your +own application, or use the example project itself to experiment with customizing the CORS +behavior in the built-in services. +// end::builtin-getting-started[] + +// tag::configuring-cors-for-builtin-services[] +== Configuring CORS for Built-in Services +You can +ifdef::cors-services-is-se[also ] +use configuration to control whether and how each of the built-in services works with CORS. + +ifdef::cors-services-is-se[] +Your application can pass configuration to the builder for each built-in service. +endif::[] +For the health, metrics, and OpenAPI services, your configuration can include a section for CORS. + +// Tag the following example so we can exclude it from MP which supplies its own complete example. +// tag::se-config-example[] +The following example restricts sharing of the +`/health` resource, provided by the health built-in service, to only the origin `\http://there.com`. +[source,hocon] +---- +... +health: + cors: + allow-origins: [http://there.com] +... +---- + +// end::se-config-example[] + +// tag::se-code-changes-for-builtin-services-config[] +Modify your application to load the `health` config node and use it to construct the `HealthSupport` service. +The following code shows this change in the the QuickStart SE example. +[source,java] +---- +HealthSupport health = HealthSupport.builder() + .config(config.get("health")) // <1> + .addLiveness(HealthChecks.healthChecks()) // Adds a convenient set of checks + .build(); +---- +<1> Use the `health` config section (if present) to configure the health service. + +// end::se-code-changes-for-builtin-services-config[] + +You have full control over the CORS configuration for a built-in Helidon service. Use a basic CORS config section +as described in +ifdef::cors-services-is-se[<>.] +ifndef::cors-services-is-se[<<{mp-cors-config-ref},Using Configuration with CORS in Helidon MP>>.] + + +// end::configuring-cors-for-builtin-services[] + +// tag::accessing-shared-resources-intro[] +== Accessing the Shared Resources +If you have edited the Helidon {helidon-variant} QuickStart application as described in the previous topics and saved your changes, +you can build and run the application. Once you do so you can execute `curl` commands to demonstrate the behavior changes +in the metric and health services with the addition of the CORS functionality. Note the addition of the +`Origin` header value in the `curl` commands, and the `Access-Control-Allow-Origin` in the successful responses. + +=== Build and Run the Application +Build and run the QuickStart application as usual. +// end::accessing-shared-resources-intro[] + +// tag::accessing-shared-resources-main[] +=== Retrieve Metrics +The metrics service rejects attempts to access metrics on behalf of a disallowed origin. +[source,bash] +---- +curl -i -H "Origin: http://other.com" http://localhost:8080/metrics + +HTTP/1.1 403 Forbidden +Date: Mon, 11 May 2020 11:08:09 -0500 +transfer-encoding: chunked +connection: keep-alive +---- + +But accesses from `foo.com` succeed. +[source,bash] +---- +curl -i -H "Origin: http://foo.com" http://localhost:8080/metrics + +HTTP/1.1 200 OK +Access-Control-Allow-Origin: http://foo.com +Content-Type: text/plain +Date: Mon, 11 May 2020 11:08:16 -0500 +Vary: Origin +connection: keep-alive +content-length: 6065 + +# TYPE base_classloader_loadedClasses_count gauge +# HELP base_classloader_loadedClasses_count Displays the number of classes that are currently loaded in the Java virtual machine. +base_classloader_loadedClasses_count 3568 +... +---- + +=== Retrieve Health +The health service rejects requests from origins not specifically approved. + +[source,bash] +---- +curl -i -H "Origin: http://foo.com" http://localhost:8080/health + +HTTP/1.1 403 Forbidden +Date: Mon, 11 May 2020 12:06:55 -0500 +transfer-encoding: chunked +connection: keep-alive +---- + +And responds successfully only to cross-origin requests from `\http://there.com`. + +[source,bash] +---- +curl -i -H "Origin: http://there.com" http://localhost:8080/health + +HTTP/1.1 200 OK +Access-Control-Allow-Origin: http://there.com +Content-Type: application/json +Date: Mon, 11 May 2020 12:07:32 -0500 +Vary: Origin +connection: keep-alive +content-length: 461 + +{"outcome":"UP",...} +---- +// end::accessing-shared-resources-main[]