== Securing CubeWerx services for Testbed 12 === Overview The purpose of this clause is to describe how CubeWerx plans to provide authentication and access control on top of its servers deployed for Testbed 12. The component of the CubeWerx suite of products that provides authentication and access control is named the CubeWerx Identity Management System (CubeIMS). CubeIMS includes an authentication service and a mechanism to define and control access to content served by CubeWerx OGC web services. This clause describes how CubeWerx services will be configured to allow fine-grained access to operations and/or content to users based on the following types of credentials: . IP address . HTTP Basic Authentication . Distributed Access Control System (DACS) . CwAuth credentials For Testbed 12 CubeWerx plans to allow the use of both HTTP Basic Authentication and CwAuth credentials to provide authentication and access control. NOTE: For a description of DACS see https://portal.opengeospatial.org/files/?artifact_id=2253&version=1 === Use cases The CwAuth credentials mechanism has the following properties: . It is secure. CwAuth credentials cannot be spoofed or manipulated. To accomplish this, a PGP public-key encryption mechanism is employed. Credentials can only be authenticated and served by a CubeWerx Authentication Server, and all servers involved can verify the authenticity of a set of credentials by successfully decrypting them with the public key of a known (and trusted) CubeWerx Authentication Server. Furthermore, as long as all connection endpoints are HTTPS, malicious third parties cannot gain access to credentials in transit. . It is flexible. It is compatible with the various standard distributed-application architectures, including "thin" browser-based clients, hybrid server-side web applications, and "thick" desktop application. It does this by communicating the credentials via an HTTP cookie and documenting how servers and desktop applications should intercept, interpret and propagate this cookie. It is also adaptable to various backend authentication mechanisms. . It is role-based. In addition to indicating a username (i.e., a specific individual), a set of credentials can also indicate one or more project-defined roles. The access-control rules for a set of services can then be formulated based on these roles, providing a much more natural and flexible mechanism for access control. . It is cascadable. In a service-chaining scenario (where, for example, a Web Map Server gets its data from a Web Feature Server), CwAuth credentials can be passed down from service to service (as long as they all have the same second-level domain name) so that all entities along the chain are aware of the user's credentials and can control access at each level accordingly. . It is single sign-on. A user that has logged on to (i.e., received authenticated credentials from) a CubeWerx Authentication Server within a particular domain can then access any server within that domain without having to log in again. . It is simple. By employing the standard HTTP cookie mechanism and standard public-key encryption technology, the CwAuth credentials mechanism is easy to understand, implement and configure. Unlike DACS, an Apache plugin is not necessary. . It is efficient. In most situations, once the user has logged in, the various entities never need to contact the authentication server. Validation of credentials is achieved solely by successfully decrypting the credentials cookie with the authentication server's public key (which has been configured beforehand). One thing that it cannot do is protect files in an online filesystem (which would require an Apache plugin). It is a service-oriented mechanism only. === Mechanism ==== CW_CREDENTIALS cookie CwAuth credentials are communicated via an HTTP cookie called CW_CREDENTIALS. Encoded within the value of this cookie is a small XML document containing an authenticated set of credentials. These credentials are signed with the PGP private key of a CubeWerx Authentication Server that is set up to provide authentication for that domain. All other servers within the domain are equipped with the public key(s) of the trusted authentication server(s) of that domain. This allows the servers to verify the authenticity of the credentials, since a successful decryption with the public key of one of the trusted authentication servers implies that the credentials must have originated from that authentication server. Note, therefore, that it is not necessary for a server to contact a CubeWerx Authentication Server in order to decrypt or verify the authenticity of a set of credentials. A user is authenticated by making a login request to a CubeWerx Authentication Server. This can be done directly by having the user visit the CubeWerx Authentication Server with his/her browser, at which point the user will be prompted for a username and password. The authentication server will then pass a CW_CREDENTIALS cookie containing the appropriate credentials back to the browser. Alternatively, a browser-based application can collect the user's username and password, and contact the authentication server on behalf of the user (via either an Ajax request or a redirection to the authentication server with a callback back to the application). Either way, the web browser receives a CW_CREDENTIALS cookie containing the user's authenticated credentials for that domain. Since the CwAuth credentials for a particular domain are communicated via an HTTP cookie, web browsers (and thus browser-based applications) will automatically pass along such credentials when communicating with servers within that domain. Furthermore, properly-equipped servers can cascade such credentials to other servers further down the service chain by recognizing the CW_CREDENTIALS cookie in the request and passing it along in the HTTP headers of any requests that it makes. A web service can determine the credentials of the current user in one of two ways: . by making a getCredentials request to a CubeWerx Authentication Server and parsing the unencrypted credentials that are returned the response. . by decrypting the CW_CREDENTIALS cookie itself with the public key of a known (and trusted) CubeWerx Authentication Server. A decrypted CW_CREDENTIALS cookie contains an XML representation of the user's credentials, and has the following form: xyzcorp.com John Doe jdoe@xyzcorp.com jdoe User Administrator 2012-08-21T05:06:52.158Z https://xyzcorp.com/cubewerx/cwauth.cgi All child elements of other than and are optional. There can be several elements. The element indicates the URL of the CubeWerx Authentication Server that authenticated the credentials. === Scenarios ==== Web-based application authenticating via Ajax request . The user clicks a "log in" button on the web-based application. . The web-based application prompts the user for a username and password. . The web-based application makes an Ajax "login" request to the configured CubeWerx Authentication Server, passing along the user's username and password. . The CubeWerx Authentication Server authenticates the user for the current domain and prepares a set of credentials which it encrypts using a private key and packages into a CW_CREDENTIALS cookie. This CW_CREDENTIALS cookie is then returned to the web-based application via the HTTP response headers. The HTTP response body contains an unencrypted version of the credentials in JSON format. . The web browser automatically absorbs the CW_CREDENTIALS cookie and will include it in all future HTTP requests to all servers in the domain that the credentials were authenticated for. The web-based application optionally parses the JSON response and greets the user. ==== Web-based application redirecting to authentication server . The user clicks a "log in" button on the web-based application. . The web-based application directs the web browser to the login page of the configured CubeWerx Authentication Server, including a callback URL (back to the appropriate state of the web-based application) as a parameter of the login page's URL. . The CubeWerx Authentication Server prompts the user for a username and password. . The CubeWerx Authentication Server authenticates the user for the current domain and prepares a set of credentials which it encrypts using a private key and packages into a CW_CREDENTIALS cookie. . The CubeWerx Authentication Server sends a web page back to the user displaying the authenticated credentials. The HTTP response headers of this web page include the CW_CREDENTIALS cookie, which the web browser automatically absorbs. . The CubeWerx Authentication Server directs the web browser back to the web-based application, which then greets the user. ==== Server cascading credentials to another server . A server receives a request that includes a CW_CREDENTIALS cookie in its HTTP headers. . The server optionally decrypts the CW_CREDENTIALS cookie with the public key of an authentication server. This step is only necessary if this server itself needs to control access based on user identity or if the authenticated user needs to be identified for some other reason (such as for logging or for a personalized greeting message). . When making a cascaded request to another server, the server checks the domain of the other server. If it's in the same second-level domain, it prepares a "Cookie" HTTP header containing a copy of the CW_CREDENTIALS cookie, and passes that along with the cascaded request. ==== Desktop application authenticating via the HTTP Basic Authentication mechanism NOTE: This requires an HttpBasic-to-CwAuth bridge to be set up. . The desktop application attempts to connect to CubeSERV via an "htcubeserv" URL, and is challenged for an HTTP-Basic identity. . The user supplies a username and password, and the desktop application attempts the connection to CubeSERV again. . The Http Basic Authentication module protecting the CubeSERV is configured to authenticate using the same underlying MySQL/MariaDB database that the local CwAuth server uses, and thus the username and password is recognized. . The CubeSERV recognizes that it is being called through an HttpBasic-to-CwAuth bridge, and sends a passwordless login request to the companion CwAuth server to obtain the CwAuth identity for that user. A configured shared secret is supplied along with the request so that the CwAuth server knows to allow this passwordless login request. . For every future connection that the desktop application makes to the CubeSERV, a cached HTTP-Basic identity will be sent, and steps 3 and 4 will be repeated. === CubeWerx Authentication Server ==== Deployment and configuration ===== Deployment The CubeWerx Authentication Server is typically deployed under the URL "cubeWerxSuiteDeploymentUrl/cwauth.cgi" on one or more of the web servers in a particular domain. More than one authentication server can exist in a single domain, and they can be identical (i.e., use the same backend authentication database) or specialized. It is strongly recommended that all authentication servers be accessible only through HTTPS in order to avoid the possibility of authenticated credentials being sniffed in transit by a malicious third party. ===== Preparation of keyrings Since the CubeWerx Authentication Server relies on PGP public-key encryption, a public/private key pair must be prepared for each deployed authentication server, and the public keys of each of the deployed authentication server must be distributed to the other CubeWerx Suite deployments in the domain. To do this, for each CubeWerxSuite installation that provides an authentication server, do the following: ---- cwauthadmin genKeyPair ---- This will generate a public/private key pair for the authenticatioh server. It will take a few seconds, and you may be prompted to perform some additional activity on the machine to help it generate good random numbers. Then, on each of the other CubeWerx Suite installations in the domain, do the following: ---- cwauthadmin installKey ---- Example: ---- cwauthadmin installKey https://auth.xyzcorp.com/cubewerx/cwauth.cgi ---- This will fetch the public key from the authentication server and install it so that this machine will be able to recognize credentials that are generated by the authentication server. At any time, you can get a list of which keys are installed (and for the authentication servers themselves, which public/private keypairs are generated) by typing: ---- cwauthadmin listKeys ---- The only public keys in a CubeWerx Suite keyring should be those of the authentication servers for that domain. The existence of a public key in a CubeWerx Suite keyring indicates that the server that that key represents is a trusted source of authenticated credentials. ===== Configuration parameters Several configuration parameters (with names of the form cwauth.*) exist for configuring a CubeWerx Authentication Server. See their descriptions in the Configuration Editor for more information. ===== Management of users and roles To list the users and/or roles of a CubeWerx Authentication Server, execute one of the following commands (in a command line shell on the authentication server itself): ---- cwauthadmin listUsers cwauthadmin listUserDetails [] cwauthadmin listRoles cwauthadmin listRoleDetails [] ---- To add, edit or remove the users and/or roles, execute one of the following commands (new in CubeWerx Suite 7.2): ---- cwauthadmin addUser username= email= password= [realName= []] [firstName=] [lastName=] [roles=,,...] ---- ---- cwauthadmin editUser username= [email=] [password=] [realName= []] [firstName=] [lastName=] [roles=,,...] [addRoles=,,...] [removeRoles=,,...] ---- ---- cwauthadmin removeUser ---- ---- cwauthadmin addRole role= [description=] [contact=] [users=,,...] ---- ---- cwauthadmin editRole role= [newRolename=] [description=] [contact=] [users=,,...] [addUsers=,,...] [removeUsers=,,...] ---- ---- cwauthadmin removeRole ---- Alternatively, management of users and roles can be performed through OpenImageMap. === Operations CubeWerx Authentication Server operations can be invoked either via HTTP GET (supplying the parameters as part of the URL) or via HTTP POST (supplying the parameters in the body of the request with an application/x-www-form-urlencoded MIME type). The usage of HTTP POST is recommended for the "login" operation in order to prevent plaintext usernames and passwords from being visible in the URL. Responses that indicate a status in the response body will also report this status via an X-CwAuth-Status MIME header (new in CubeWerx Suite 7.4). If a request to a CubeWerx Authentication Server contains cross-origin resource sharing (CORS) headers, then the origin of the request must be in the same CwAuth domain as the CubeWerx Authentication Server. ==== *_Auto_* ===== parameters (none) ===== usage If the user has valid credentials, displays these credentials in an HTML page and provides the user with the ability to log out. Otherwise, presents a simple HTML login page. ==== Login (HTML page) ===== parameters ---- operation=login [&username=] [&callback=] ---- ===== usage Presents a simple HTML login page. When the user submits this page, the "login" operation (described next) is triggered. If a username is provided, the username field is pre-filled-in with this value. If a callbackUrl is provided, then once the user has logged in successfully, a button is presented which allows the user to go to that URL, returning the user to the web application that invoked the login page. ==== *_Login_* ===== parameters ---- operation=login &username= &password= &format=XML|JSON|HTML [&callback=] ---- ==== usage Authenticates the user for the current domain (i.e., the second-level domain name of the URL that the CubeWerx Authentication Server was invoked with) and logs him/her in by supplying the user agent (e.g., web browser) with a CW_CREDENTIALS cookie for that domain. ==== returns If authentication is successful, a CW_CREDENTIALS cookie is returned in the response HTTP headers, and a "loginSuccessful" response in the requested format is returned in the response body. The response body includes unencrypted credentials. XML: ---- loginSuccessful xyzcorp.com John Doe jdoe@xyzcorp.com jdoe User Administrator 2012-08-21T05:06:52.158Z https://xyzcorp.com/cubewerx/cwauth.cgi ---- JSON: ---- { "status": "loginSuccessful", "credentials": { "domain": "xyzcorp.com", "firstName": "John", "lastName": "Doe", "eMailAddress": "jdoe@xyzcorp.com", "username": "jdoe", "roles": [ "User", "Administrator" ], "expires": "2012-08-21T05:06:52.158Z", "originServer": "https://xyzcorp.com/cubewerx/cwauth.cgi" } } ---- HTML (if a callbackUrl is provided): ---- An HTTP redirection to the specified callbackUrl. ---- HTML (if a callbackUrl is not provided): ---- An HTML document showing the user's new credentials. ---- If authentication is unsuccessful, a response that indicates a status of "loginFailed" is returned. If unsuccessful login requests are occurring too frequently for this user, the login request is rejected and a response that indicates a status of "loginAttemptsTooFrequent" is returned. This protects the CubeWerx Authentication Server against brute-force password-cracking attempts. ==== *_Logout_* ===== parameters ---- operation=logout &format=XML|JSON|HTML [&callback=] ---- ===== usage Logs the user out of the current domain (i.e., the second-level domain name of the URL that the CubeWerx Authentication Server was invoked with) by requesting that the user agent (e.g., web browser) expire the CW_CREDENTIALS cookie for that domain. ===== returns An empty CW_CREDENTIALS cookie with a cookie expiry date in the past is returned in the response HTTP headers, and a "logoutSuccessful" response in the requested format is returned in the response body. XML: ---- logoutSuccessful ---- JSON: ---- { "status": "logoutSuccessful" } ---- HTML (if a callbackUrl is provided): ---- An HTTP redirection to the specified callbackUrl. ---- HTML (if a callbackUrl is not provided): ---- An HTML document indicating that the user has been logged out. ---- ==== *_GetCredentials_* ===== parameters ---- operation=getCredentials &format=XML|JSON|HTML [&callback=] ---- ===== usage Returns the user's current credentials (i.e., the unencrypted contents of the user's CW_CREDENTIALS cookie) for the current domain. ===== returns Either a "credentialsOkay", a "noCredentials", a "credentialsExpired" or a "credentialsInvalid" response in the requested format is returned in the response body. If "credentialsOkay" or "credentialsExpired", the response body includes unencrypted credentials. Note that a CW_CREDENTIALS cookie is not returned in the response HTTP headers, as this would be redundant. The "credentialsInvalid" response response is returned in situations where the user has credentials but they are not recognized by this authentication server. XML: ---- credentialsOkay xyzcorp.com John Doe jdoe@xyzcorp.com jdoe User Administrator 2012-08-21T05:06:52.158Z https://xyzcorp.com/cubewerx/cwauth.cgi ---- or ---- noCredentials ---- or ---- credentialsExpired ... ---- or ---- credentialsInvalid ---- JSON: ---- { "status": "credentialsOkay", "credentials": { "domain": "xyzcorp.com", "firstName": "John", "lastName": "Doe", "eMailAddress": "jdoe@xyzcorp.com", "username": "jdoe", "roles": [ "User", "Administrator" ], "expires": "2012-08-21T05:06:52.158Z", "originServer": "https://xyzcorp.com/cubewerx/cwauth.cgi" } } ---- or ---- { "status": "noCredentials" } ---- or ---- { "status": "credentialsExpired", "credentials": { ... } } ---- or ---- { "status": "credentialsInvalid" } ---- HTML: ---- An HTML document showing the user's current credentials. ---- If a callbackUrl is provided, then the HTML response document will include a button which allows the user to go to that URL, returning the user to the web application that invoked the getCredentials operation. ==== *_RefreshCredentials_* ===== parameters ---- operation=refreshCredentials &format=XML|JSON|HTML [&callback=] ---- ===== usage Refreshes the user's current credentials by updating the expiry date and supplying the user agent (e.g., web browser) with an updated CW_CREDENTIALS cookie for that domain. ===== returns Either a "credentialsRefreshed", a "noCredentials" a "credentialsExpired" or a "credentialsInvalid" response in the requested format is returned in the response body. If "credentialsRefreshed", a new CW_CREDENTIALS cookie is returned in the response HTTP headers and the response body includes the refreshed unencrypted credentials. If "credentialsExpired", the response body includes the original (expired) unencrypted credentials. XML: ---- credentialsRefreshed xyzcorp.com John Doe jdoe@xyzcorp.com jdoe User Administrator 2012-08-21T05:06:52.158Z https://xyzcorp.com/cubewerx/cwauth.cgi ---- or ---- noCredentials ---- or ---- credentialsExpired ... ---- JSON: ---- { "status": "credentialsRefreshed", "credentials": { "domain": "xyzcorp.com", "firstName": "John", "lastName": "Doe", "eMailAddress": "jdoe@xyzcorp.com", "username": "jdoe", "roles": [ "User", "Administrator" ], "expires": "2012-08-21T05:06:52.158Z", "originServer": "https://xyzcorp.com/cubewerx/cwauth.cgi" } } ---- or ---- { "status": "noCredentials" } ---- or ---- { "status": "credentialsExpired", "credentials": { ... } } ---- HTML: ---- An HTML document showing the user's refreshed credentials. ---- If a callbackUrl is provided, then the HTML response document will include a button which allows the user to go to that URL, returning the user to the web application that invoked the refreshCredentials operation. ==== *_EditUserInfo (HTML page)_* ===== parameters ---- operation=editUserInfo [&callback=] ---- ===== usage Presents a simple HTML page allowing the user to change his/her e-mail address, name and/or password. When the user submits this page, the "editUserInfo" operation (described next) is triggered. If a callbackUrl is provided, then once the user has successfully changed his/her information, a button is presented which allows the user to go to that URL, returning the user to the web application that invoked the editUserInfo page. ==== *_EditUserInfo_* ===== parameters ---- operation=editUserInfo [&email=] [&firstName=] [&lastName=] [&oldPassword=&newPassword=] [&format=] [&callback=] ---- ===== usage Changes the e-mail address, name and/or password in the CubeWerx Authentication Server database record of the current user. If an attempt is made to change the password, the user's current password must also be supplied. ===== returns If the operation is successful, an updated CW_CREDENTIALS cookie is returned in the response HTTP headers, and an "editUserInfoSuccessful" response in the requested format is returned in the response body. XML: ---- editUserInfoSuccessful xyzcorp.com John Doe jdoe@xyzcorp.com jdoe User Administrator 2012-08-21T05:06:52.158Z https://xyzcorp.com/cubewerx/cwauth.cgi ---- JSON: ---- { "status": "editUserInfoSuccessful", "credentials": { "domain": "xyzcorp.com", "firstName": "John", "lastName": "Doe", "eMailAddress": "jdoe@xyzcorp.com", "username": "jdoe", "roles": [ "User", "Administrator" ], "expires": "2012-08-21T05:06:52.158Z", "originServer": "https://xyzcorp.com/cubewerx/cwauth.cgi" } } ---- HTML: ---- An HTML document indicating that the information has been successfully updated. ---- If a callbackUrl is provided, then the HTML response document will include a button which allows the user to go to that URL, returning the user to the web application that invoked the editUserInfo operation. ==== *_ForgotPassword_* ===== parameters ---- operation=forgotPassword &username= &format=XML|JSON|HTML [&callback=] ---- ===== usage Indicates to the CubeWerx Authentication Server that the specified user has forgotten his/her password. An e-mail message is sent to the e-mail address registered for the user providing a URL that the user can click on to initiate a password reset. This URL is a call to the "resetPassword" operation (with a one-time authentication key), and is only valid for a limited time. ===== returns A "passwordResetInitiated" response in the requested format is returned. ==== *_ResetPassword_* ===== parameters ---- operation=resetPassword &username= &authKey= &format=XML|JSON|HTML [&callback=] ---- ===== usage This is phase two of the "forgotPassword" operation, and is triggered via the URL that is sent out as a result of that operation. If the specified authKey matches the one that was set up by the "forgotPassword" operation, and it is within the allowed time frame, the password of the specified user is reset to a random value, and an e-mail message is sent to the user indicating what the new password is. ===== returns A "passwordResetComplete" response in the requested format is returned. ==== *_GetPublicKey_* ===== parameters ---- operation=getPublicKey &format=XML|JSON|HTML ---- ===== usage Returns a PGP PUBLIC KEY BLOCK containing the public key of this CubeWerx Authentication Server. It is this key that must be propagated to the other servers in the domain so that they can verify the authenticity of credentials that originate from this server. ===== returns A PGP PUBLIC KEY BLOCK containing the public key of this CubeWerx Authentication Server is returned (with a MIME type of application/pgp-keys). The format parameter controls only the format of the returned exception report if an internal error occurs. ==== *_Internal errors_* For any operation, if an internal error occurs, an exception report in the requested format is returned in the response body. XML: an OGC OWS 1.1 exception report with an HTTP response code of 500 ("Internal Server Error") JSON: ---- { "status": "exception", "exception": [ { "module": "CWAUTH", "message": "", "messageLang":"en", "locationInfo": "" }, { "module": "CWAUTH", "message": "", "messageLang":"en", "locationInfo": "" } ... ] } ---- HTML: ---- An HTML document providing details about the error. ---- === HttpBasic-to-CwAuth bridge Since the CwAuth login mechanism is implemented as a web service that utilizes HTTP cookies to store the user's authenticated identity on the client side, non-web-based (i.e., desktop) applications are not directly compatible with it (unless they're augmented with specific support for it). However, many desktop applications have built-in support for the HTTP Basic Authentication mechanism when connecting to web services. To allow such desktop applications to connect to CubeSERV services with a CwAuth identity, it is necessary to provide an HttpBasic-to-CwAuth bridge. The steps to providing such a bridge are as follows: Set the value of the cubeserv.httpBasicToCwAuthBridge.enable configuration parameter to TRUE. Configure the HTTP-Basic authentication mechanism to protect access to "htcubeserv" and to use the same underlying MySQL/MariaDB database that the local CwAuth server uses. This may be accomplished by ensuring that the apr-util-mysql package and the mod_authn_dbd and mod_dbd Apache modules are installed, uncommenting out the indicated lines in the /etc/httpd/conf.d/zz_cubewerx.conf (or equivalent) file, filling in the appropriate values in the DBDParams line, and then restarting apache. Once this bridge is set up, a client application can connect to CubeSERV via "htcubeserv" with an HTTP Basic identity, and it will be internally converted to the corresponding full-fledged CwAuth identity. === CubeWerx's access-control mechanism The access-control mechanism employed by the CubeSERV OGC Web Services is an internal software module that controls access to data and map layers in a number of ways, based on the identity of the user. It's configured by the CubeSERV administrator as a set of rules (expressed as XML), where each rule has an appliesTo attribute indicating which user(s) the rule applies to. An individual user may trigger more than one applicable rule, or none at all. If more than one rule is applicable, then the user is granted access to the union of what the applicable rules grant. Rules can be configured to expire after a certain time, allowing subscription times to be configured. The following identity types can be specified by the appliesTo attribute: . IP address (or address range) . HTTP Basic identity . DACS identity (user, group or any) . CwAuth identity (user, role or any) . everybody A rule cannot revoke access; it can only grant it. Therefore, if no rules are applicable for a particular user, that user is not granted any access. It's typical for service deployments to include an "everybody" rule granting public read-only access to a subset of the data, but this isn't necessary. Each rule consists of one or more Allow clauses and zero or more Except clauses, and grants access to the union of the things specified by the Allow clauses minus the union of the things specified by the Except clauses. Note that an Except clause of one rule cannot revoke any access granted by another rule. Each Allow (and Except) clause specifies a set of OWS operations (e.g., "WMS GetMap", "WFS GetFeature", etc.,) and the content that's being granted (or excepted) through these operations. Specific content to be granted or excepted is referenced by indicating the data store, name and type (i.e., layer, feature set or coverage) of the content. Specific spatial areas can be specified by using inline GML geometries or by referencing local or online vector data. A minimum scale (also expressible as a finest resolution) can be specified. Filter expressions can be specified, limiting feature-set content to only those features that pass through the filter. Individual properties (columns) can be specified, limiting the accessible feature-set schema to a controlled subset. A watermark (with specified opacity and placement) can be specified for layers. Wildcards can be specified in most places, making it easy, for example, to grant all content through a specific operation, or to grant specific content through all operations. Typically a set of access-control rules specifies the same content for a service's GetCapabilities operation as it does for its other primary operations. That way, if a user doesn't have access to a particular set of layers, coverages or feature sets, that user won't even be aware of its existence (because the capabilities document won't report it). However, if desired, different rules can be specified for each operation, allowing, for example, the capabilities document to list all content even though only a subset of it is available to the user. When a capabilities document does list content, the reported bounding box for that content is clipped to the granted area of accessibility for the content. === Cross-Origin Resource Sharing (CORS) considerations To be completed. === References To be completed.