)
};
diff --git a/demos/jans-tarp/src/static/chrome/manifest.json b/demos/jans-tarp/src/static/chrome/manifest.json
index 2cb920ca3c3..99ebfbff5be 100644
--- a/demos/jans-tarp/src/static/chrome/manifest.json
+++ b/demos/jans-tarp/src/static/chrome/manifest.json
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "jans-tarp",
- "version": "0.0.0-nightly",
+ "version": "1.3.0",
"version_name": "nightly",
"description": "Relying Party tool in form of a Chrome Extension. Please note that the manifest version field should be one to four dot-separated integers identifying the version of this extension. The descriptive version string can be stated in the `version_name` field. For more details see https://developer.chrome.com/docs/extensions/reference/manifest/version.",
"icons": {
@@ -24,5 +24,8 @@
"*://*/*"
],
"options_page": "options.html",
- "incognito": "split"
+ "incognito": "split",
+ "content_security_policy": {
+ "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"
+ }
}
\ No newline at end of file
diff --git a/demos/jans-tarp/src/static/firefox/manifest.json b/demos/jans-tarp/src/static/firefox/manifest.json
index e30988c67a1..5361a2aa467 100644
--- a/demos/jans-tarp/src/static/firefox/manifest.json
+++ b/demos/jans-tarp/src/static/firefox/manifest.json
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "jans-tarp",
- "version": "0.0.0-nightly",
+ "version": "1.3.0",
"description": "Relying Party tool in form of a Firefox Extension.",
"icons": {
"16": "icon.png",
@@ -27,5 +27,8 @@
"gecko": {
"id": "jans-tarp@gluu.org"
}
+ },
+ "content_security_policy": {
+ "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"
}
}
\ No newline at end of file
diff --git a/demos/jans-tent/.flaskenv b/demos/jans-tent/.flaskenv
deleted file mode 100644
index bc1b2cf6e71..00000000000
--- a/demos/jans-tent/.flaskenv
+++ /dev/null
@@ -1,2 +0,0 @@
-#.flaskenv
-FLASK_APP=clientapp
diff --git a/demos/jans-tent/.gitignore b/demos/jans-tent/.gitignore
deleted file mode 100644
index 6b3dc1fcd19..00000000000
--- a/demos/jans-tent/.gitignore
+++ /dev/null
@@ -1,146 +0,0 @@
-#jans-tent-specific
-client_info.json
-*.log.*
-
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-wheels/
-share/python-wheels/
-*.egg-info/
-.installed.cfg
-*.egg
-MANIFEST
-
-# PyInstaller
-# Usually these files are written by a python script from a template
-# before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.nox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*.cover
-*.py,cover
-.hypothesis/
-.pytest_cache/
-cover/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-local_settings.py
-db.sqlite3
-db.sqlite3-journal
-
-# Flask stuff:
-instance/
-.webassets-cache
-
-# Scrapy stuff:
-.scrapy
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-.pybuilder/
-target/
-
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# IPython
-profile_default/
-ipython_config.py
-
-# pyenv
-# For a library or package, you might want to ignore these files since the code is
-# intended to run in multiple environments; otherwise, check them in:
-# .python-version
-
-# pipenv
-# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
-# However, in case of collaboration, if having platform-specific dependencies or dependencies
-# having no cross-platform support, pipenv may install dependencies that don't work, or not
-# install all needed dependencies.
-#Pipfile.lock
-
-# PEP 582; used by e.g. github.com/David-OConnor/pyflow
-__pypackages__/
-
-# Celery stuff
-celerybeat-schedule
-celerybeat.pid
-
-# SageMath parsed files
-*.sage.py
-
-# Environments
-.env
-.venv
-env/
-venv/
-ENV/
-env.bak/
-venv.bak/
-
-# Spyder project settings
-.spyderproject
-.spyproject
-
-# Rope project settings
-.ropeproject
-
-# mkdocs documentation
-/site
-
-# mypy
-.mypy_cache/
-.dmypy.json
-dmypy.json
-
-# Pyre type checker
-.pyre/
-
-# pytype static type analyzer
-.pytype/
-
-# Cython debug symbols
-cython_debug/
-
-.vscode/
-.scannerwork
-
diff --git a/demos/jans-tent/LICENSE b/demos/jans-tent/LICENSE
deleted file mode 100644
index 6912a5f93c9..00000000000
--- a/demos/jans-tent/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright 2023 Christian Eland
-
- 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.
\ No newline at end of file
diff --git a/demos/jans-tent/README.md b/demos/jans-tent/README.md
deleted file mode 100644
index 3f6c6c1ff87..00000000000
--- a/demos/jans-tent/README.md
+++ /dev/null
@@ -1,144 +0,0 @@
-# Jans Tent
-
-To test an OpenID Provider ("OP"), you need a test Relying Party ("RP"). Jans
-Tent is easy to configure RP which enables you to send different requests by
-quickly modifying one file (`config.py`). It's a Python Flask application,
-so it's easy to hack for other testing requirements.
-
-By default, it uses `localhost` as the `redirect_uri`, so if you run it on your
-laptop, all you need to do is specify the OP hostname to run it. Tent uses
-dynamic client registration to obtain client credentials. But you can also use
-an existing client_id if you like.
-
-## Installation
-
-**Important**: Ensure you have `Python >= 3.11`
-
-**Mac Users**: We recommend using [pyenv - simple python version management](https://github.com/pyenv/pyenv) instead of Os x native python.
-
-1. Navigate to the project root folder `jans/demos/jans-tent`
-2. Create virtual environment
-```bash
-python3 -m venv venv
-````
-3. Activate the virtual virtual environment
-```bash
-source venv/bin/activate
-```
-4. Install dependencies
-```bash
-pip install -r requirements.txt
-```
-
-## Setup
-
-### 1. Edit configuration file `clientapp/config.py` according to your needs:
- * Set `ISSUER`, replace `op_hostname` (required)
- * Set any other desired configuration
-
-### 2. Generate test RP server self signed certs
-
-Generate `key.pem` and `cert.pem` at `jans-tent` project root folder (`jans/demos/jans-tent`). i.e:
-```bash
-openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes
-```
-
-### 3. Import your OP TLS certificate
-
-(remember to be inside your virtual environment)
-
-Supply the hostname of the ISSUER after the `=`
-
-```bash
-export OP_HOSTNAME=
-```
-
-```bash
-echo | openssl s_client -servername $OP_HOSTNAME -connect $OP_HOSTNAME:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > op_web_cert.cer
-```
-
-```bash
-export CERT_PATH=$(python3 -m certifi)
-```
-
-```bash
-export SSL_CERT_FILE=${CERT_PATH}
-```
-
-```bash
-export REQUESTS_CA_BUNDLE=${CERT_PATH} && mv op_web_cert.cer $CERT_PATH
-```
-
-## Using the server
-
-### Start the server
-
-Please notice that your client will be automatically registered once the server
-starts. If your client was already registered, when you start the server again,
-it won't register. Remember to be inside your virtual environment!
-
-```bash
-python main.py
-```
-
-### Login!
-
-Navigate your browser to `https://localhost:9090` and click the link to start.
-
-## Manual client configuration
-
-In case your OP doesn't support dynamic registration, manually configure your
-client by creating a file caled `client_info.json` in the `jans-tent` folder
-with the following claims:
-
-```json
-{
- "op_metadata_url": "https://op_hostname/.well-known/openid-configuration",
- "client_id": "e4f2c3a9-0797-4c6c-9268-35c5546fb3e9",
- "client_secret": "a3e71cf1-b9b4-44c5-a9e6-4c7b5c660a5d"
-}
-```
-
-## Updating Tent to use a different OP
-
-If you want to test a different OP, do the following:
-
-1. Remove `op_web_cert` from the tent folder, and follow the procedure above
-to download and install a new OP TLS certificate
-2. Remove `client_info.json` from the tent folder
-3. Update the value of `ISSUER` in `./clientapp/config.py`
-4. Run `./register_new_client.py`
-
-## Other Tent endpoints
-
-### Auto-register endpoint
-
-Sending a `POST` request to Jans Tent `/register` endpoint containing a `JSON`
-with the OP/AS url and client url, like this:
-
-```json
-{
- "op_url": "https://OP_HOSTNAME",
- "client_url": "https://localhost:9090",
- "additional_params": {
- "scope": "openid mail profile"
- }
-}
-```
-Please notice that `additional_params` is not required by endpoint.
-
-The response will return the registered client id and client secret
-
-### Auto-config endpoint
-
-Sending a `POST` request to the Tent `/configuration` endpoint, containing the
-client id, client secret, and metadata endpoint will fetch data from OP metadata
-url and override the `config.py` settings during runtime.
-
-```json
-{
- "client_id": "e4f2c3a9-0797-4c6c-9268-35c5546fb3e9",
- "client_secret": "5c9e4775-0f1d-4a56-87c9-a629e1f88b9b",
- "op_metadata_url": "https://OP_HOSTNAME/.well-known/openid-configuration"
-}
-```
diff --git a/demos/jans-tent/behave.ini b/demos/jans-tent/behave.ini
deleted file mode 100644
index cbb1bc67a71..00000000000
--- a/demos/jans-tent/behave.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[behave]
-stderr_capture=False
-stdout_capture=False
diff --git a/demos/jans-tent/clientapp/__init__.py b/demos/jans-tent/clientapp/__init__.py
deleted file mode 100644
index a7429e815a5..00000000000
--- a/demos/jans-tent/clientapp/__init__.py
+++ /dev/null
@@ -1,251 +0,0 @@
-'''
-Project: Test Auth Client
-Author: Christian Hawk
-
-
-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.
-'''
-import base64
-import urllib
-import json
-import os
-from urllib.parse import urlparse
-from authlib.integrations.flask_client import OAuth
-from flask import (Flask, jsonify, redirect, render_template, request, session,
- url_for)
-from . import config as cfg
-from .helpers.client_handler import ClientHandler
-from .helpers.cgf_checker import register_client_if_no_client_info
-from .utils.logger import setup_logger
-
-setup_logger()
-
-oauth = OAuth()
-
-
-def add_config_from_json():
- with open('client_info.json', 'r') as openfile:
- client_info = json.load(openfile)
- cfg.SERVER_META_URL = client_info['op_metadata_url']
- cfg.CLIENT_ID = client_info['client_id']
- cfg.CLIENT_SECRET = client_info['client_secret']
- cfg.END_SESSION_ENDPOINT = client_info['end_session_endpoint'] # separate later
-
-
-def get_preselected_provider():
- provider_id_string = cfg.PRE_SELECTED_PROVIDER_ID
- provider_object = '{ "provider" : "%s" }' % provider_id_string
- provider_object_bytes = provider_object.encode()
- base64url_bytes = base64.urlsafe_b64encode(provider_object_bytes)
- base64url_value = base64url_bytes.decode()
- # if base64url_value.endswith('='):
- # base64url_value_unpad = base64url_value.replace('=', '')
- # return base64url_value_unpad
- return base64url_value
-
-
-def get_provider_host():
- provider_host_string = cfg.PROVIDER_HOST_STRING
- provider_object = '{ "providerHost" : "%s" }' % provider_host_string
- provider_object_bytes = provider_object.encode()
- base64url_bytes = base64.urlsafe_b64encode(provider_object_bytes)
- base64url_value = base64url_bytes.decode()
- # if base64url_value.endswith('='):
- # base64url_value_unpad = base64url_value.replace('=', '')
- # return base64url_value_unpad
- return base64url_value
-
-
-def ssl_verify(ssl_verify=cfg.SSL_VERIFY):
- if ssl_verify is False:
- os.environ['CURL_CA_BUNDLE'] = ""
-
-
-class BaseClientErrors(Exception):
- status_code = 500
-
-
-def create_app():
- register_client_if_no_client_info()
- add_config_from_json()
- ssl_verify()
-
- app = Flask(__name__)
-
- app.secret_key = b'fasfafpj3rasdaasfglaksdgags331s'
- app.config['OP_CLIENT_ID'] = cfg.CLIENT_ID
- app.config['OP_CLIENT_SECRET'] = cfg.CLIENT_SECRET
- oauth.init_app(app)
- oauth.register(
- 'op',
- server_metadata_url=cfg.SERVER_META_URL,
- client_kwargs={
- 'scope': cfg.SCOPE
- },
- token_endpoint_auth_method=cfg.SERVER_TOKEN_AUTH_METHOD
- )
-
- @app.route('/')
- def index():
- user = session.get('user')
- id_token = session.get('id_token')
- return render_template("home.html", user=user, id_token=id_token)
-
- @app.route('/logout')
- def logout():
- app.logger.info('Called /logout')
- if 'id_token' in session.keys():
- app.logger.info('Cleaning session credentials')
- token_hint = session.get('id_token')
- session.pop('id_token')
- session.pop('user')
- parsed_redirect_uri = urllib.parse.urlparse(cfg.REDIRECT_URIS[0])
- post_logout_redirect_uri = '%s://%s' % (parsed_redirect_uri.scheme, parsed_redirect_uri.netloc)
- return redirect(
- '%s?post_logout_redirect_uri=%s&token_hint=%s' % (
- cfg.END_SESSION_ENDPOINT, post_logout_redirect_uri, token_hint
- )
- )
-
- app.logger.info('Not authorized to logout, redirecting to index')
- return redirect(url_for('index'))
-
- @app.route('/register', methods=['POST'])
- def register():
- app.logger.info('/register called')
- content = request.json
- app.logger.debug('data = %s' % content)
- status = 0
- data = ''
- if content is None:
- status = 400
- # message = 'No json data posted'
- elif 'op_url' and 'redirect_uris' not in content:
- status = 400
- # message = 'Not needed keys found in json'
- else:
- app.logger.info('Trying to register client %s on %s' %
- (content['redirect_uris'], content['op_url']))
- op_url = content['op_url']
- redirect_uris = content['redirect_uris']
-
- op_parsed_url = urlparse(op_url)
- client_parsed_redirect_uri = urlparse(redirect_uris[0])
-
- if op_parsed_url.scheme != 'https' or client_parsed_redirect_uri.scheme != 'https':
- status = 400
-
- elif (((
- op_parsed_url.path != '' or op_parsed_url.query != '') or client_parsed_redirect_uri.path == '') or client_parsed_redirect_uri.query != ''):
- status = 400
-
- else:
- additional_metadata = {}
- if 'additional_params' in content.keys():
- additional_metadata = content['additional_params']
- client_handler = ClientHandler(
- content['op_url'], content['redirect_uris'], additional_metadata
- )
- data = client_handler.get_client_dict()
- status = 200
- return jsonify(data), status
-
- @app.route('/protected-content', methods=['GET'])
- def protected_content():
- app.logger.debug('/protected-content - cookies = %s' % request.cookies)
- app.logger.debug('/protected-content - session = %s' % session)
- if 'user' in session:
- return session['user']
-
- return redirect(url_for('login'))
-
- @app.route('/login')
- def login():
- app.logger.info('/login requested')
- redirect_uri = cfg.REDIRECT_URIS[0]
- app.logger.debug('/login redirect_uri = %s' % redirect_uri)
- # response = oauth.op.authorize_redirect()
- query_args = {
- 'redirect_uri': redirect_uri,
- }
-
- if cfg.ACR_VALUES is not None:
- query_args['acr_values'] = cfg.ACR_VALUES
-
- # used for inbound-saml, uncomment and set config.py to use it
- # if cfg.PRE_SELECTED_PROVIDER is True:
- # query_args[
- # 'preselectedExternalProvider'] = get_preselected_provider()
-
- # used for gluu-passport, , uncomment and set config.py to use it
- # if cfg.PROVIDER_HOST_STRING is not None:
- # query_args["providerHost"] = get_provider_host()
-
- if cfg.ADDITIONAL_PARAMS is not None:
- query_args |= cfg.ADDITIONAL_PARAMS
-
- response = oauth.op.authorize_redirect(**query_args)
-
- app.logger.debug('/login authorize_redirect(redirect_uri) url = %s' %
- (response.location))
-
- return response
-
- @app.route('/oidc_callback')
- @app.route('/callback')
- def callback():
- try:
- if not request.args['code']:
- return {}, 400
-
- app.logger.info('/callback - received %s - %s' %
- (request.method, request.query_string))
- token = oauth.op.authorize_access_token()
- app.logger.debug('/callback - token = %s' % token)
- user = oauth.op.userinfo()
- app.logger.debug('/callback - user = %s' % user)
- session['user'] = user
- session['id_token'] = token['userinfo']
- app.logger.debug('/callback - cookies = %s' % request.cookies)
- app.logger.debug('/callback - session = %s' % session)
-
- return redirect('/')
-
- except Exception as error:
- app.logger.error(str(error))
- return {'error': str(error)}, 400
-
- @app.route("/configuration", methods=["POST"])
- def configuration():
- # Receives client configuration via API
- app.logger.info('/configuration called')
- content = request.json
- app.logger.debug("content = %s" % content)
- if content is not None:
- if 'provider_id' in content:
- cfg.PRE_SELECTED_PROVIDER_ID = content['provider_id']
- cfg.PRE_SELECTED_PROVIDER = True
- app.logger.debug('/configuration: provider_id = %s' %
- content['provider_id'])
-
- return jsonify({"provider_id": content['provider_id']}), 200
-
- if "client_id" in content and "client_secret" in content:
- # Setup client_id and client_secret
- oauth.op.client_id = content['client_id']
- oauth.op.client_secret = content['client_secret']
- return {}, 200
- else:
- return {}, 400
-
- return app
diff --git a/demos/jans-tent/clientapp/config.py b/demos/jans-tent/clientapp/config.py
deleted file mode 100644
index 04bcb8df3b9..00000000000
--- a/demos/jans-tent/clientapp/config.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# REQUIRED
-# Replace op_hostname
-ISSUER = 'https://op_hostname'
-
-# Tent redirect uri
-REDIRECT_URIS = [
- 'https://localhost:9090/oidc_callback'
-]
-
-# OPTIONAL: Use at your own risk
-
-# Token authentication method can be
-# client_secret_basic
-# client_secret_post
-# none
-SERVER_TOKEN_AUTH_METHOD = 'client_secret_post'
-
-# ACR VALUES
-# Examples:
-# ACR_VALUES = "agama"
-# ACR_VALUES = 'simple_password_auth'
-ACR_VALUES = None
-
-# ADDITIONAL PARAMS TO CALL AUTHORIZE ENDPOINT, WITHOUT BASE64 ENCODING. USE DICT {'param': 'value'}
-# ADDITIONAL_PARAMS = {'paramOne': 'valueOne', 'paramTwo': 'valueTwo'}
-ADDITIONAL_PARAMS = None
-
-# SYSTEM SETTINGS
-# use with caution, unsecure requests, for development environments
-SSL_VERIFY = False
-
-# SCOPES
-# Only scope "openid" is required for a pairwise identifier from the OP.
-# OP can provision additional optional scopes as needed.
-# SCOPE = 'openid email profile'
-SCOPE = 'openid'
diff --git a/demos/jans-tent/clientapp/helpers/__init__.py b/demos/jans-tent/clientapp/helpers/__init__.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/demos/jans-tent/clientapp/helpers/cgf_checker.py b/demos/jans-tent/clientapp/helpers/cgf_checker.py
deleted file mode 100644
index e5ade597adf..00000000000
--- a/demos/jans-tent/clientapp/helpers/cgf_checker.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from os.path import exists
-import logging
-from clientapp.utils.dcr_from_config import register
-
-logger = logging.getLogger(__name__)
-
-
-def configuration_exists() -> bool:
- return exists('client_info.json')
-
-
-def register_client_if_no_client_info() -> None:
- if configuration_exists() :
- logger.info('Found configuration file client_info.json, skipping auto-register')
- else:
- logger.info('Client configuration not found, trying to auto-register through DCR')
- register()
diff --git a/demos/jans-tent/clientapp/helpers/client_handler.py b/demos/jans-tent/clientapp/helpers/client_handler.py
deleted file mode 100644
index 7e5f8f12e2a..00000000000
--- a/demos/jans-tent/clientapp/helpers/client_handler.py
+++ /dev/null
@@ -1,117 +0,0 @@
-'''
-Project: Test Auth Client
-Author: Christian Hawk
-
-
-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.
-'''
-import logging
-import json
-from httplib2 import RelativeURIError
-from typing import Optional, Dict, Any
-
-from oic.oauth2 import ASConfigurationResponse
-from oic.oic import Client
-from oic.utils.authn.client import CLIENT_AUTHN_METHOD
-from .custom_msg_factory import CustomMessageFactory
-
-
-logger = logging.getLogger(__name__)
-
-
-class ClientHandler:
- __redirect_uris = None
- __client_id = None
- __client_secret = None
- __metadata_url = None
- __op_url = None
- __additional_metadata = None
- __end_session_endpoint = None
- op_data = None
-
- def __init__(self, op_url: str, redirect_uris: list[str], additional_metadata: dict):
- """[initializes]
-
- :param op_url: [url from oidc provider starting with https]
- :type op_url: str
- :param redirect_uris: [url from client starting with https]
- :type redirect_uris: list
- :param additional_metadata: additional client metadata
- :type additional_metadata: dict
- """
- self.__additional_metadata = additional_metadata
- self.clientAdapter = Client(client_authn_method=CLIENT_AUTHN_METHOD, message_factory=CustomMessageFactory)
- self.__op_url = op_url
- self.__redirect_uris = redirect_uris
- self.__metadata_url = '%s/.well-known/openid-configuration' % op_url
- self.op_data = self.discover(op_url)
- self.reg_info = self.register_client(op_data=self.op_data, redirect_uris=redirect_uris)
- self.__end_session_endpoint = self.op_data['end_session_endpoint']
- self.__client_id = self.reg_info['client_id']
- self.__client_secret = self.reg_info['client_secret']
-
- def get_client_dict(self) -> dict:
- r = {
- 'op_metadata_url': self.__metadata_url,
- 'client_id': self.__client_id,
- 'client_secret': self.__client_secret,
- 'end_session_endpoint': self.__end_session_endpoint
- }
-
- return r
-
- def register_client(self, op_data: ASConfigurationResponse = op_data, redirect_uris: Optional[list[str]] = __redirect_uris) -> dict:
- """[register client and returns client information]
-
- :param op_data: [description]
- :type op_data: dict
- :param redirect_uris: [description]
- :type redirect_uris: list[str]
- :return: [client information including client-id and secret]
- :rtype: dict
- """
- logger.debug('called ClientHandler´s register_client method')
- registration_args = {'redirect_uris': redirect_uris,
- 'response_types': ['code'],
- 'grant_types': ['authorization_code'],
- 'application_type': 'web',
- 'client_name': 'Jans Tent',
- 'token_endpoint_auth_method': 'client_secret_post',
- **self.__additional_metadata
- }
- logger.info('calling register with registration_args: %s', json.dumps(registration_args, indent=2))
- reg_info = self.clientAdapter.register(op_data['registration_endpoint'], **registration_args)
- logger.info('register_client - reg_info = %s', json.dumps(reg_info.to_dict(), indent=2))
- return reg_info
-
- def discover(self, op_url: Optional[str] = __op_url) -> ASConfigurationResponse:
- """Discover op information on .well-known/open-id-configuration
- :param op_url: [description], defaults to __op_url
- :type op_url: str, optional
- :return: [data retrieved from OP url]
- :rtype: ASConfigurationResponse
- """
- logger.debug('called discover')
- try:
- op_data = self.clientAdapter.provider_config(op_url)
- return op_data
-
- except json.JSONDecodeError as err:
- logger.error('Error trying to decode JSON: %s' % err)
-
- except RelativeURIError as err:
- logger.error(err)
-
- except Exception as e:
- logging.error('An unexpected ocurred: %s' % e)
-
diff --git a/demos/jans-tent/clientapp/helpers/custom_msg_factory.py b/demos/jans-tent/clientapp/helpers/custom_msg_factory.py
deleted file mode 100644
index 11f0ae09a60..00000000000
--- a/demos/jans-tent/clientapp/helpers/custom_msg_factory.py
+++ /dev/null
@@ -1,59 +0,0 @@
-"""
-Custom message factory required by pyoic to add scope param
-Overrides RegistrationRequest, RegistrationResponse
-and use them to create CustomMessageFactory
-"""
-
-from oic.oic.message import OIDCMessageFactory, RegistrationRequest, RegistrationResponse, MessageTuple, OPTIONAL_LOGICAL
-from oic.oauth2.message import OPTIONAL_LIST_OF_STRINGS, REQUIRED_LIST_OF_STRINGS, SINGLE_OPTIONAL_STRING, SINGLE_OPTIONAL_INT
-
-
-class MyRegistrationRequest(RegistrationRequest):
- c_param = {
- "redirect_uris": REQUIRED_LIST_OF_STRINGS,
- "response_types": OPTIONAL_LIST_OF_STRINGS,
- "grant_types": OPTIONAL_LIST_OF_STRINGS,
- "application_type": SINGLE_OPTIONAL_STRING,
- "contacts": OPTIONAL_LIST_OF_STRINGS,
- "client_name": SINGLE_OPTIONAL_STRING,
- "logo_uri": SINGLE_OPTIONAL_STRING,
- "client_uri": SINGLE_OPTIONAL_STRING,
- "policy_uri": SINGLE_OPTIONAL_STRING,
- "tos_uri": SINGLE_OPTIONAL_STRING,
- "jwks": SINGLE_OPTIONAL_STRING,
- "jwks_uri": SINGLE_OPTIONAL_STRING,
- "sector_identifier_uri": SINGLE_OPTIONAL_STRING,
- "subject_type": SINGLE_OPTIONAL_STRING,
- "id_token_signed_response_alg": SINGLE_OPTIONAL_STRING,
- "id_token_encrypted_response_alg": SINGLE_OPTIONAL_STRING,
- "id_token_encrypted_response_enc": SINGLE_OPTIONAL_STRING,
- "userinfo_signed_response_alg": SINGLE_OPTIONAL_STRING,
- "userinfo_encrypted_response_alg": SINGLE_OPTIONAL_STRING,
- "userinfo_encrypted_response_enc": SINGLE_OPTIONAL_STRING,
- "request_object_signing_alg": SINGLE_OPTIONAL_STRING,
- "request_object_encryption_alg": SINGLE_OPTIONAL_STRING,
- "request_object_encryption_enc": SINGLE_OPTIONAL_STRING,
- "token_endpoint_auth_method": SINGLE_OPTIONAL_STRING,
- "token_endpoint_auth_signing_alg": SINGLE_OPTIONAL_STRING,
- "default_max_age": SINGLE_OPTIONAL_INT,
- "require_auth_time": OPTIONAL_LOGICAL,
- "default_acr_values": OPTIONAL_LIST_OF_STRINGS,
- "initiate_login_uri": SINGLE_OPTIONAL_STRING,
- "request_uris": OPTIONAL_LIST_OF_STRINGS,
- "post_logout_redirect_uris": OPTIONAL_LIST_OF_STRINGS,
- "frontchannel_logout_uri": SINGLE_OPTIONAL_STRING,
- "frontchannel_logout_session_required": OPTIONAL_LOGICAL,
- "backchannel_logout_uri": SINGLE_OPTIONAL_STRING,
- "backchannel_logout_session_required": OPTIONAL_LOGICAL,
- "scope": OPTIONAL_LIST_OF_STRINGS, # added
- }
- c_default = {"application_type": "web", "response_types": ["code"]}
- c_allowed_values = {
- "application_type": ["native", "web"],
- "subject_type": ["public", "pairwise"],
- }
-
-
-class CustomMessageFactory(OIDCMessageFactory):
- registration_endpoint = MessageTuple(MyRegistrationRequest, RegistrationResponse)
-
diff --git a/demos/jans-tent/clientapp/templates/home.html b/demos/jans-tent/clientapp/templates/home.html
deleted file mode 100644
index 021c6fcfaac..00000000000
--- a/demos/jans-tent/clientapp/templates/home.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
- Index Test
-
-
- {% endif %}
-
-
\ No newline at end of file
diff --git a/demos/jans-tent/clientapp/utils/__init__.py b/demos/jans-tent/clientapp/utils/__init__.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/demos/jans-tent/clientapp/utils/dcr_from_config.py b/demos/jans-tent/clientapp/utils/dcr_from_config.py
deleted file mode 100644
index 7ab19246abf..00000000000
--- a/demos/jans-tent/clientapp/utils/dcr_from_config.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import logging
-import urllib.parse
-
-from clientapp import config as cfg
-from clientapp.helpers.client_handler import ClientHandler
-import json
-from urllib import parse
-
-OP_URL = cfg.ISSUER
-REDIRECT_URIS = cfg.REDIRECT_URIS
-SCOPE = cfg.SCOPE
-parsed_redirect_uri = urllib.parse.urlparse(cfg.REDIRECT_URIS[0])
-POST_LOGOUT_REDIRECT_URI = '%s://%s' % (parsed_redirect_uri.scheme, parsed_redirect_uri.netloc)
-
-
-def setup_logging() -> None:
- logging.getLogger('oic')
- logging.getLogger('urllib3')
- logging.basicConfig(
- level=logging.DEBUG,
- handlers=[logging.StreamHandler(), logging.FileHandler('register_new_client.log')],
- format='[%(asctime)s] %(levelname)s %(name)s in %(module)s : %(message)s')
-
-
-def register() -> None:
- """
- Register client with information from config and write info to client_info.json
- :return: None
- """
- logger = logging.getLogger(__name__)
- scope_as_list = SCOPE.split(" ")
- additional_params = {
- 'scope': scope_as_list,
- 'post_logout_redirect_uris': [POST_LOGOUT_REDIRECT_URI]
- }
- client_handler = ClientHandler(OP_URL, REDIRECT_URIS, additional_params)
- json_client_info = json.dumps(client_handler.get_client_dict(), indent=4)
- with open('client_info.json', 'w') as outfile:
- logger.info('Writing registered client information to client_info.json')
- outfile.write(json_client_info)
-
diff --git a/demos/jans-tent/clientapp/utils/logger.py b/demos/jans-tent/clientapp/utils/logger.py
deleted file mode 100644
index acbcc2ca7bf..00000000000
--- a/demos/jans-tent/clientapp/utils/logger.py
+++ /dev/null
@@ -1,16 +0,0 @@
-import logging
-from logging.handlers import TimedRotatingFileHandler
-
-
-def setup_logger() -> None:
- formatter = logging.Formatter("[%(asctime)s] %(levelname)s %(name)s in %(module)s : %(message)s")
- log_file = "test-client.log"
- file_handler = TimedRotatingFileHandler(log_file, when='midnight')
- console_handler = logging.StreamHandler()
- console_handler.setFormatter(formatter)
- file_handler.setFormatter(formatter)
- logging.getLogger("oic")
- logging.getLogger("oauth")
- logging.getLogger("flask-oidc")
- logging.getLogger("urllib3")
- logging.basicConfig(level=logging.DEBUG, handlers=[file_handler, console_handler])
diff --git a/demos/jans-tent/docs/images/authorize_code_flow.png b/demos/jans-tent/docs/images/authorize_code_flow.png
deleted file mode 100644
index a0c26264989..00000000000
Binary files a/demos/jans-tent/docs/images/authorize_code_flow.png and /dev/null differ
diff --git a/demos/jans-tent/main.py b/demos/jans-tent/main.py
deleted file mode 100644
index ebd89f31fd6..00000000000
--- a/demos/jans-tent/main.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from clientapp import create_app
-
-if __name__ == '__main__':
- app = create_app()
- app.debug = True
- app.run(host='0.0.0.0', ssl_context=('cert.pem', 'key.pem'), port=9090, use_reloader=False)
diff --git a/demos/jans-tent/register_new_client.py b/demos/jans-tent/register_new_client.py
deleted file mode 100644
index 89061c42275..00000000000
--- a/demos/jans-tent/register_new_client.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# executes a new client auto-register from config.py
-import logging
-from clientapp.utils.dcr_from_config import register
-
-# add independent logging for CLI script
-logging.getLogger('oic')
-logging.getLogger('urllib3')
-logging.basicConfig(
- level=logging.DEBUG,
- handlers=[logging.StreamHandler(), logging.FileHandler('register_new_client.log')],
- format='[%(asctime)s] %(levelname)s %(name)s in %(module)s : %(message)s')
-register()
diff --git a/demos/jans-tent/requirements.txt b/demos/jans-tent/requirements.txt
deleted file mode 100644
index f6438fdbaae..00000000000
--- a/demos/jans-tent/requirements.txt
+++ /dev/null
@@ -1,119 +0,0 @@
-appnope==0.1.3
-astroid==2.12.5
-asttokens==2.0.8
-async-generator==1.10
-attrs==22.1.0
-Authlib==1.2.0
-autopep8==1.7.0
-backcall==0.2.0
-bandit==1.7.4
-behave==1.2.6
-certifi==2022.12.7
-cffi==1.15.1
-chardet==5.0.0
-charset-normalizer==2.1.1
-click==8.1.3
-coverage==6.4.4
-cryptography==42.0.0
-decorator==5.1.1
-defusedxml==0.7.1
-dill==0.3.5.1
-dodgy==0.2.1
-EasyProcess==1.1
-executing==1.0.0
-flake8==5.0.4
-Flask==2.2.2
-flask-oidc==1.4.0
-future==0.18.3
-gitdb==4.0.9
-GitPython==3.1.37
-h11==0.13.0
-httplib2==0.21.0
-idna==3.3
-importlib-metadata==4.12.0
-iniconfig==1.1.1
-install==1.3.5
-ipdb==0.13.9
-ipython==8.10.0
-ipython-genutils==0.2.0
-isort==5.10.1
-itsdangerous==2.0.0
-jedi==0.18.1
-Jinja2==3.1.2
-lazy-object-proxy==1.7.1
-Mako==1.2.4
-MarkupSafe==2.1.1
-matplotlib-inline==0.1.6
-mccabe==0.7.0
-more-itertools==8.14.0
-mypy==0.971
-mypy-extensions==0.4.3
-oauth2client==4.1.3
-oic==1.5.0
-outcome==1.2.0
-packaging==21.3
-parse==1.19.0
-parse-type==0.6.0
-parso==0.8.3
-pbr==5.10.0
-pep8==1.7.1
-pep8-naming==0.13.2
-pexpect==4.8.0
-pickleshare==0.7.5
-platformdirs==2.5.2
-pluggy==1.0.0
-poetry-semver==0.1.0
-prompt-toolkit==3.0.31
-prospector==0.12.2
-ptyprocess==0.7.0
-pure-eval==0.2.2
-py==1.11.0
-pyasn1==0.4.8
-pyasn1-modules==0.2.8
-pycodestyle==2.9.1
-pycparser==2.21
-pycryptodomex==3.17
-pydocstyle==6.1.1
-pyflakes==2.5.0
-Pygments==2.13.0
-pyjwkest==1.4.2
-pylama==8.4.1
-pylint==2.15.0
-pylint-celery==0.3
-pylint-common==0.2.5
-pylint-django==2.5.3
-pylint-flask==0.6
-pylint-plugin-utils==0.7
-pyOpenSSL==22.0.0
-pyparsing==3.0.9
-PySocks==1.7.1
-pytest==7.1.3
-python-dotenv==0.21.0
-PyVirtualDisplay==3.0
-PyYAML==6.0
-requests==2.28.1
-requirements-detector==1.0.3
-rsa==4.9
-selenium==4.4.3
-setoptconf==0.3.0
-six==1.16.0
-smmap==5.0.0
-sniffio==1.3.0
-snowballstemmer==2.2.0
-sortedcontainers==2.4.0
-stack-data==0.5.0
-stevedore==4.0.0
-toml==0.10.2
-tomli==2.0.1
-tomlkit==0.11.4
-traitlets==5.3.0
-trio==0.21.0
-trio-websocket==0.9.2
-typed-ast==1.5.4
-typing_extensions==4.3.0
-urllib3==1.26.12
-wcwidth==0.2.5
-Werkzeug==2.2.2
-wrapt==1.14.1
-wsproto==1.2.0
-zipp==3.8.1
diff --git a/demos/jans-tent/tests/behaver/features/environment.py b/demos/jans-tent/tests/behaver/features/environment.py
deleted file mode 100644
index 037be43286f..00000000000
--- a/demos/jans-tent/tests/behaver/features/environment.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from selenium import webdriver
-import os
-from pyvirtualdisplay import Display
-
-display = Display(visible=0, size=(1024, 768))
-
-
-def before_all(context):
- os.environ['CURL_CA_BUNDLE'] = ""
- display.start()
-
-
-def before_scenario(context, scenario):
- options = webdriver.FirefoxOptions()
- options.headless = True
- context.web = webdriver.Firefox()
-
- # context.web = webdriver.Firefox()
-
-
-def after_scenario(context, scenario):
- context.web.delete_all_cookies()
- context.web.close()
-
-
-def after_step(context, step):
- print()
-
-
-def after_all(context):
- pass
diff --git a/demos/jans-tent/tests/behaver/features/oidc_auth.feature b/demos/jans-tent/tests/behaver/features/oidc_auth.feature
deleted file mode 100644
index b95f5df3e61..00000000000
--- a/demos/jans-tent/tests/behaver/features/oidc_auth.feature
+++ /dev/null
@@ -1,40 +0,0 @@
-Feature: Allow authenticated users to access protected pages
-
- @authenticated
- Scenario: User is authenticated
- Given username is "johndoe"
- And user is authenticated
- And protected content link is https://chris.testingenv.org/protected-content
- When user clicks the protected content link
- Then user access the protected content link
-
- Scenario: User does not exist
- Given user does not exist
- And protected content link is https://chris.testingenv.org/protected-content
- When user clicks the protected content link
- Then user goes to external login page
-
- Scenario: User is not authenticated
- Given username is "johndoe"
- And protected content link is https://chris.testingenv.org/protected-content
- When user clicks the protected content link
- Then user goes to external login page
-
- # Scenario: Normal user try to access admin content
- # Given username is "johndoe"
- # And user role is "user"
- # And protected content link is https://chris.testingenv.org/admin/admin-protected-content
- # When user clicks the protected content link
- # Then user gets a 403 error
-
- # Scenario: Admin can access admin contents
- # Given username is "johndoe"
- # And user role is "admin"
- # And protected content link is https://chris.testingenv.org/admin/admin-protected-content
- # When user clicks the protected content link
- # Then user access the protected content link
-
-
-
-
-
diff --git a/demos/jans-tent/tests/behaver/features/passport_social_auth.feature b/demos/jans-tent/tests/behaver/features/passport_social_auth.feature
deleted file mode 100644
index 685576147f2..00000000000
--- a/demos/jans-tent/tests/behaver/features/passport_social_auth.feature
+++ /dev/null
@@ -1,26 +0,0 @@
-Feature: use passport social github to login
- """
- As an user,
- I want to use passport-social flow to authenticate
- So I can access protected-content
- """
-
- Background:
- Given auth method is passport-social
- And user is visiting "/"
-
- Scenario: User is authenticated
- Given username is "johndoe"
- And protected content link is https://localhost:5000/content/protected-user-content
- When user clicks the protected content link
- Then user access the protected content link
-
- Scenario: User is not authenticated
- Given user is not authenticated
- When user clicks the protected content link
- Then user goes to external login page
-
-
-
-
-
\ No newline at end of file
diff --git a/demos/jans-tent/tests/behaver/features/steps/allow.py b/demos/jans-tent/tests/behaver/features/steps/allow.py
deleted file mode 100644
index 97a6ddfdb4a..00000000000
--- a/demos/jans-tent/tests/behaver/features/steps/allow.py
+++ /dev/null
@@ -1,116 +0,0 @@
-from behave import when, then, given
-import requests
-import time
-from selenium.webdriver.common.by import By
-
-base_url = "https://chris.testingenv.org"
-
-
-def cookiesTransformer(sel_session_id, sel_other_cookies):
- ''' This transform cookies from selenium to requests '''
- s = requests.Session()
- s.cookies.set('session_id', sel_session_id)
- i = 0
- while i < len(sel_other_cookies):
- s.cookies.set(sel_other_cookies[i]['name'],
- sel_other_cookies[i]['value'],
- path=sel_other_cookies[i]['path'],
- domain=sel_other_cookies[i]['domain'],
- secure=sel_other_cookies[i]['secure'],
- rest={'httpOnly': sel_other_cookies[i]['httpOnly']})
- i = i + 1
-
- return s
-
-
-@given(u'username is "{username}"')
-def define_username(context, username):
- context.username = username
- context.password = "test123"
-
-
-@given(u'user is authenticated')
-def user_authenticates(context):
- context.web.get("https://chris.testingenv.org/login")
- time.sleep(3)
- context.web.set_window_size(625, 638)
- context.web.find_element(By.ID, "username").click()
- context.web.find_element(By.ID, "username").send_keys("johndoo")
- time.sleep(3)
- context.web.find_element(By.ID, "password").send_keys("test123")
- context.web.find_element(By.ID, "loginButton").click()
- time.sleep(3)
-
-
-@given(u'protected content link is {protected_content}')
-def define_protected_content_link(context, protected_content):
- context.protected_content = protected_content
-
-
-@when(u'user clicks the protected content link')
-def user_clicks_protected_content_link(context):
-
- context.web.get(base_url)
- time.sleep(2)
- context.web.find_element_by_xpath(
- '//a[@href="' + "https://chris.testingenv.org/protected-content" +
- '"]').click()
- context.has_clicked = True
- context.response = requests.get(context.protected_content)
-
-
-@then(u'user access the protected content link')
-def user_access_protected_content_link(context):
- # WE FETCH THE COOKIES FROM SELENIUM AND PASS THEM TO REQUESTS TO VALIDATE
- #sel_cookies = context.web.get_cookies()
- #sel_cookie = sel_cookies[0]
- # set cookie in requests
-
- # get session id from selenium
- #sel_session_id = context.web.session_id
- '''
- sess = requests.Session()
-
- sess.cookies.set('session_id',sel_session_id)
- sess.cookies.set(
- sel_cookie['name'],
- sel_cookie['value'],
- path = sel_cookie['path'],
- domain = sel_cookie['domain'],
- secure = sel_cookie['secure'],
- rest= {'httpOnly' : sel_cookie['httpOnly']}
- )
-
- new_sess = cookiesTransformer(sel_session_id,sel_cookies)
- '''
- new_sess = cookiesTransformer(context.web.session_id,
- context.web.get_cookies())
- res = new_sess.get(context.protected_content, verify=False)
-
- assert res.url == context.protected_content
-
-
-@given(u'user does not exist')
-def user_does_not_exist(context):
- pass
-
-
-@then(u'user goes to external login page')
-def user_directed_to_external_login_page(context):
- #context.web.get("https://chris.testingenv.org/login")
-
- time.sleep(1)
- external_login_url = 'https://chris.gluutwo.org/oxauth/login.htm'
- #import ipdb; ipdb.set_trace()
- assert (context.web.current_url == external_login_url)
- #new_sess = cookiesTransformer(context.web.session_id,context.web.get_cookies())
-
-
-@given(u'user role is "{role}"')
-def define_user_role(context, role):
- context.role = role
-
-
-@then(u'user gets a 403 error')
-def step_impl(context):
- raise NotImplementedError(u'STEP: Then user gets a 403 error')
diff --git a/demos/jans-tent/tests/unit_integration/helper.py b/demos/jans-tent/tests/unit_integration/helper.py
deleted file mode 100644
index b282f4b64ab..00000000000
--- a/demos/jans-tent/tests/unit_integration/helper.py
+++ /dev/null
@@ -1,189 +0,0 @@
-
-from unittest import TestCase
-from unittest.mock import MagicMock
-import clientapp
-from clientapp import create_app
-from clientapp.helpers.client_handler import ClientHandler
-from flask import Flask
-from typing import List
-import helper
-import os
-import builtins
-
-
-class FlaskBaseTestCase(TestCase):
- def setUp(self):
- self.stashed_add_config_from_json = clientapp.add_config_from_json
- clientapp.cfg.CLIENT_ID = 'any-client-id-stub'
- clientapp.cfg.CLIENT_SECRET = 'any-client-secret-stub'
- clientapp.cfg.SERVER_META_URL = 'https://ophostname.com/server/meta/url'
- clientapp.cfg.END_SESSION_ENDPOINT = 'https://ophostname.com/end_session_endpoint'
- clientapp.add_config_from_json = MagicMock(name='add_config_from_json')
- clientapp.add_config_from_json.return_value(None)
- self.stashed_discover = ClientHandler.discover
- self.stashed_register_client = ClientHandler.register_client
- self.stashed_open = builtins.open
- builtins.open = MagicMock(name='open')
- ClientHandler.discover = MagicMock(name='discover')
- ClientHandler.discover.return_value = helper.OP_DATA_DICT_RESPONSE
- ClientHandler.register_client = MagicMock(name='register_client')
- ClientHandler.register_client.return_value = helper.REGISTER_CLIENT_RESPONSE
- self.app = create_app()
- self.app.testing = True
- self.app_context = self.app.test_request_context(
- base_url="https://chris.testingenv.org")
- self.app_context.push()
- self.client = self.app.test_client()
-
- #self.oauth = OAuth(self.app)
- os.environ['AUTHLIB_INSECURE_TRANSPORT'] = "1"
-
- def tearDown(self) -> None:
- ClientHandler.discover = self.stashed_discover
- ClientHandler.register_client = self.stashed_register_client
- builtins.open = self.stashed_open
- clientapp.add_config_from_json = self.stashed_add_config_from_json
-
-
-# Helper functions
-def app_endpoints(app: Flask) -> List[str]:
- """ Return all enpoints in app """
- endpoints = []
- for item in app.url_map.iter_rules():
- endpoint = item.endpoint.replace("_", "-")
- endpoints.append(endpoint)
- return endpoints
-
-
-# Mocks
-OP_DATA_DICT_RESPONSE = {
- 'request_parameter_supported': True,
- 'token_revocation_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/revoke',
- 'introspection_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/introspection',
- 'claims_parameter_supported': False,
- 'issuer': 'https://t1.techno24x7.com',
- 'userinfo_encryption_enc_values_supported': ['RSA1_5', 'RSA-OAEP', 'A128KW', 'A256KW'],
- 'id_token_encryption_enc_values_supported': ['A128CBC+HS256', 'A256CBC+HS512', 'A128GCM', 'A256GCM'],
- 'authorization_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/authorize',
- 'service_documentation': 'http://gluu.org/docs',
- 'id_generation_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/id',
- 'claims_supported': ['street_address', 'country', 'zoneinfo', 'birthdate', 'role', 'gender', 'formatted',
- 'user_name', 'phone_mobile_number', 'preferred_username', 'locale', 'inum', 'updated_at',
- 'nickname', 'email', 'website', 'email_verified', 'profile', 'locality',
- 'phone_number_verified', 'given_name', 'middle_name', 'picture', 'name', 'phone_number',
- 'postal_code', 'region', 'family_name'],
- 'scope_to_claims_mapping': [{
- 'profile': ['name', 'family_name', 'given_name', 'middle_name', 'nickname', 'preferred_username', 'profile',
- 'picture', 'website', 'gender', 'birthdate', 'zoneinfo', 'locale', 'updated_at']
- }, {
- 'openid': []
- }, {
- 'https://t1.techno24x7.com/oxauth/restv1/uma/scopes/scim_access': []
- }, {
- 'permission': ['role']
- }, {
- 'super_gluu_ro_session': []
- }, {
- 'https://t1.techno24x7.com/oxauth/restv1/uma/scopes/passport_access': []
- }, {
- 'phone': ['phone_number_verified', 'phone_number']
- }, {
- 'revoke_session': []
- }, {
- 'address': ['formatted', 'postal_code', 'street_address', 'locality', 'country', 'region']
- }, {
- 'clientinfo': ['name', 'inum']
- }, {
- 'mobile_phone': ['phone_mobile_number']
- }, {
- 'email': ['email_verified', 'email']
- }, {
- 'user_name': ['user_name']
- }, {
- 'oxtrust-api-write': []
- }, {
- 'oxd': []
- }, {
- 'uma_protection': []
- }, {
- 'oxtrust-api-read': []
- }],
- 'op_policy_uri': 'http://ox.gluu.org/doku.php?id=oxauth:policy',
- 'token_endpoint_auth_methods_supported': ['client_secret_basic', 'client_secret_post', 'client_secret_jwt',
- 'private_key_jwt', 'tls_client_auth', 'self_signed_tls_client_auth'],
- 'tls_client_certificate_bound_access_tokens': True,
- 'response_modes_supported': ['query', 'form_post', 'fragment'],
- 'backchannel_logout_session_supported': True,
- 'token_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/token',
- 'response_types_supported': ['code id_token', 'code', 'id_token', 'token', 'code token', 'code id_token token',
- 'id_token token'],
- 'request_uri_parameter_supported': True,
- 'backchannel_user_code_parameter_supported': False,
- 'grant_types_supported': ['implicit', 'refresh_token', 'client_credentials', 'authorization_code', 'password',
- 'urn:ietf:params:oauth:grant-type:uma-ticket'],
- 'ui_locales_supported': ['en', 'bg', 'de', 'es', 'fr', 'it', 'ru', 'tr'],
- 'userinfo_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/userinfo',
- 'op_tos_uri': 'http://ox.gluu.org/doku.php?id=oxauth:tos',
- 'auth_level_mapping': {
- '-1': ['simple_password_auth'],
- '60': ['passport_saml'],
- '40': ['passport_social']
- },
- 'require_request_uri_registration': False,
- 'id_token_encryption_alg_values_supported': ['RSA1_5', 'RSA-OAEP', 'A128KW', 'A256KW'],
- 'frontchannel_logout_session_supported': True,
- 'claims_locales_supported': ['en'],
- 'clientinfo_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/clientinfo',
- 'request_object_signing_alg_values_supported': ['none', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512',
- 'ES256', 'ES384', 'ES512'],
- 'request_object_encryption_alg_values_supported': ['RSA1_5', 'RSA-OAEP', 'A128KW', 'A256KW'],
- 'session_revocation_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/revoke_session',
- 'check_session_iframe': 'https://t1.techno24x7.com/oxauth/opiframe.htm',
- 'scopes_supported': ['address', 'openid', 'clientinfo', 'user_name', 'profile',
- 'https://t1.techno24x7.com/oxauth/restv1/uma/scopes/scim_access', 'uma_protection',
- 'permission', 'revoke_session', 'oxtrust-api-write', 'oxtrust-api-read', 'phone',
- 'mobile_phone', 'oxd', 'super_gluu_ro_session', 'email',
- 'https://t1.techno24x7.com/oxauth/restv1/uma/scopes/passport_access'],
- 'backchannel_logout_supported': True,
- 'acr_values_supported': ['simple_password_auth', 'passport_saml', 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password',
- 'urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol',
- 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport', 'passport_social'],
- 'request_object_encryption_enc_values_supported': ['A128CBC+HS256', 'A256CBC+HS512', 'A128GCM', 'A256GCM'],
- 'display_values_supported': ['page', 'popup'],
- 'userinfo_signing_alg_values_supported': ['HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512', 'ES256', 'ES384',
- 'ES512'],
- 'claim_types_supported': ['normal'],
- 'userinfo_encryption_alg_values_supported': ['RSA1_5', 'RSA-OAEP', 'A128KW', 'A256KW'],
- 'end_session_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/end_session',
- 'revocation_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/revoke',
- 'backchannel_authentication_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/bc-authorize',
- 'token_endpoint_auth_signing_alg_values_supported': ['HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512', 'ES256',
- 'ES384', 'ES512'],
- 'frontchannel_logout_supported': True,
- 'jwks_uri': 'https://t1.techno24x7.com/oxauth/restv1/jwks',
- 'subject_types_supported': ['public', 'pairwise'],
- 'id_token_signing_alg_values_supported': ['none', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512', 'ES256',
- 'ES384', 'ES512'],
- 'registration_endpoint': 'https://t1.techno24x7.com/oxauth/restv1/register',
- 'id_token_token_binding_cnf_values_supported': ['tbh']
-}
-
-REGISTER_CLIENT_RESPONSE = {'allow_spontaneous_scopes': False, 'application_type': 'web', 'rpt_as_jwt': False,
- 'registration_client_uri': 'https://t1.techno24x7.com/jans-auth/restv1/register?client_id'
- '=079f3682-3d60-4bca-8ff7-bbc7dbc18cd7',
- 'run_introspection_script_before_jwt_creation': False,
- 'registration_access_token': '89c51fd6-34ec-497e-a4ae-85e21b7e725b',
- 'client_id': '079f3682-3d60-4bca-8ff7-bbc7dbc18cd7',
- 'token_endpoint_auth_method': 'client_secret_post',
- 'scope': 'online_access device_sso openid permission uma_protection offline_access',
- 'client_secret': '8f53c454-f6ee-4181-8581-9f1ee120b878', 'client_id_issued_at': 1680038429,
- 'backchannel_logout_session_required': False, 'client_name': 'Jans Tent',
- 'par_lifetime': 600, 'spontaneous_scopes': [], 'id_token_signed_response_alg': 'RS256',
- 'access_token_as_jwt': False, 'grant_types': ['authorization_code'],
- 'subject_type': 'pairwise', 'additional_token_endpoint_auth_methods': [],
- 'keep_client_authorization_after_expiration': False, 'require_par': False,
- 'redirect_uris': ['https://localhost:9090/oidc_callback'], 'additional_audience': [],
- 'frontchannel_logout_session_required': False, 'client_secret_expires_at': 0,
- 'access_token_signing_alg': 'RS256', 'response_types': ['code']}
-
-
diff --git a/demos/jans-tent/tests/unit_integration/test_callback_endpoint.py b/demos/jans-tent/tests/unit_integration/test_callback_endpoint.py
deleted file mode 100644
index 91ebb43edca..00000000000
--- a/demos/jans-tent/tests/unit_integration/test_callback_endpoint.py
+++ /dev/null
@@ -1,62 +0,0 @@
-import clientapp
-from flask import Flask, url_for
-from typing import List
-from helper import FlaskBaseTestCase
-
-
-def app_endpoints(app: Flask) -> List[str]:
- """ Return all enpoints in app """
-
- endpoints = []
- for item in app.url_map.iter_rules():
- endpoint = item.endpoint.replace("_", "-")
- endpoints.append(endpoint)
- return endpoints
-
-
-# class FlaskBaseTestCase(TestCase):
-# def setUp(self):
-# self.app = clientapp.create_app()
-# self.app.testing = True
-# self.app_context = self.app.test_request_context(
-# base_url="https://chris.testingenv.org")
-# self.app_context.push()
-# self.client = self.app.test_client()
-# #self.oauth = OAuth(self.app)
-# os.environ['AUTHLIB_INSECURE_TRANSPORT'] = "1"
-
-
-class TestCallbackEndpoint(FlaskBaseTestCase):
- def test_oidc_callback_endpoint_exist(self):
- endpoints = []
- for item in clientapp.create_app().url_map.iter_rules():
- endpoint = item.rule
- endpoints.append(endpoint)
-
- self.assertTrue('/oidc_callback' in endpoints,
- "enpoint /oidc_callback knão existe no app")
-
- def test_callback_endpoint_should_exist(self):
-
- self.assertTrue('callback' in app_endpoints(clientapp.create_app()),
- 'endpoint /callback does not exist in app')
-
- def test_endpoint_args_without_code_should_return_400(self):
- resp = self.client.get(url_for('callback'))
-
- self.assertEqual(resp.status_code, 400)
-
-
-'''
- def test_endpoint_should_return_status_code_302(self):
- # if there is
-
- self.assertEqual(
- self.client.get(url_for('callback')).status_code,
- 302,
- 'Callback endpoint is not returning 302 status_code'
- )
-
-
- #def test_endpoint_should_return_
-'''
diff --git a/demos/jans-tent/tests/unit_integration/test_cfg_checker.py b/demos/jans-tent/tests/unit_integration/test_cfg_checker.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/demos/jans-tent/tests/unit_integration/test_client_register_endpoint.py b/demos/jans-tent/tests/unit_integration/test_client_register_endpoint.py
deleted file mode 100644
index 83aef096738..00000000000
--- a/demos/jans-tent/tests/unit_integration/test_client_register_endpoint.py
+++ /dev/null
@@ -1,145 +0,0 @@
-from helper import FlaskBaseTestCase
-import clientapp
-import helper
-from flask import url_for
-from clientapp.helpers.client_handler import ClientHandler
-from unittest.mock import MagicMock, patch
-
-
-class TestRegisterEndpoint(FlaskBaseTestCase):
-
- def test_if_app_has_register_endpoint(self):
- self.assertIn(
- 'register',
- helper.app_endpoints(clientapp.create_app())
- )
-
- def test_if_endpoint_accepts_post(self):
- methods = None
- for rule in self.app.url_map.iter_rules('register'):
- methods = rule.methods
- self.assertIn(
- 'POST',
- methods
- )
-
- # def test_init_should_call_discover_once(self):
- # ClientHandler.discover = MagicMock(name='discover')
- # ClientHandler.discover.return_value = helper.OP_DATA_DICT_RESPONSE
- # ClientHandler.discover.assert_called_once()
-
- def test_endpoint_should_return_valid_req(self):
- self.assertIn(
- self.client.post(url_for('register')).status_code,
- range(100, 511),
- '/register returned invalid requisition'
- )
-
- @patch('clientapp.helpers.client_handler.ClientHandler.__init__', MagicMock(return_value=None))
- def test_endpoint_should_init_client_handler(self):
- self.client.post(url_for('register'), json={
- 'op_url': 'https://test.com',
- 'redirect_uris': ['https://clienttoberegistered.com/oidc_callback']
- })
- ClientHandler.__init__.assert_called_once()
-
- @patch('clientapp.helpers.client_handler.ClientHandler.__init__', MagicMock(return_value=None))
- def test_endpoint_should_accept_2_params(self):
- first_value = 'https://op'
- second_value = ['https://client.com.br/oidc_callback']
-
- self.client.post(url_for('register'), json={
- 'op_url': first_value,
- 'redirect_uris': second_value
- })
- ClientHandler.__init__.assert_called_once_with(first_value, second_value, {})
-
- @patch('clientapp.helpers.client_handler.ClientHandler.__init__', MagicMock(return_value=None))
- def test_endpoint_should_accept_3_params(self):
- first_value = 'https://op'
- second_value = ['https://client.com.br/oidc_callback']
- third_value = {'scope': 'openid email profile'}
-
- self.client.post(url_for('register'), json={
- 'op_url': first_value,
- 'redirect_uris': second_value,
- 'additional_params': third_value
- })
-
- ClientHandler.__init__.assert_called_once_with(first_value, second_value, third_value)
-
- def test_endpoint_should_return_error_code_400_if_no_data_sent(self):
- self.assertEqual(
- self.client.post(url_for('register')).status_code,
- 400,
- 'status_code for empty request is NOT 400'
- )
-
- def test_should_return_400_error_if_no_needed_keys_provided(self):
- self.assertEqual(
- self.client.post(url_for('register'), json={
- 'other_key': 'othervalue',
- 'another_key': 'another_value'
- }).status_code,
- 400,
- 'not returning 400 code if no needed keys provided'
- )
-
- def test_should_return_400_if_values_are_not_valid_urls(self):
- self.assertEqual(
- self.client.post(url_for('register'), json={
- 'op_url': 'not_valid_url',
- 'redirect_uris': ['https://clienttoberegistered.com/oidc_callback']
- }).status_code,
- 400,
- 'not returning status 400 if values are not valid urls'
- )
-
- @patch('clientapp.helpers.client_handler.ClientHandler.get_client_dict', MagicMock(return_value=None))
- def test_valid_post_should_should_call_get_client_dict_once(self):
- op_url = 'https://op.com.br'
- self.client.post(url_for('register'), json={
- 'op_url': op_url,
- 'redirect_uris': ['https://clienttoberegistered.com/oidc_callback']
- })
- ClientHandler.get_client_dict.assert_called_once()
-
- def test_should_should_return_200_if_registered(self):
- op_url = 'https://op.com.br'
- test_client_id = '1234-5678-9ten11'
- test_client_secret = 'mysuperprotectedsecret'
- with patch.object(ClientHandler, 'get_client_dict', return_value={
- 'op_metadata_url': '%s/.well-known/open-id-configuration' % op_url,
- 'client_id': test_client_id,
- 'client_secret': test_client_secret
- }) as get_client_dict:
- response = self.client.post(url_for('register'), json={
- 'op_url': op_url,
- 'redirect_uris': ['https://clienttoberegistered.com/oidc_callback']
- })
- self.assertEqual(response.status_code, 200)
- get_client_dict.reset()
-
- def test_should_return_expected_keys(self):
- op_url = 'https://op.com.br'
- redirect_uris = ['https://client.com.br/oidc_calback']
- test_client_id = '1234-5678-9ten11'
- test_client_secret = 'mysuperprotectedsecret'
- additional_params = {'param1': 'value1'}
-
- expected_keys = {'op_metadata_url', 'client_id', 'client_secret'}
-
- with patch.object(ClientHandler, 'get_client_dict', return_value={
- 'op_metadata_url': '%s/.well-known/open-id-configuration' % op_url,
- 'client_id': test_client_id,
- 'client_secret': test_client_secret
- }) as get_client_dict:
- response = self.client.post(url_for('register'), json={
- 'op_url': op_url,
- 'redirect_uris': redirect_uris,
- 'additional_params': additional_params
- })
- print(response)
- assert expected_keys <= response.json.keys(), response.json
-
- get_client_dict.reset()
diff --git a/demos/jans-tent/tests/unit_integration/test_config.py b/demos/jans-tent/tests/unit_integration/test_config.py
deleted file mode 100644
index 419e3bd56a9..00000000000
--- a/demos/jans-tent/tests/unit_integration/test_config.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import clientapp.config as cfg
-from unittest import TestCase
-
-
-class TestConfig(TestCase):
- def test_has_attribute_SSL_VERIFY(self):
- self.assertTrue(hasattr(cfg, 'SSL_VERIFY'),
- 'SSL_VERIFY attribute is missing in config.')
-
- def test_SSL_VERIFY_has_boolean_value(self):
- self.assertTrue('__bool__' in cfg.SSL_VERIFY.__dir__(),
- 'SSL_VERIFY is not boolean.')
-
- def test_has_attribute_SCOPE(self):
- self.assertTrue(hasattr(cfg, 'SCOPE'),
- 'SCOPE attribute is missing in config.')
-
- def test_SCOPE_default_should_be_openid(self):
- self.assertTrue(cfg.SCOPE == 'openid')
diff --git a/demos/jans-tent/tests/unit_integration/test_configuration_endpoint.py b/demos/jans-tent/tests/unit_integration/test_configuration_endpoint.py
deleted file mode 100644
index 1890b1d4cee..00000000000
--- a/demos/jans-tent/tests/unit_integration/test_configuration_endpoint.py
+++ /dev/null
@@ -1,107 +0,0 @@
-import clientapp
-from flask import Flask, url_for
-from typing import List
-import json
-from helper import FlaskBaseTestCase
-
-
-def app_endpoints(app: Flask) -> List[str]:
- """ Return all enpoints in app """
-
- endpoints = []
- for item in app.url_map.iter_rules():
- endpoint = item.endpoint.replace("_", "-")
- endpoints.append(endpoint)
- return endpoints
-
-
-def valid_client_configuration():
- return {
- "client_id": "my-client-id",
- "client_secret": "my-client-secret",
- "op_metadata_url": "https://op.com/.well-known/openidconfiguration"
- }
-
-
-class TestConfigurationEndpoint(FlaskBaseTestCase):
- def test_create_app_has_configuration(self):
- self.assertTrue(
- 'configuration' in app_endpoints(clientapp.create_app()),
- 'endpoint /configuration does not exist in app')
-
- def test_configuration_endpoint_should_return_valid_req(self):
- self.assertIn(
- self.client.post(url_for('configuration')).status_code,
- range(100, 511), '/configuration returned invalid requisition')
-
- def test_endpoint_should_return_200_if_valid_json(self):
- headers = {'Content-type': 'application/json'}
- data = {'provider_id': 'whatever'}
- json_data = json.dumps(data)
- response = self.client.post(url_for('configuration'),
- data=json_data,
- headers=headers)
- self.assertEqual(response.status_code, 200)
-
- def test_endpoint_should_return_posted_data_if_valid_json(self):
- headers = {'Content-type': 'application/json'}
- data = {'provider_id': 'whatever'}
- json_data = json.dumps(data)
- response = self.client.post(url_for('configuration'),
- data=json_data,
- headers=headers)
-
- self.assertEqual(json_data, json.dumps(response.json))
-
- def test_endpoint_should_setup_cfg_with_provider_id(self):
- headers = {'Content-type': 'application/json'}
- data = {'provider_id': 'whatever'}
- json_data = json.dumps(data)
- self.client.post(url_for('configuration'),
- data=json_data,
- headers=headers)
-
- self.assertEqual(clientapp.cfg.PRE_SELECTED_PROVIDER_ID, 'whatever')
-
- def test_endpoint_should_setup_cfg_with_pre_selected_provider_true(self):
- clientapp.cfg.PRE_SELECTED_PROVIDER = False
- headers = {'Content-type': 'application/json'}
- data = {'provider_id': 'whatever'}
- json_data = json.dumps(data)
- self.client.post(url_for('configuration'),
- data=json_data,
- headers=headers)
-
- self.assertTrue(clientapp.cfg.PRE_SELECTED_PROVIDER, )
-
- def test_endpoint_should_return_200_if_valid_client_config(self):
- headers = {'Content-type': 'application/json'}
- json_data = json.dumps(valid_client_configuration())
- response = self.client.post(
- url_for('configuration'), data=json_data, headers=headers)
- self.assertEqual(response.status_code, 200,
- 'endpoint is NOT returning 200 for valid client configuration')
-
- def test_endpoint_should_register_new_oauth_client_id(self):
- headers = {'Content-type': 'application/json'}
- client_id = "my-client-id"
- client_secret = "my-client-secret"
- op_metadata_url = "https://op.com/.well-known/openidconfiguration"
- json_data = json.dumps({
- "client_id": client_id,
- "client_secret": client_secret,
- "op_metadata_url": op_metadata_url
- })
- self.client.post(
- url_for('configuration'), data=json_data, headers=headers)
- self.assertTrue(clientapp.oauth.op.client_id == client_id,
- 'endpoint is NOT changing op.client_id')
-
- def test_endpoint_should_register_new_oauth_client_secret(self):
- headers = {'Content-type': 'application/json'}
- json_data = json.dumps(valid_client_configuration())
- client_secret = valid_client_configuration()['client_secret']
- self.client.post(
- url_for('configuration'), data=json_data, headers=headers)
- self.assertTrue(clientapp.oauth.op.client_secret == client_secret,
- '%s is is not %s' % (clientapp.oauth.op.client_secret, client_secret))
diff --git a/demos/jans-tent/tests/unit_integration/test_dcr_from_config.py b/demos/jans-tent/tests/unit_integration/test_dcr_from_config.py
deleted file mode 100644
index e02f909ea28..00000000000
--- a/demos/jans-tent/tests/unit_integration/test_dcr_from_config.py
+++ /dev/null
@@ -1,76 +0,0 @@
-from clientapp.utils import dcr_from_config
-from clientapp import config as cfg
-from unittest.mock import MagicMock, patch, mock_open
-from unittest import TestCase
-from clientapp.helpers.client_handler import ClientHandler
-import helper
-import json
-import builtins
-
-class TestDrcFromConfig(TestCase):
-
- def setUp(self) -> None:
- # stashing to restore on teardown
- self.stashed_discover = ClientHandler.discover
- self.stashed_register_client = ClientHandler.register_client
- self.stashed_open = builtins.open
- ClientHandler.discover = MagicMock(name='discover')
- ClientHandler.discover.return_value = helper.OP_DATA_DICT_RESPONSE
- ClientHandler.register_client = MagicMock(name='register_client')
- ClientHandler.register_client.return_value = helper.REGISTER_CLIENT_RESPONSE
- builtins.open = MagicMock(name='open')
-
- def tearDown(self) -> None:
- ClientHandler.discover = self.stashed_discover
- ClientHandler.register_client = self.stashed_register_client
- builtins.open = self.stashed_open
-
- def test_if_setup_logging_exists(self):
- assert hasattr(dcr_from_config, 'setup_logging')
-
- def test_if_static_variables_exists(self):
- assert hasattr(dcr_from_config, 'OP_URL')
- assert hasattr(dcr_from_config, 'REDIRECT_URIS')
-
- def test_if_static_variables_from_config(self):
- assert dcr_from_config.OP_URL == cfg.ISSUER
- assert dcr_from_config.REDIRECT_URIS == cfg.REDIRECT_URIS
-
- def test_register_should_be_calable(self):
- assert callable(dcr_from_config.register), 'not callable'
-
- @patch('clientapp.helpers.client_handler.ClientHandler.__init__', MagicMock(return_value=None))
- def test_register_should_call_ClientHandler(self):
- dcr_from_config.register()
- ClientHandler.__init__.assert_called_once()
-
- @patch('clientapp.helpers.client_handler.ClientHandler.__init__', MagicMock(return_value=None))
- def test_register_should_call_ClientHandler_with_params(self):
- dcr_from_config.register()
- ClientHandler.__init__.assert_called_once_with(
- cfg.ISSUER, cfg.REDIRECT_URIS, {
- 'scope': cfg.SCOPE.split(" "),
- "post_logout_redirect_uris": ['https://localhost:9090']
- }
- )
-
- def test_register_should_call_open(self):
- with patch('builtins.open', mock_open()) as open_mock:
- dcr_from_config.register()
-
- open_mock.assert_called_once()
-
- def test_register_should_call_open_with_correct_params(self):
- with patch('builtins.open', mock_open()) as open_mock:
- dcr_from_config.register()
- open_mock.assert_called_once_with('client_info.json', 'w')
-
- def test_register_should_call_write_with_client_info(self):
- client = ClientHandler(cfg.ISSUER, cfg.REDIRECT_URIS, {})
- expected_json_client_info = json.dumps(client.get_client_dict(), indent=4)
- with patch('builtins.open', mock_open()) as open_mock:
- dcr_from_config.register()
- open_mock_handler = open_mock()
- open_mock_handler.write.assert_called_once_with(expected_json_client_info)
-
-
diff --git a/demos/jans-tent/tests/unit_integration/test_dynamic_client_registration.py b/demos/jans-tent/tests/unit_integration/test_dynamic_client_registration.py
deleted file mode 100644
index 0f51b7cb596..00000000000
--- a/demos/jans-tent/tests/unit_integration/test_dynamic_client_registration.py
+++ /dev/null
@@ -1,277 +0,0 @@
-from unittest import TestCase
-from unittest.mock import MagicMock
-import inspect
-
-import clientapp.helpers.client_handler as client_handler
-from typing import Optional
-import helper
-from oic.oauth2 import ASConfigurationResponse
-
-
-ClientHandler = client_handler.ClientHandler
-
-# helper
-def get_class_instance(op_url='https://t1.techno24x7.com',
- client_url='https://mock.test.com',
- additional_metadata={}):
- client_handler_obj = ClientHandler(op_url, client_url, additional_metadata)
- return client_handler_obj
-
-
-class TestDynamicClientRegistration(TestCase):
-
- def setUp(self) -> None:
- self.register_client_stash = ClientHandler.register_client
- self.discover_stash = ClientHandler.discover
-
- @staticmethod
- def mock_methods():
- ClientHandler.register_client = MagicMock(name='register_client')
- ClientHandler.register_client.return_value = helper.REGISTER_CLIENT_RESPONSE
- ClientHandler.discover = MagicMock(name='discover')
- ClientHandler.discover.return_value = helper.OP_DATA_DICT_RESPONSE
-
- def restore_stashed_mocks(self):
- ClientHandler.discover = self.discover_stash
- ClientHandler.register_client = self.register_client_stash
-
- def test_if_json_exists(self):
- self.assertTrue(hasattr(client_handler, 'json'),
- 'json does not exists in client_handler')
-
- def test_if_json_is_from_json_package(self):
- self.assertTrue(client_handler.json.__package__ == 'json',
- 'json is not from json')
-
- # testing ClientHandler class
- def test_if_ClientHandler_is_class(self):
- self.assertTrue(inspect.isclass(ClientHandler))
-
- def test_if_register_client_exists(self):
- self.assertTrue(hasattr(ClientHandler, 'register_client'),
- 'register_client does not exists in ClientHandler')
-
- def test_if_register_client_is_callable(self):
- self.assertTrue(callable(ClientHandler.register_client),
- 'register_client is not callable')
-
- def test_if_register_client_receives_params(self):
- expected_args = ['self', 'op_data', 'redirect_uris']
- self.assertTrue(
- inspect.getfullargspec(
- ClientHandler.register_client).args == expected_args,
- 'register_client does not receive expected args')
-
- def test_if_register_client_params_are_expected_type(self):
- insp = inspect.getfullargspec(ClientHandler.register_client)
- self.assertTrue(
- insp.annotations['op_data'] == ASConfigurationResponse
- and insp.annotations['redirect_uris'] == Optional[list[str]],
- 'register_client is not receiving the right params')
-
- def test_if_class_has_initial_expected_attrs(self):
- initial_expected_attrs = [
- '_ClientHandler__client_id',
- '_ClientHandler__client_secret',
- '_ClientHandler__redirect_uris',
- '_ClientHandler__metadata_url',
- '_ClientHandler__additional_metadata',
- 'discover', # method
- 'register_client' # method
- ]
-
- self.assertTrue(
- all(attr in ClientHandler.__dict__.keys()
- for attr in initial_expected_attrs),
- 'ClientHandler does not have initial attrs')
-
- def test_if_discover_exists(self):
- self.assertTrue(hasattr(ClientHandler, 'discover'),
- 'discover does not exists in ClientHandler')
-
- def test_if_discover_is_callable(self):
- self.assertTrue(callable(ClientHandler.discover),
- 'discover is not callable')
-
- def test_if_discover_receives_params(self):
- expected_args = ['self', 'op_url']
- self.assertTrue(
- inspect.getfullargspec(
- ClientHandler.discover).args == expected_args,
- 'discover does not receive expected args')
-
- def test_if_discover_params_are_expected_type(self):
- insp = inspect.getfullargspec(ClientHandler.discover)
- self.assertTrue(
- insp.annotations['op_url'] == Optional[str],
- 'discover is not receiving the right params')
-
- def test_discover_should_return_valid_dict(self):
- """[Checks if returns main keys]
- """
-
- main_keys = {
- 'issuer', 'authorization_endpoint', 'token_endpoint',
- 'userinfo_endpoint', 'clientinfo_endpoint',
- 'session_revocation_endpoint', 'end_session_endpoint',
- 'revocation_endpoint', 'registration_endpoint'
- }
-
- self.mock_methods()
- op_data = ClientHandler.discover(ClientHandler,
- 'https://t1.techno24x7.com')
- self.assertTrue(main_keys <= set(op_data),
- 'discovery return data does not have main keys')
- self.restore_stashed_mocks()
-
- def test_if_get_client_dict_exists(self):
- self.assertTrue(hasattr(ClientHandler, 'get_client_dict'),
- 'get_client_dict does not exists in ClientHandler')
-
- def test_if_get_client_dict_is_callable(self):
- self.assertTrue(callable(ClientHandler.get_client_dict),
- 'get_client_dict is not callable')
-
- def test_if_get_client_dict_receives_params(self):
- expected_args = ['self']
- self.assertTrue(
- inspect.getfullargspec(
- ClientHandler.get_client_dict).args == expected_args,
- 'get_client_dict does not receive expected args')
-
- def test_client_id_should_return_something(self):
- self.assertIsNotNone(
- ClientHandler.get_client_dict(ClientHandler),
- 'get_client_dict returning NoneType. It has to return something!')
-
- def test_get_client_dict_should_return_a_dict(self):
- self.assertIsInstance(ClientHandler.get_client_dict(ClientHandler),
- dict, 'get_client_dict is not returning a dict')
-
- def test_class_init_should_set_op_url(self):
- self.mock_methods()
-
- op_url = 'https://t1.techno24x7.com'
-
- client_handler_obj = get_class_instance(op_url)
-
- self.restore_stashed_mocks()
-
- self.assertEqual(client_handler_obj.__dict__['_ClientHandler__op_url'],
- op_url)
-
- def test_class_init_should_set_redirect_uris(self):
- self.mock_methods()
-
- op_url = 'https://t1.techno24x7.com'
- redirect_uris = 'https://mock.test.com/oidc_callback'
- client_handler_obj = ClientHandler(op_url, redirect_uris, {})
-
- self.restore_stashed_mocks()
-
- self.assertEqual(
- client_handler_obj.__dict__['_ClientHandler__redirect_uris'],
- redirect_uris)
-
- def test_class_init_should_set_metadata_url(self):
- self.mock_methods()
-
- op_url = 'https://t1.techno24x7.com'
-
- client_handler_obj = get_class_instance(op_url)
-
- self.restore_stashed_mocks()
-
- expected_metadata_url = op_url + '/.well-known/openid-configuration'
-
- self.assertEqual(
- client_handler_obj.__dict__['_ClientHandler__metadata_url'],
- expected_metadata_url)
-
- def test_class_init_should_set_additional_params(self):
- self.mock_methods()
- expected_metadata = {'metakey1': 'meta value 1'}
- client_handler_obj = get_class_instance(additional_metadata=expected_metadata)
- self.restore_stashed_mocks()
-
- self.assertEqual(
- client_handler_obj.__dict__['_ClientHandler__additional_metadata'],
- expected_metadata
- )
-
- def test_class_init_should_have_docstring(self):
- self.assertTrue(ClientHandler.__init__.__doc__,
- 'ClientHandler.__init__ has doc')
-
- def test_if_get_client_dict_return_expected_keys(self):
- expected_keys = [
- 'op_metadata_url',
- 'client_id',
- 'client_secret',
- ]
-
- self.mock_methods()
-
- client_handler_obj = get_class_instance()
- client_dict = client_handler_obj.get_client_dict()
-
- self.restore_stashed_mocks()
-
- self.assertTrue(
- all(key in client_dict.keys() for key in expected_keys),
- 'there is no %s IN %s: get_client_dict is NOT returning expected keys'
- % (str(expected_keys), str(client_dict.keys())))
-
- def test_get_client_dict_values_cannot_be_none(self):
- self.mock_methods()
-
- op_url = 'https://t1.techno24x7.com'
- client_handler_obj = get_class_instance(op_url)
- client_dict = client_handler_obj.get_client_dict()
-
- self.restore_stashed_mocks()
-
- for key in client_dict.keys():
- self.assertIsNotNone(client_dict[key],
- 'get_client_dict[%s] cannot be None!' % key)
-
- def test_get_client_dict_should_return_url_metadata_value(self):
- self.mock_methods()
-
- client_handler_obj = get_class_instance()
-
- self.restore_stashed_mocks()
-
- self.assertEqual(
- client_handler_obj.get_client_dict()['op_metadata_url'],
- client_handler_obj._ClientHandler__metadata_url)
-
- def test_get_client_dict_should_return_client_id_value(self):
- self.mock_methods()
-
- client_handler_obj = get_class_instance()
-
- self.restore_stashed_mocks()
-
- self.assertEqual(
- client_handler_obj.get_client_dict()['client_id'],
- client_handler_obj._ClientHandler__client_id
- )
-
- def test_init_should_call_discover_once(self):
- self.mock_methods()
-
- get_class_instance()
-
- ClientHandler.discover.assert_called_once()
-
- self.restore_stashed_mocks()
-
- def test_init_should_call_register_client_once(self):
- self.mock_methods()
-
- get_class_instance()
- ClientHandler.register_client.assert_called_once()
-
- self.restore_stashed_mocks()
-
diff --git a/demos/jans-tent/tests/unit_integration/test_flask_factory.py b/demos/jans-tent/tests/unit_integration/test_flask_factory.py
deleted file mode 100644
index 0813c3b1ad1..00000000000
--- a/demos/jans-tent/tests/unit_integration/test_flask_factory.py
+++ /dev/null
@@ -1,93 +0,0 @@
-from unittest import TestCase
-from unittest.mock import MagicMock
-import clientapp
-from flask import Flask
-import os
-import builtins
-from clientapp.helpers.client_handler import ClientHandler
-import helper
-
-
-class TestFlaskApp(TestCase):
-
- def setUp(self) -> None:
- self.stashed_add_config_from_json = clientapp.add_config_from_json
- clientapp.cfg.CLIENT_ID = 'any-client-id-stub'
- clientapp.cfg.CLIENT_SECRET = 'any-client-secret-stub'
- clientapp.cfg.SERVER_META_URL = 'https://ophostname.com/server/meta/url'
- clientapp.add_config_from_json = MagicMock(name='add_config_from_json')
- clientapp.add_config_from_json.return_value(None)
- self.stashed_discover = ClientHandler.discover
- self.stashed_register_client = ClientHandler.register_client
- self.stashed_open = builtins.open
- builtins.open = MagicMock(name='open')
- ClientHandler.discover = MagicMock(name='discover')
- ClientHandler.discover.return_value = helper.OP_DATA_DICT_RESPONSE
- ClientHandler.register_client = MagicMock(name='register_client')
- ClientHandler.register_client.return_value = helper.REGISTER_CLIENT_RESPONSE
-
- def tearDown(self) -> None:
- ClientHandler.discover = self.stashed_discover
- ClientHandler.register_client = self.stashed_register_client
- builtins.open = self.stashed_open
- clientapp.add_config_from_json = self.stashed_add_config_from_json
-
- def test_create_app_should_exist(self):
- self.assertEqual(hasattr(clientapp, 'create_app'), True,
- 'app factory does not exists')
-
- def test_create_app_should_be_invokable(self):
- self.assertEqual(callable(clientapp.create_app), True,
- 'cannot invoke create_app from clientapp')
-
- def test_create_app_should_return_a_flask_app(self):
-
- self.assertIsInstance(clientapp.create_app(), Flask,
- 'create_app is not returning a Flask instance')
-
- def test_if_app_has_secret_key(self):
- self.assertTrue(hasattr(clientapp.create_app(), 'secret_key'), )
-
- def test_if_secret_key_not_none(self):
- self.assertIsNotNone(clientapp.create_app().secret_key,
- 'app secret key is unexpectedly None')
-
- def test_if_oauth_is_app_extension(self):
- self.assertTrue('authlib.integrations.flask_client' in
- clientapp.create_app().extensions)
-
- def test_if_settings_py_exists(self):
- self.assertTrue(os.path.exists('clientapp/config.py'),
- 'File clientapp/config.py does not exist')
-
- def test_if_op_client_id_exists_in_app_configuration(self):
- self.assertTrue('OP_CLIENT_ID' in clientapp.create_app().config,
- 'No OP_CLIENT_ID in app.config')
-
- def test_if_clientapp_has_cfg(self):
- self.assertTrue(hasattr(clientapp, 'cfg'))
-
- def test_if_cfg_is_module_from_configpy(self):
- self.assertTrue(
- os.path.relpath(clientapp.cfg.__file__) == 'clientapp/config.py')
-
- ...
-
- def test_if_OP_CLIENT_ID_is_equal_cfg_CLIENT_ID(self):
- self.assertEqual(clientapp.create_app().config['OP_CLIENT_ID'],
- clientapp.cfg.CLIENT_ID)
-
- def test_if_OP_CLIENT_SECRET_exists_in_app_configuration(self):
- self.assertTrue('OP_CLIENT_SECRET' in clientapp.create_app().config,
- 'No OP_CLIENT_SECRET in app.config')
-
- def test_if_OP_CLIENT_SECRET_is_equal_cfg_CLIENT_ID(self):
- self.assertEqual(clientapp.create_app().config['OP_CLIENT_SECRET'],
- clientapp.cfg.CLIENT_SECRET)
-
- def test_if_has_attr_ssl_verify(self):
- self.assertTrue(hasattr(clientapp, 'ssl_verify'),
- 'There is no ssl_verify in clientapp')
-
- def test_should_have_method_to_set_CA_CURL_CERT(self):
- self.assertTrue(clientapp.ssl_verify.__call__)
diff --git a/demos/jans-tent/tests/unit_integration/test_gluu_preselected_provider.py b/demos/jans-tent/tests/unit_integration/test_gluu_preselected_provider.py
deleted file mode 100644
index 4f7fc9dc13f..00000000000
--- a/demos/jans-tent/tests/unit_integration/test_gluu_preselected_provider.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import clientapp
-from helper import FlaskBaseTestCase
-
-
-class TestPreselectedProvider(FlaskBaseTestCase):
-
- # """
- # We should be able to send Preselected passport provider to gluu OIDC as a authorization param
- # like this: preselectedExternalProvider=
- # Where is the Base64-encoded representation of a small JSON
- # content that looking like this:
- # { "provider" : }
- # """
- def setUp(self):
- clientapp.cfg.PRE_SELECTED_PROVIDER = True
- FlaskBaseTestCase.setUp(FlaskBaseTestCase)
-
- def test_config_should_have_preselected_provider_option(self):
- self.assertTrue(hasattr(clientapp.cfg, 'PRE_SELECTED_PROVIDER'),
- 'cfg doesnt have PRE_SELECTED_PROVIDER attribute')
-
- def test_config_pre_selected_provider_should_be_boolean(self):
- self.assertTrue(
- type(clientapp.cfg.PRE_SELECTED_PROVIDER) == bool,
- 'cfg.PRE_SELECTED_PROVIDER is not bool')
-
- def test_preselected_provider_id_should_exist_in_cfg(self):
- self.assertTrue(hasattr(clientapp.cfg, 'PRE_SELECTED_PROVIDER_ID'))
-
- def test_clientapp_should_have_get_preselected_provider(self):
- self.assertTrue(
- hasattr(clientapp, 'get_preselected_provider'),
- 'client app does not have get_preselected_provider attr')
-
- def test_get_preselected_provider_should_be_callable(self):
- self.assertTrue(callable(clientapp.get_preselected_provider),
- 'get_preselected_provider is not callable')
-
- def test_get_selected_provider_should_return_base64(self):
-
- clientapp.cfg.PRE_SELECTED_PROVIDER_ID = 'saml-emaillink'
- expected_response = "eyAicHJvdmlkZXIiIDogInNhbWwtZW1haWxsaW5rIiB9"
- self.assertEqual(clientapp.get_preselected_provider(),
- expected_response)
-
-
diff --git a/demos/jans-tent/tests/unit_integration/test_logout_endpoint.py b/demos/jans-tent/tests/unit_integration/test_logout_endpoint.py
deleted file mode 100644
index c0bb85bfc87..00000000000
--- a/demos/jans-tent/tests/unit_integration/test_logout_endpoint.py
+++ /dev/null
@@ -1,65 +0,0 @@
-import clientapp
-from helper import FlaskBaseTestCase, app_endpoints
-from flask import url_for, session
-from urllib import parse
-from clientapp import config as cfg
-
-class TestLogoutEndpoint(FlaskBaseTestCase):
- def authenticated_session_mock(self):
- with self.client.session_transaction() as session:
- session['id_token'] = 'id_token_stub'
-
- def test_endpoint_exists(self):
- self.assertIn(
- 'logout',
- app_endpoints(clientapp.create_app())
- )
-
- def test_endpoint_should_require_authentication(self):
- ...
- def test_logout_endpoint_should_redirect_to_home_if_unauthenticated(self):
- # print(self.client.get(url_for('logout')).response)
- response = self.client.get(url_for('logout'))
- assert(response.status_code == 302)
- assert(response.location == url_for('index'))
-
-
- def test_logout_endpoint_should_clear_session(self):
- with self.client.session_transaction() as sess:
- sess['id_token'] = 'id_token_stub'
- sess['user'] = 'userinfo stub'
-
- with self.client:
- self.client.get(url_for('logout'))
- assert 'id_token' not in session
- assert 'user' not in session
-
- def test_endpoint_should_redirect_to_end_session_endpoint(self):
- with self.client.session_transaction() as session:
- session['id_token'] = 'id_token_stub'
- session['user'] = 'userinfo stub'
-
- response = self.client.get(url_for('logout'))
-
- parsed_location = parse.urlparse(response.location)
- assert parsed_location.scheme == 'https'
- assert parsed_location.netloc == 'ophostname.com'
- assert parsed_location.path == '/end_session_endpoint'
-
-
-
- def test_endpoint_should_redirect_to_end_session_endpoint_with_params(self):
- token_stub = 'id_token_stub'
- with self.client.session_transaction() as session:
- session['id_token'] = token_stub
- session['user'] = 'userinfo stub'
-
- parsed_redirect_uri = parse.urlparse(cfg.REDIRECT_URIS[0])
- post_logout_uri = '%s://%s' % (parsed_redirect_uri.scheme, parsed_redirect_uri.netloc)
-
- expected_query = 'post_logout_redirect_uri=%s&token_hint=%s' % (post_logout_uri, token_stub)
- response = self.client.get(url_for('logout'))
-
- parsed_location = parse.urlparse(response.location)
- assert parsed_location.query == expected_query
-
diff --git a/demos/jans-tent/tests/unit_integration/test_protected_content_endpoint.py b/demos/jans-tent/tests/unit_integration/test_protected_content_endpoint.py
deleted file mode 100644
index 68f7012c88e..00000000000
--- a/demos/jans-tent/tests/unit_integration/test_protected_content_endpoint.py
+++ /dev/null
@@ -1,68 +0,0 @@
-from clientapp import create_app, session
-from flask import Flask, url_for
-from typing import List
-from werkzeug import local
-from helper import FlaskBaseTestCase
-
-
-def app_endpoint(app: Flask) -> List[str]:
- """ Return all enpoints in app """
-
- endpoints = []
- for item in app.url_map.iter_rules():
- endpoint = item.endpoint.replace("_", "-")
- endpoints.append(endpoint)
- return endpoints
-
-
-class TestProtectedContentEndpoint(FlaskBaseTestCase):
- def test_app_should_contain_protected_content_route(self):
-
- endpoints = app_endpoint(create_app())
- self.assertIn('protected-content', endpoints,
- 'protected-content route not found in app endpoints')
-
- def test_app_protected_content_route_should_return_valid_requisition(self):
-
- self.client.get(url_for('protected_content'))
-
- self.assertIn(
- self.client.get(url_for('protected_content')).status_code,
- range(100, 511),
- 'protected content route returned invalid requisition')
-
- def test_should_return_if_session_exists_in_clientapp(self):
- import clientapp
- self.assertTrue(hasattr(clientapp, 'session'),
- "session is not an attribute of clientapp")
- del clientapp
-
- def test_should_check_if_session_is_LocalProxy_instance(self):
- self.assertIsInstance(session, local.LocalProxy)
-
- def test_protected_content_return_status_200_ir_session_profile_exists(
- self):
-
- with self.client.session_transaction() as sess:
- sess['user'] = 'foo'
-
- self.assertEqual(
- self.client.get(url_for('protected_content')).status_code, 200)
-
- def test_should_return_302_if_no_session_profile(self):
- self.assertEqual(
- self.client.get(url_for('protected_content')).status_code, 302)
-
- def test_protected_content_should_redirect_to_login_if_session_profile_doesnt_exist(
- self):
-
- response = self.client.get(url_for('protected_content'))
- self.assertTrue(response.location.endswith(url_for('login')),
- 'Protected page is not redirecting to login page')
-
- ''' TODO
- def test_should_return_if_user_logged_in_exists(self):
- self.assertTrue(
- hasattr(app,'user_logged_in')
- )
- '''
diff --git a/docker-jans-all-in-one/Dockerfile b/docker-jans-all-in-one/Dockerfile
index 6591e7a7424..3f7383b4397 100644
--- a/docker-jans-all-in-one/Dockerfile
+++ b/docker-jans-all-in-one/Dockerfile
@@ -3,7 +3,7 @@
# ==============
# original Janssen base version
-ARG BASE_VERSION=0.0.0-nightly
+ARG BASE_VERSION=1.3.0-1
# the following ARGs set default base images
# they can be overriden in build process via --build-arg option
@@ -58,7 +58,7 @@ RUN apk update \
# Assets sync
# ===========
-ENV JANS_SOURCE_VERSION=aa1b2edaa8d7e3413bd57a7bd7cc86206086768b
+ENV JANS_SOURCE_VERSION=040ff17942019bc10433ce17d819b8d8474f13c8
# note that as we're pulling from a monorepo (with multiple project in it)
# we are using partial-clone and sparse-checkout to get the assets
@@ -178,7 +178,7 @@ RUN mkdir -p /opt/jans/configurator/db \
COPY app /app
# CN version as env var (with suffix if any, i.e. SNAPSHOT)
-ENV CN_VERSION=0.0.0-nightly
+ENV CN_VERSION=1.3.0
# set directory contains installer code that will be added to Python sys.path
ENV PYTHONPATH=/app
@@ -235,7 +235,7 @@ ENV JETTY_BASE=/opt/jans/jetty \
LABEL org.opencontainers.image.url="ghcr.io/janssenproject/jans/all-in-one" \
org.opencontainers.image.authors="Janssen Project " \
org.opencontainers.image.vendor="Janssen Project" \
- org.opencontainers.image.version="0.0.0-nightly" \
+ org.opencontainers.image.version="1.3.0-1" \
org.opencontainers.image.title="Janssen All-in-One" \
org.opencontainers.image.description=""
diff --git a/docker-jans-all-in-one/app/templates/nginx/jans-auth-location.conf b/docker-jans-all-in-one/app/templates/nginx/jans-auth-location.conf
index 6b2fd017c11..04e8cd67693 100644
--- a/docker-jans-all-in-one/app/templates/nginx/jans-auth-location.conf
+++ b/docker-jans-all-in-one/app/templates/nginx/jans-auth-location.conf
@@ -1,3 +1,28 @@
+location /.well-known/authzen-configuration {
+ proxy_pass http://jans_auth_backend/jans-auth/restv1/authzen-configuration;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-Host $http_host;
+ proxy_set_header X-Forwarded-For $remote_addr;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header X-Forwarded-Scheme $scheme;
+ proxy_set_header X-Forwarded-Port "";
+ proxy_set_header X-Scheme $scheme;
+ proxy_set_header X-Original-Forwarded-For $http_x_forwarded_for;
+ proxy_set_header Proxy "";
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
+ proxy_connect_timeout 300s;
+ proxy_send_timeout 300;
+ proxy_read_timeout 300;
+ send_timeout 300;
+
+ proxy_redirect off;
+ port_in_redirect off;
+ proxy_http_version 1.1;
+}
+
location /.well-known/openid-configuration {
proxy_pass http://jans_auth_backend/jans-auth/.well-known/openid-configuration;
proxy_set_header Host $http_host;
diff --git a/docker-jans-all-in-one/app/templates/nginx/jans-fido2-location.conf b/docker-jans-all-in-one/app/templates/nginx/jans-fido2-location.conf
index 2abf9b8295f..67edc8d3cf2 100644
--- a/docker-jans-all-in-one/app/templates/nginx/jans-fido2-location.conf
+++ b/docker-jans-all-in-one/app/templates/nginx/jans-fido2-location.conf
@@ -1,3 +1,28 @@
+location /.well-known/webauthn {
+ proxy_pass http://jans_fido2_backend/jans-fido2/restv1/webauthn/configuration;
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-Host $http_host;
+ proxy_set_header X-Forwarded-For $remote_addr;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header X-Forwarded-Scheme $scheme;
+ proxy_set_header X-Forwarded-Port "";
+ proxy_set_header X-Scheme $scheme;
+ proxy_set_header X-Original-Forwarded-For $http_x_forwarded_for;
+ proxy_set_header Proxy "";
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
+ proxy_connect_timeout 300s;
+ proxy_send_timeout 300;
+ proxy_read_timeout 300;
+ send_timeout 300;
+
+ proxy_redirect off;
+ port_in_redirect off;
+ proxy_http_version 1.1;
+}
+
location /.well-known/fido2-configuration {
proxy_pass http://jans_fido2_backend/jans-fido2/restv1/configuration;
proxy_set_header Host $http_host;
diff --git a/docker-jans-auth-server/Dockerfile b/docker-jans-auth-server/Dockerfile
index 0e35b582bef..b95f8b559b6 100644
--- a/docker-jans-auth-server/Dockerfile
+++ b/docker-jans-auth-server/Dockerfile
@@ -50,8 +50,8 @@ RUN /opt/jython/bin/pip uninstall -y pip setuptools
# Auth server
# ===========
-ENV CN_VERSION=0.0.0-nightly
-ENV CN_BUILD_DATE='2024-12-20 08:35'
+ENV CN_VERSION=1.3.0
+ENV CN_BUILD_DATE='2025-01-13 16:11'
ENV CN_SOURCE_URL=https://jenkins.jans.io/maven/io/jans/jans-auth-server/${CN_VERSION}/jans-auth-server-${CN_VERSION}.war
@@ -94,7 +94,7 @@ RUN mkdir -p ${JETTY_BASE}/jans-auth/agama/fl \
/app/static/rdbm \
/app/schema
-ENV JANS_SOURCE_VERSION=aa1b2edaa8d7e3413bd57a7bd7cc86206086768b
+ENV JANS_SOURCE_VERSION=040ff17942019bc10433ce17d819b8d8474f13c8
ARG JANS_SETUP_DIR=jans-linux-setup/jans_setup
# note that as we're pulling from a monorepo (with multiple project in it)
@@ -245,7 +245,7 @@ EXPOSE $CN_AUTH_JETTY_PORT
LABEL org.opencontainers.image.url="ghcr.io/janssenproject/jans/auth-server" \
org.opencontainers.image.authors="Janssen Project " \
org.opencontainers.image.vendor="Janssen Project" \
- org.opencontainers.image.version="0.0.0-nightly" \
+ org.opencontainers.image.version="1.3.0-1" \
org.opencontainers.image.title="Janssen Authorization Server" \
org.opencontainers.image.description="OAuth 2.0 server and client; OpenID Connect Provider (OP) & UMA Authorization Server (AS)"
@@ -290,7 +290,8 @@ RUN chmod -R g=u ${JETTY_BASE}/jans-auth/custom \
&& chown -R 1000:0 /opt/prometheus \
&& chown 1000:0 ${JETTY_BASE}/jans-auth/webapps/jans-auth.xml \
&& chown -R 1000:0 ${JETTY_HOME}/temp \
- && chown -R 1000:0 ${JETTY_BASE}/jans-auth/_libs
+ && chown -R 1000:0 ${JETTY_BASE}/jans-auth/_libs \
+ && chown -R 1000:0 /app/templates
USER 1000
diff --git a/docker-jans-auth-server/scripts/lock.py b/docker-jans-auth-server/scripts/lock.py
index 637b17cb696..3a7a6f5b384 100644
--- a/docker-jans-auth-server/scripts/lock.py
+++ b/docker-jans-auth-server/scripts/lock.py
@@ -6,6 +6,8 @@
from string import Template
from uuid import uuid4
+from ldif import LDIFWriter
+
from jans.pycloudlib import get_manager
from jans.pycloudlib.persistence.sql import SqlClient
from jans.pycloudlib.persistence.utils import PersistenceMapper
@@ -155,12 +157,42 @@ def ctx(self) -> dict[str, _t.Any]:
@cached_property
def ldif_files(self) -> list[str]:
- return [
- "/app/templates/jans-lock/config.ldif",
- "/app/templates/jans-lock/clients.ldif",
- ]
+ filenames = ["config.ldif", "clients.ldif"]
+
+ # generate extra scopes
+ self.generate_scopes_ldif()
+ filenames.append("scopes.ldif")
+
+ return [f"/app/templates/jans-lock/{filename}" for filename in filenames]
def import_ldif_files(self) -> None:
for file_ in self.ldif_files:
logger.info(f"Importing {file_}")
self.client.create_from_ldif(file_, self.ctx)
+
+ def generate_scopes_ldif(self):
+ # prepare required scopes (if any)
+ with open("/app/templates/jans-lock/scopes.json") as f:
+ scopes = json.loads(f.read())
+
+ with open("/app/templates/jans-lock/scopes.ldif", "wb") as fd:
+ writer = LDIFWriter(fd, cols=1000)
+
+ for scope in scopes:
+ writer.unparse(
+ f"inum={scope['inum']},ou=scopes,o=jans",
+ {
+ "objectClass": ["top", "jansScope"],
+ "description": [scope["description"]],
+ "displayName": [scope["displayName"]],
+ "inum": [scope["inum"]],
+ "jansDefScope": [str(scope["jansDefScope"]).lower()],
+ "jansId": [scope["jansId"]],
+ "jansScopeTyp": [scope["jansScopeTyp"]],
+ "jansAttrs": [json.dumps({
+ "spontaneousClientId": None,
+ "spontaneousClientScopes": [],
+ "showInConfigurationEndpoint": False,
+ })],
+ },
+ )
diff --git a/docker-jans-auth-server/scripts/upgrade.py b/docker-jans-auth-server/scripts/upgrade.py
index 70f9b9d889d..bf8080f62e3 100644
--- a/docker-jans-auth-server/scripts/upgrade.py
+++ b/docker-jans-auth-server/scripts/upgrade.py
@@ -62,6 +62,7 @@ def _transform_lock_dynamic_config(conf, manager):
],
}),
("groupScopeEnabled", True),
+ ("statEnabled", True),
]:
if missing_key not in conf:
conf[missing_key] = value
@@ -165,6 +166,8 @@ def invoke(self):
if as_boolean(os.environ.get("CN_LOCK_ENABLED", "false")):
self.update_lock_dynamic_config()
self.update_lock_client_scopes()
+ self.update_lock_error_config()
+ self.update_lock_static_config()
def update_lock_dynamic_config(self):
kwargs = {"table_name": "jansAppConf"}
@@ -235,6 +238,46 @@ def update_lock_client_scopes(self):
entry.attrs["jansScope"] = client_scopes + diff
self.backend.modify_entry(entry.id, entry.attrs, **kwargs)
+ def update_lock_error_config(self):
+ kwargs = {"table_name": "jansAppConf"}
+ id_ = doc_id_from_dn("ou=jans-lock,ou=configuration,o=jans")
+
+ entry = self.backend.get_entry(id_, **kwargs)
+
+ if not entry:
+ return
+
+ with contextlib.suppress(json.decoder.JSONDecodeError):
+ entry.attrs["jansConfErrors"] = json.loads(entry.attrs["jansConfErrors"])
+
+ with open("/app/templates/jans-lock/errors.json") as f:
+ conf = json.loads(f.read())
+
+ if conf != entry.attrs["jansConfErrors"]:
+ entry.attrs["jansConfErrors"] = json.dumps(conf)
+ entry.attrs["jansRevision"] += 1
+ self.backend.modify_entry(entry.id, entry.attrs, **kwargs)
+
+ def update_lock_static_config(self):
+ kwargs = {"table_name": "jansAppConf"}
+ id_ = doc_id_from_dn("ou=jans-lock,ou=configuration,o=jans")
+
+ entry = self.backend.get_entry(id_, **kwargs)
+
+ if not entry:
+ return
+
+ with contextlib.suppress(json.decoder.JSONDecodeError):
+ entry.attrs["jansConfStatic"] = json.loads(entry.attrs["jansConfStatic"])
+
+ with open("/app/templates/jans-lock/static-conf.json") as f:
+ conf = json.loads(f.read())
+
+ if conf != entry.attrs["jansConfStatic"]:
+ entry.attrs["jansConfStatic"] = json.dumps(conf)
+ entry.attrs["jansRevision"] += 1
+ self.backend.modify_entry(entry.id, entry.attrs, **kwargs)
+
def main(): # noqa: D103
manager = get_manager()
diff --git a/docker-jans-casa/Dockerfile b/docker-jans-casa/Dockerfile
index 9f4beb98f2d..199601cd6e2 100644
--- a/docker-jans-casa/Dockerfile
+++ b/docker-jans-casa/Dockerfile
@@ -29,7 +29,7 @@ RUN wget -q https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-home/${JETTY_
# Casa
# ====
-ENV CN_VERSION=0.0.0-nightly
+ENV CN_VERSION=1.3.0
ENV CN_BUILD_DATE='2024-12-20 09:16'
ENV CN_SOURCE_URL=https://jenkins.jans.io/maven/io/jans/casa/${CN_VERSION}/casa-${CN_VERSION}.war
@@ -60,7 +60,7 @@ RUN mkdir -p /usr/share/java \
# Assets sync
# ===========
-ENV JANS_SOURCE_VERSION=aa1b2edaa8d7e3413bd57a7bd7cc86206086768b
+ENV JANS_SOURCE_VERSION=040ff17942019bc10433ce17d819b8d8474f13c8
ARG JANS_SETUP_DIR=jans-linux-setup/jans_setup
# note that as we're pulling from a monorepo (with multiple project in it)
@@ -206,7 +206,7 @@ EXPOSE $CN_CASA_JETTY_PORT
LABEL org.opencontainers.image.url="ghcr.io/janssenproject/jans/casa" \
org.opencontainers.image.authors="Janssen Project " \
org.opencontainers.image.vendor="Janssen Project" \
- org.opencontainers.image.version="0.0.0-nightly" \
+ org.opencontainers.image.version="1.3.0-1" \
org.opencontainers.image.title="Janssen Casa" \
org.opencontainers.image.description="Self-service portal for people to manage their account security preferences in the Janssen, like 2FA"
diff --git a/docker-jans-certmanager/Dockerfile b/docker-jans-certmanager/Dockerfile
index d14f8d2f8b8..fefb30cda55 100644
--- a/docker-jans-certmanager/Dockerfile
+++ b/docker-jans-certmanager/Dockerfile
@@ -14,7 +14,7 @@ RUN apk update \
# ===========
# JAR files required to generate OpenID Connect keys
-ENV CN_VERSION=0.0.0-nightly
+ENV CN_VERSION=1.3.0
ENV CN_BUILD_DATE='2024-12-20 08:29'
ENV CN_SOURCE_URL=https://jenkins.jans.io/maven/io/jans/jans-auth-client/${CN_VERSION}/jans-auth-client-${CN_VERSION}-jar-with-dependencies.jar
@@ -25,7 +25,7 @@ RUN wget -q ${CN_SOURCE_URL} -P /app/javalibs/
# Assets sync
# ===========
-ENV JANS_SOURCE_VERSION=aa1b2edaa8d7e3413bd57a7bd7cc86206086768b
+ENV JANS_SOURCE_VERSION=040ff17942019bc10433ce17d819b8d8474f13c8
# note that as we're pulling from a monorepo (with multiple project in it)
# we are using partial-clone and sparse-checkout to get the assets
@@ -130,7 +130,7 @@ ENV CN_WAIT_MAX_TIME=300 \
LABEL org.opencontainers.image.url="ghcr.io/janssenproject/jans/certmanager" \
org.opencontainers.image.authors="Janssen Project " \
org.opencontainers.image.vendor="Janssen Project" \
- org.opencontainers.image.version="0.0.0-nightly" \
+ org.opencontainers.image.version="1.3.0-1" \
org.opencontainers.image.title="Janssen Certmanager" \
org.opencontainers.image.description="Manage certs and crypto keys for Janssen Server"
diff --git a/docker-jans-certmanager/README.md b/docker-jans-certmanager/README.md
index adb12653d6a..f835942ca00 100644
--- a/docker-jans-certmanager/README.md
+++ b/docker-jans-certmanager/README.md
@@ -186,7 +186,7 @@ spec:
spec:
containers:
- name: auth-key-rotation
- image: ghcr.io/janssenproject/jans/certmanager:0.0.0-nightly
+ image: ghcr.io/janssenproject/jans/certmanager:1.3.0-1
resources:
requests:
memory: "300Mi"
diff --git a/docker-jans-config-api/Dockerfile b/docker-jans-config-api/Dockerfile
index ece0aef0c42..b3c075f2ba8 100644
--- a/docker-jans-config-api/Dockerfile
+++ b/docker-jans-config-api/Dockerfile
@@ -40,7 +40,7 @@ RUN wget -q https://maven.jans.io/maven/io/jans/jython-installer/${JYTHON_VERSIO
# Config API
# ==========
-ENV CN_VERSION=0.0.0-nightly
+ENV CN_VERSION=1.3.0
ENV CN_BUILD_DATE='2024-12-20 09:07'
ENV CN_SOURCE_URL=https://jenkins.jans.io/maven/io/jans/jans-config-api-server/${CN_VERSION}/jans-config-api-server-${CN_VERSION}.war
@@ -70,7 +70,7 @@ RUN mkdir -p ${JETTY_BASE}/jans-config-api/_plugins \
# Assets sync
# ===========
-ENV JANS_SOURCE_VERSION=aa1b2edaa8d7e3413bd57a7bd7cc86206086768b
+ENV JANS_SOURCE_VERSION=040ff17942019bc10433ce17d819b8d8474f13c8
ARG JANS_SETUP_DIR=jans-linux-setup/jans_setup
ARG JANS_CONFIG_API_RESOURCES=jans-config-api/server/src/main/resources
@@ -223,7 +223,7 @@ EXPOSE $CN_CONFIG_API_JETTY_PORT
LABEL org.opencontainers.image.url="ghcr.io/janssenproject/jans/config-api" \
org.opencontainers.image.authors="Janssen Project " \
org.opencontainers.image.vendor="Janssen Project" \
- org.opencontainers.image.version="0.0.0-nightly" \
+ org.opencontainers.image.version="1.3.0-1" \
org.opencontainers.image.title="Janssen Config API" \
org.opencontainers.image.description=""
diff --git a/docker-jans-configurator/Dockerfile b/docker-jans-configurator/Dockerfile
index 3b9acd83810..e606a4fcaea 100644
--- a/docker-jans-configurator/Dockerfile
+++ b/docker-jans-configurator/Dockerfile
@@ -15,7 +15,7 @@ RUN apk update \
# JAR files required to generate OpenID Connect keys
-ENV CN_VERSION=0.0.0-nightly
+ENV CN_VERSION=1.3.0
ENV CN_BUILD_DATE='2024-12-20 08:29'
ENV CN_SOURCE_URL=https://jenkins.jans.io/maven/io/jans/jans-auth-client/${CN_VERSION}/jans-auth-client-${CN_VERSION}-jar-with-dependencies.jar
@@ -27,7 +27,7 @@ RUN mkdir -p /opt/jans/configurator/javalibs \
# Assets sync
# ===========
-ENV JANS_SOURCE_VERSION=aa1b2edaa8d7e3413bd57a7bd7cc86206086768b
+ENV JANS_SOURCE_VERSION=040ff17942019bc10433ce17d819b8d8474f13c8
ARG GIT_CLONE_DEPTH=100
RUN git clone --depth ${GIT_CLONE_DEPTH} --filter blob:none --no-checkout https://github.com/janssenproject/jans /tmp/jans \
@@ -125,7 +125,7 @@ ENV CN_WAIT_MAX_TIME=300 \
LABEL org.opencontainers.image.url="ghcr.io/janssenproject/jans/configurator" \
org.opencontainers.image.authors="Janssen Project " \
org.opencontainers.image.vendor="Janssen Project" \
- org.opencontainers.image.version="0.0.0-nightly" \
+ org.opencontainers.image.version="1.3.0-1" \
org.opencontainers.image.title="Janssen Configuration Manager" \
org.opencontainers.image.description="Manage config and secret"
diff --git a/docker-jans-configurator/scripts/bootstrap.py b/docker-jans-configurator/scripts/bootstrap.py
index 95b8637276b..5c1f9a7c25c 100644
--- a/docker-jans-configurator/scripts/bootstrap.py
+++ b/docker-jans-configurator/scripts/bootstrap.py
@@ -476,6 +476,16 @@ def get_dump_file():
return f"{DB_DIR}/configuration.out.json"
+def get_configuration_key_file():
+ path = os.environ.get("CN_CONFIGURATOR_CONFIGURATION_KEY_FILE", "/etc/jans/conf/configuration.key")
+
+ if os.path.isfile(path):
+ return path
+
+ # backward-compat
+ return f"{DB_DIR}/configuration.key"
+
+
# ============
# CLI commands
# ============
@@ -501,7 +511,14 @@ def cli():
default=get_dump_file(),
show_default=True,
)
-def load(configuration_file, dump_file):
+@click.option(
+ "--key-file",
+ type=click.Path(exists=False),
+ help="Absolute path to file contains key to decrypt configmaps and secrets (if applicable)",
+ default=get_configuration_key_file(),
+ show_default=True,
+)
+def load(configuration_file, dump_file, key_file):
"""Loads configmaps and secrets from JSON file (generate if not exist).
"""
deps = ["config_conn", "secret_conn"]
@@ -517,7 +534,7 @@ def load(configuration_file, dump_file):
with manager.create_lock("configurator-load"):
logger.info(f"Loading configmaps and secrets from {configuration_file}")
- params, err, code = load_schema_from_file(configuration_file)
+ params, err, code = load_schema_from_file(configuration_file, key_file=key_file)
if code != 0:
logger.error(f"Unable to load configmaps and secrets; reason={err}")
raise click.Abort()
diff --git a/docker-jans-fido2/Dockerfile b/docker-jans-fido2/Dockerfile
index c5aad76a3b6..62c4e839e01 100644
--- a/docker-jans-fido2/Dockerfile
+++ b/docker-jans-fido2/Dockerfile
@@ -41,7 +41,7 @@ RUN wget -q https://maven.jans.io/maven/io/jans/jython-installer/${JYTHON_VERSIO
# =====
-ENV CN_VERSION=0.0.0-nightly
+ENV CN_VERSION=1.3.0
ENV CN_BUILD_DATE='2024-12-20 11:02'
ENV CN_SOURCE_URL=https://jenkins.jans.io/maven/io/jans/jans-fido2-server/${CN_VERSION}/jans-fido2-server-${CN_VERSION}.war
@@ -61,7 +61,7 @@ RUN mkdir -p ${JETTY_BASE}/jans-fido2/webapps \
# Assets sync
# ===========
-ENV JANS_SOURCE_VERSION=aa1b2edaa8d7e3413bd57a7bd7cc86206086768b
+ENV JANS_SOURCE_VERSION=040ff17942019bc10433ce17d819b8d8474f13c8
ARG JANS_SETUP_DIR=jans-linux-setup/jans_setup
# note that as we're pulling from a monorepo (with multiple project in it)
@@ -209,7 +209,7 @@ EXPOSE $CN_FIDO2_JETTY_PORT
LABEL org.opencontainers.image.url="ghcr.io/janssenproject/jans/fido2" \
org.opencontainers.image.authors="Janssen Project " \
org.opencontainers.image.vendor="Janssen Project" \
- org.opencontainers.image.version="0.0.0-nightly" \
+ org.opencontainers.image.version="1.3.0-1" \
org.opencontainers.image.title="Janssen FIDO2" \
org.opencontainers.image.description="FIDO2 server"
@@ -243,7 +243,8 @@ RUN chmod 664 ${JETTY_BASE}/jans-fido2/resources/log4j2.xml \
&& chown -R 1000:0 /usr/share/java \
&& chown -R 1000:0 /opt/prometheus \
&& chown 1000:0 ${JETTY_BASE}/jans-fido2/webapps/jans-fido2.xml \
- && chown -R 1000:0 ${JETTY_HOME}/temp
+ && chown -R 1000:0 ${JETTY_HOME}/temp \
+ && chown -R 1000:0 /app/templates
USER 1000
diff --git a/docker-jans-kc-scheduler/Dockerfile b/docker-jans-kc-scheduler/Dockerfile
index 686d061ed6b..5b1135dd0c7 100644
--- a/docker-jans-kc-scheduler/Dockerfile
+++ b/docker-jans-kc-scheduler/Dockerfile
@@ -13,7 +13,7 @@ RUN apk update \
# KC scheduler
# ============
-ENV CN_VERSION=0.0.0-nightly
+ENV CN_VERSION=1.3.0
ENV CN_BUILD_DATE='2024-12-20 09:15'
ENV SCHEDULER_HOME=/opt/kc-scheduler
@@ -38,7 +38,7 @@ RUN wget -q https://repo1.maven.org/maven2/org/codehaus/janino/janino/3.1.9/jani
# Assets sync
# ===========
-ENV JANS_SOURCE_VERSION=aa1b2edaa8d7e3413bd57a7bd7cc86206086768b
+ENV JANS_SOURCE_VERSION=040ff17942019bc10433ce17d819b8d8474f13c8
# note that as we're pulling from a monorepo (with multiple project in it)
# we are using partial-clone and sparse-checkout to get the assets
@@ -139,7 +139,7 @@ EXPOSE $CN_SAML_HTTP_PORT
LABEL org.opencontainers.image.url="ghcr.io/janssenproject/jans/kc-scheduler" \
org.opencontainers.image.authors="Janssen Project " \
org.opencontainers.image.vendor="Janssen Project" \
- org.opencontainers.image.version="0.0.0-nightly" \
+ org.opencontainers.image.version="1.3.0-1" \
org.opencontainers.image.title="Janssen KC scheduler" \
org.opencontainers.image.description=""
@@ -160,7 +160,8 @@ RUN adduser -s /bin/sh -h /home/1000 -D -G root -u 1000 jans
RUN chmod -R g=u /etc/certs \
&& chmod -R g=u /etc/jans \
&& chmod 664 /opt/java/lib/security/cacerts \
- && chown -R 1000:0 /opt/kc-scheduler
+ && chown -R 1000:0 /opt/kc-scheduler \
+ && chown -R 1000:0 /app/templates
USER 1000
diff --git a/docker-jans-keycloak-link/Dockerfile b/docker-jans-keycloak-link/Dockerfile
index c8663bbedb4..3cd9c64fd20 100644
--- a/docker-jans-keycloak-link/Dockerfile
+++ b/docker-jans-keycloak-link/Dockerfile
@@ -41,7 +41,7 @@ RUN wget -q https://maven.jans.io/maven/io/jans/jython-installer/${JYTHON_VERSIO
# =======
-ENV CN_VERSION=0.0.0-nightly
+ENV CN_VERSION=1.3.0
ENV CN_BUILD_DATE='2024-12-20 09:05'
ENV CN_SOURCE_URL=https://jenkins.jans.io/maven/io/jans/jans-keycloak-link-server/${CN_VERSION}/jans-keycloak-link-server-${CN_VERSION}.war
@@ -61,7 +61,7 @@ RUN mkdir -p ${JETTY_BASE}/jans-keycloak-link/webapps \
# Assets sync
# ===========
-ENV JANS_SOURCE_VERSION=aa1b2edaa8d7e3413bd57a7bd7cc86206086768b
+ENV JANS_SOURCE_VERSION=040ff17942019bc10433ce17d819b8d8474f13c8
ARG JANS_SETUP_DIR=jans-linux-setup/jans_setup
# note that as we're pulling from a monorepo (with multiple project in it)
@@ -202,7 +202,7 @@ EXPOSE $CN_KEYCLOAK_LINK_JETTY_PORT
LABEL org.opencontainers.image.url="ghcr.io/janssenproject/jans/keycloak-link" \
org.opencontainers.image.authors="Janssen Project " \
org.opencontainers.image.vendor="Janssen Project" \
- org.opencontainers.image.version="0.0.0-nightly" \
+ org.opencontainers.image.version="1.3.0-1" \
org.opencontainers.image.title="Janssen Keycloak Link" \
org.opencontainers.image.description=""
@@ -237,7 +237,8 @@ RUN chmod 664 ${JETTY_BASE}/jans-keycloak-link/resources/log4j2.xml \
&& chown -R 1000:0 /opt/prometheus \
&& chown 1000:0 ${JETTY_BASE}/jans-keycloak-link/webapps/jans-keycloak-link.xml \
&& chown -R 1000:0 /var/jans/cr-snapshots \
- && chown -R 1000:0 ${JETTY_HOME}/temp
+ && chown -R 1000:0 ${JETTY_HOME}/temp \
+ && chown -R 1000:0 /app/templates
USER 1000
diff --git a/docker-jans-link/Dockerfile b/docker-jans-link/Dockerfile
index fb7381450bc..f0fa8b23719 100644
--- a/docker-jans-link/Dockerfile
+++ b/docker-jans-link/Dockerfile
@@ -41,7 +41,7 @@ RUN wget -q https://maven.jans.io/maven/io/jans/jython-installer/${JYTHON_VERSIO
# ====
-ENV CN_VERSION=0.0.0-nightly
+ENV CN_VERSION=1.3.0
ENV CN_BUILD_DATE='2024-12-20 08:56'
ENV CN_SOURCE_URL=https://jenkins.jans.io/maven/io/jans/jans-link-server/${CN_VERSION}/jans-link-server-${CN_VERSION}.war
@@ -61,7 +61,7 @@ RUN mkdir -p ${JETTY_BASE}/jans-link/webapps \
# Assets sync
# ===========
-ENV JANS_SOURCE_VERSION=aa1b2edaa8d7e3413bd57a7bd7cc86206086768b
+ENV JANS_SOURCE_VERSION=040ff17942019bc10433ce17d819b8d8474f13c8
ARG JANS_SETUP_DIR=jans-linux-setup/jans_setup
# note that as we're pulling from a monorepo (with multiple project in it)
@@ -202,7 +202,7 @@ EXPOSE $CN_LINK_JETTY_PORT
LABEL org.opencontainers.image.url="ghcr.io/janssenproject/jans/link" \
org.opencontainers.image.authors="Janssen Project " \
org.opencontainers.image.vendor="Janssen Project" \
- org.opencontainers.image.version="0.0.0-nightly" \
+ org.opencontainers.image.version="1.3.0-1" \
org.opencontainers.image.title="Janssen Link" \
org.opencontainers.image.description=""
@@ -237,7 +237,8 @@ RUN chmod 664 ${JETTY_BASE}/jans-link/resources/log4j2.xml \
&& chown -R 1000:0 /opt/prometheus \
&& chown 1000:0 ${JETTY_BASE}/jans-link/webapps/jans-link.xml \
&& chown -R 1000:0 /var/jans/link-snapshots \
- && chown -R 1000:0 ${JETTY_HOME}/temp
+ && chown -R 1000:0 ${JETTY_HOME}/temp \
+ && chown -R 1000:0 /app/templates
USER 1000
diff --git a/docker-jans-monolith/Dockerfile b/docker-jans-monolith/Dockerfile
index 069e406c4fc..25ab0018e30 100644
--- a/docker-jans-monolith/Dockerfile
+++ b/docker-jans-monolith/Dockerfile
@@ -42,7 +42,7 @@ EXPOSE 443 8080 1636
# jans-linux-setup
# =====================
-ENV JANS_SOURCE_VERSION=aa1b2edaa8d7e3413bd57a7bd7cc86206086768b
+ENV JANS_SOURCE_VERSION=040ff17942019bc10433ce17d819b8d8474f13c8
# cleanup
RUN rm -rf /tmp/jans
@@ -92,7 +92,7 @@ ENV CN_HOSTNAME="demoexample.jans.io" \
LABEL org.opencontainers.image.url="ghcr.io/janssenproject/jans/monolith" \
org.opencontainers.image.authors="Janssen Project " \
org.opencontainers.image.vendor="Janssen Project" \
- org.opencontainers.image.version="0.0.0-nightly" \
+ org.opencontainers.image.version="1.3.0-1" \
org.opencontainers.image.title="Janssen Monolith Image" \
org.opencontainers.image.description="Janssen Authorization server"
diff --git a/docker-jans-monolith/clean.sh b/docker-jans-monolith/clean.sh
index 96b650b521a..ff9bbd47109 100644
--- a/docker-jans-monolith/clean.sh
+++ b/docker-jans-monolith/clean.sh
@@ -23,7 +23,7 @@ if [ -z "$INSTALLED_JANSSEN_NAME" ]; then
fi
if [ -z "$JANSSEN_VERSION" ]; then
- JANSSEN_VERSION="0.0.0-nightly"
+ JANSSEN_VERSION="1.3.0-1"
fi
if [ -z "$DATABASE_VOLUME_NAME" ]; then
diff --git a/docker-jans-monolith/down.sh b/docker-jans-monolith/down.sh
index d1af54d8a61..2b2b921507d 100644
--- a/docker-jans-monolith/down.sh
+++ b/docker-jans-monolith/down.sh
@@ -23,7 +23,7 @@ if [ -z "$INSTALLED_JANSSEN_NAME" ]; then
fi
if [ -z "$JANSSEN_VERSION" ]; then
- JANSSEN_VERSION="0.0.0-nightly"
+ JANSSEN_VERSION="1.3.0-1"
fi
if [ -z "$JANSSEN_SERVICE_NAME" ]; then
diff --git a/docker-jans-monolith/jans-mysql-compose.yml b/docker-jans-monolith/jans-mysql-compose.yml
index 07a4d639d67..95708ffbc0e 100644
--- a/docker-jans-monolith/jans-mysql-compose.yml
+++ b/docker-jans-monolith/jans-mysql-compose.yml
@@ -16,7 +16,7 @@ services:
- MYSQL_PASSWORD=1t5Fin3#security
- MYSQL_ROOT_PASSWORD=1t5Fin3#security
jans:
- image: ${JANSSEN_IMAGE:-ghcr.io/janssenproject/jans/monolith:0.0.0-nightly}
+ image: ${JANSSEN_IMAGE:-ghcr.io/janssenproject/jans/monolith:1.3.0-1}
restart: always
ports:
- "443:443"
diff --git a/docker-jans-monolith/jans-postgres-compose.yml b/docker-jans-monolith/jans-postgres-compose.yml
index c7707f0a342..891a496cada 100644
--- a/docker-jans-monolith/jans-postgres-compose.yml
+++ b/docker-jans-monolith/jans-postgres-compose.yml
@@ -14,7 +14,7 @@ services:
POSTGRES_PASSWORD: 1t5Fin3#security
POSTGRES_DB: jans
jans:
- image: ${JANSSEN_IMAGE:-ghcr.io/janssenproject/jans/monolith:0.0.0-nightly}
+ image: ${JANSSEN_IMAGE:-ghcr.io/janssenproject/jans/monolith:1.3.0-1}
restart: always
ports:
- "443:443"
diff --git a/docker-jans-monolith/up.sh b/docker-jans-monolith/up.sh
index 7dd0eb8cff8..e5a6170a651 100644
--- a/docker-jans-monolith/up.sh
+++ b/docker-jans-monolith/up.sh
@@ -23,7 +23,7 @@ if [ -z "$INSTALLED_JANSSEN_NAME" ]; then
fi
if [ -z "$JANSSEN_VERSION" ]; then
- JANSSEN_VERSION="0.0.0-nightly"
+ JANSSEN_VERSION="1.3.0-1"
fi
if [ -z "$DATABASE_VOLUME_NAME" ]; then
diff --git a/docker-jans-persistence-loader/Dockerfile b/docker-jans-persistence-loader/Dockerfile
index bb662da7577..675ea717070 100644
--- a/docker-jans-persistence-loader/Dockerfile
+++ b/docker-jans-persistence-loader/Dockerfile
@@ -16,7 +16,7 @@ RUN apk update \
# ===========
# janssenproject/jans SHA commit
-ENV JANS_SOURCE_VERSION=aa1b2edaa8d7e3413bd57a7bd7cc86206086768b
+ENV JANS_SOURCE_VERSION=040ff17942019bc10433ce17d819b8d8474f13c8
ARG JANS_SETUP_DIR=jans-linux-setup/jans_setup
ARG JANS_SCRIPT_CATALOG_DIR=docs/script-catalog
ARG JANS_CONFIG_API_RESOURCES=jans-config-api/server/src/main/resources
@@ -159,7 +159,7 @@ ENV CN_CACHE_TYPE=NATIVE_PERSISTENCE \
LABEL org.opencontainers.image.url="ghcr.io/janssenproject/jans/persistence-loader" \
org.opencontainers.image.authors="Janssen Project " \
org.opencontainers.image.vendor="Janssen Project" \
- org.opencontainers.image.version="0.0.0-nightly" \
+ org.opencontainers.image.version="1.3.0-1" \
org.opencontainers.image.title="Janssen Authorization Server Persistence loader" \
org.opencontainers.image.description="Generate initial data for persistence layer"
@@ -180,7 +180,8 @@ RUN adduser -s /bin/sh -h /home/1000 -D -G root -u 1000 1000
# adjust ownership and permission
RUN chmod -R g=u /app/custom_ldif \
&& chmod -R g=u /etc/certs \
- && chmod -R g=u /etc/jans
+ && chmod -R g=u /etc/jans \
+ && chown -R 1000:0 /app/templates
USER 1000
diff --git a/docker-jans-saml/Dockerfile b/docker-jans-saml/Dockerfile
index 222834ca2bc..ce66e05ed61 100644
--- a/docker-jans-saml/Dockerfile
+++ b/docker-jans-saml/Dockerfile
@@ -23,7 +23,7 @@ RUN mkdir -p /opt/keycloak/logs \
# KC integration
# ==============
-ENV CN_VERSION=0.0.0-nightly
+ENV CN_VERSION=1.3.0
ENV CN_BUILD_DATE='2024-12-20 09:15'
RUN wget -q https://jenkins.jans.io/maven/io/jans/kc-jans-spi/${CN_VERSION}/kc-jans-spi-${CN_VERSION}.jar -P /opt/keycloak/providers \
@@ -35,7 +35,7 @@ RUN wget -q https://jenkins.jans.io/maven/io/jans/kc-jans-spi/${CN_VERSION}/kc-j
# Assets sync
# ===========
-ENV JANS_SOURCE_VERSION=aa1b2edaa8d7e3413bd57a7bd7cc86206086768b
+ENV JANS_SOURCE_VERSION=040ff17942019bc10433ce17d819b8d8474f13c8
ARG JANS_SETUP_DIR=jans-linux-setup/jans_setup
# note that as we're pulling from a monorepo (with multiple project in it)
@@ -173,7 +173,7 @@ EXPOSE $CN_SAML_HTTP_PORT
LABEL org.opencontainers.image.url="ghcr.io/janssenproject/jans/saml" \
org.opencontainers.image.authors="Janssen Project " \
org.opencontainers.image.vendor="Janssen Project" \
- org.opencontainers.image.version="0.0.0-nightly" \
+ org.opencontainers.image.version="1.3.0-1" \
org.opencontainers.image.title="Janssen SAML" \
org.opencontainers.image.description=""
@@ -203,7 +203,8 @@ RUN chmod -R g=u /etc/certs \
&& chown -R 1000:0 /opt/idp \
&& chown -R 1000:0 /usr/share/java \
&& chown -R 1000:0 /opt/keycloak/logs \
- && chown -R 1000:0 /opt/keycloak/conf
+ && chown -R 1000:0 /opt/keycloak/conf \
+ && chown -R 1000:0 /app/templates
USER 1000
diff --git a/docker-jans-scim/Dockerfile b/docker-jans-scim/Dockerfile
index 5a3d48c1857..9ff848d9f88 100644
--- a/docker-jans-scim/Dockerfile
+++ b/docker-jans-scim/Dockerfile
@@ -40,7 +40,7 @@ RUN wget -q https://maven.jans.io/maven/io/jans/jython-installer/${JYTHON_VERSIO
# SCIM
# ====
-ENV CN_VERSION=0.0.0-nightly
+ENV CN_VERSION=1.3.0
ENV CN_BUILD_DATE='2024-12-20 10:33'
ENV CN_SOURCE_URL=https://jenkins.jans.io/maven/io/jans/jans-scim-server/${CN_VERSION}/jans-scim-server-${CN_VERSION}.war
@@ -60,7 +60,7 @@ RUN mkdir -p ${JETTY_BASE}/jans-scim/webapps \
# Assets sync
# ===========
-ENV JANS_SOURCE_VERSION=aa1b2edaa8d7e3413bd57a7bd7cc86206086768b
+ENV JANS_SOURCE_VERSION=040ff17942019bc10433ce17d819b8d8474f13c8
ARG JANS_SETUP_DIR=jans-linux-setup/jans_setup
ARG JANS_SCIM_RESOURCE_DIR=jans-scim/server/src/main/resources
@@ -204,7 +204,7 @@ EXPOSE $CN_SCIM_JETTY_PORT
LABEL org.opencontainers.image.url="ghcr.io/janssenproject/jans/scim" \
org.opencontainers.image.authors="Janssen Project " \
org.opencontainers.image.vendor="Janssen Project" \
- org.opencontainers.image.version="0.0.0-nightly" \
+ org.opencontainers.image.version="1.3.0-1" \
org.opencontainers.image.title="Janssen SCIM" \
org.opencontainers.image.description="SCIM server"
@@ -237,7 +237,8 @@ RUN chmod 664 ${JETTY_BASE}/jans-scim/resources/log4j2.xml \
&& chown -R 1000:0 /usr/share/java \
&& chown -R 1000:0 /opt/prometheus \
&& chown 1000:0 ${JETTY_BASE}/jans-scim/webapps/jans-scim.xml \
- && chown -R 1000:0 ${JETTY_HOME}/temp
+ && chown -R 1000:0 ${JETTY_HOME}/temp \
+ && chown -R 1000:0 /app/templates
USER 1000
diff --git a/docs/agama-catalog/jans/inboundID/project/project.json b/docs/agama-catalog/jans/inboundID/project/project.json
index 2ff995479db..f06e2f76819 100644
--- a/docs/agama-catalog/jans/inboundID/project/project.json
+++ b/docs/agama-catalog/jans/inboundID/project/project.json
@@ -2,7 +2,7 @@
"projectName": "agama-inbound-oauth",
"author": "jgomer2001",
"type": "Community",
- "version": "0.0.0-nightly",
+ "version": "1.3.0",
"description": "A project useful to delegate authorization to external services like social sites",
"noDirectLaunch": [ "io.jans.inbound.Apple", "io.jans.inbound.GenericProvider", "io.jans.inbound.oauth2.AuthzCode", "io.jans.inbound.oauth2.AuthzCodeWithUserInfo" ],
"configs": {
diff --git a/docs/assets/agama/challenge-flow.png b/docs/assets/agama/challenge-flow.png
new file mode 100644
index 00000000000..9cb8292887b
Binary files /dev/null and b/docs/assets/agama/challenge-flow.png differ
diff --git a/docs/assets/jans_health.png b/docs/assets/jans_health.png
new file mode 100644
index 00000000000..86553acaa75
Binary files /dev/null and b/docs/assets/jans_health.png differ
diff --git a/docs/assets/jans_info.png b/docs/assets/jans_info.png
new file mode 100644
index 00000000000..9fea89fa659
Binary files /dev/null and b/docs/assets/jans_info.png differ
diff --git a/docs/assets/jans_logs.png b/docs/assets/jans_logs.png
new file mode 100644
index 00000000000..ed8818f2443
Binary files /dev/null and b/docs/assets/jans_logs.png differ
diff --git a/docs/assets/jans_restart.png b/docs/assets/jans_restart.png
new file mode 100644
index 00000000000..b4c8a7cea97
Binary files /dev/null and b/docs/assets/jans_restart.png differ
diff --git a/docs/assets/jans_start.png b/docs/assets/jans_start.png
new file mode 100644
index 00000000000..e85c33c5307
Binary files /dev/null and b/docs/assets/jans_start.png differ
diff --git a/docs/assets/jans_status.png b/docs/assets/jans_status.png
new file mode 100644
index 00000000000..42485240e54
Binary files /dev/null and b/docs/assets/jans_status.png differ
diff --git a/docs/assets/jans_stop.png b/docs/assets/jans_stop.png
new file mode 100644
index 00000000000..b840865ff68
Binary files /dev/null and b/docs/assets/jans_stop.png differ
diff --git a/docs/assets/jans_version.png b/docs/assets/jans_version.png
new file mode 100644
index 00000000000..305b0848251
Binary files /dev/null and b/docs/assets/jans_version.png differ
diff --git a/docs/casa/developer/overview.md b/docs/casa/developer/overview.md
index 25827284d02..7515dbfb138 100644
--- a/docs/casa/developer/overview.md
+++ b/docs/casa/developer/overview.md
@@ -109,7 +109,7 @@ This is probably the most common requirement. Visit this [page](./add-authn-meth
### Other forms of customization
-Most forms of customization can be tackled using flow cancellation. Through cancellation, a flow can be aborted while running and the control returned to one of its callers. Learn more about this topic [here](../../janssen-server/developer/agama/advanced-usages#cancellation).
+Most forms of customization can be tackled using flow cancellation. Through cancellation, a flow can be aborted while running and the control returned to one of its callers. Learn more about this topic [here](../../janssen-server/developer/agama/advanced-usages.md#cancellation).
As an example, let's assume you want to add a _"don't have an account? register here"_ button in the initial screen of Casa flow. Here's what you can do:
diff --git a/docs/cedarling/cedarling-authz.md b/docs/cedarling/cedarling-authz.md
index 0f5f03fe0f3..70445076942 100644
--- a/docs/cedarling/cedarling-authz.md
+++ b/docs/cedarling/cedarling-authz.md
@@ -55,7 +55,9 @@ Action, Resource and Context is sent by the application in the authorization req
this is a sample request from a hypothetical application:
```js
-input = {
+const bootstrap_config = {...};
+const cedarling = await init(bootstrap_config);
+let input = {
"tokens": {
"access_token": "eyJhbGc....",
"id_token": "eyJjbGc...",
@@ -76,19 +78,19 @@ input = {
}
}
-decision_result = authz(input)
+decision_result = await cedarling(input)
```
## Automatically Adding Entity References to the Context
-Cedarling simplifies context creation by automatically including certain entities. This means you don't need to manually pass their references when using them in your policies. The following entities are automatically added to the context, along with their naming conventions in `lower_snake_case` format:
+Cedarling simplifies context creation by automatically including certain entities. This means you don't need to manually pass their references when using them in your policies. The following entities are automatically added to the context.
-- **Workload Entity**: `workload`
-- **User Entity**: `user`
-- **Resource Entity**: `resource`
-- **Access Token Entity**: `access_token`
-- **ID Token Entity**: `id_token`
-- **Userinfo Token Entity**: `userinfo_token`
+- Workload Entity
+- User Entity
+- Resource Entity
+- Access Token Entity
+- ID Token Entity
+- Userinfo Token Entity
### Example Policy
diff --git a/docs/cedarling/cedarling-logs.md b/docs/cedarling/cedarling-logs.md
index 05cf27bf645..47c254073dc 100644
--- a/docs/cedarling/cedarling-logs.md
+++ b/docs/cedarling/cedarling-logs.md
@@ -95,6 +95,15 @@ Example of decision log.
"Workload": {
"org_id": "some_long_id"
},
+ "diagnostics": {
+ "reason": [
+ {
+ "id": "840da5d85403f35ea76519ed1a18a33989f855bf1cf8",
+ "description": "policy for user"
+ }
+ ],
+ "errors": []
+ },
"lock_client_id": null,
"action": "Jans::Action::\"Update\"",
"resource": "Jans::Issue::\"random_id\"",
diff --git a/docs/cedarling/cedarling-policy-store.md b/docs/cedarling/cedarling-policy-store.md
index 55bdf64de8c..504f9cf69ca 100644
--- a/docs/cedarling/cedarling-policy-store.md
+++ b/docs/cedarling/cedarling-policy-store.md
@@ -178,7 +178,11 @@ This record contains the information needed to validate tokens from this issuer:
- **description** : (*String*) A brief description of the trusted issuer, providing context for administrators.
- **openid_configuration_endpoint** : (*String*) The HTTPS URL for the OpenID Connect configuration endpoint (usually found at `/.well-known/openid-configuration`).
- **identity_source** : (*Object*, *optional*) Metadata related to the tokens issued by this issuer.
-- **`access_tokens`, `id_tokens`, `userinfo_tokens`, and `tx_tokens`**: See: [Token Metadata Schema](#token-metadata-schema).
+
+**Notes**:
+
+- The `access_tokens`, `id_tokens`, `userinfo_tokens`, and `tx_tokens` fields will follow the [Token Metadata Schema](#token-metadata-schema).
+- The `access_tokens` will contain a `trusted` and `principal_identifier` field in addition to the fields from the `Token Metadata Schema`.
### Token Metadata Schema
@@ -186,8 +190,6 @@ The Token Entity Metadata Schema defines how tokens are mapped, parsed, and tran
```json
{
- "trusted": bool,
- "principal_identifier": "str",
"user_id": "",
"role_mapping": "",
"claim_mapping": {
diff --git a/docs/cedarling/python/sidecar.md b/docs/cedarling/cedarling-sidecar.md
similarity index 51%
rename from docs/cedarling/python/sidecar.md
rename to docs/cedarling/cedarling-sidecar.md
index 9febea1c8a4..6a3647a0fc6 100644
--- a/docs/cedarling/python/sidecar.md
+++ b/docs/cedarling/cedarling-sidecar.md
@@ -1,7 +1,6 @@
---
tags:
- cedarling
- - python
- sidecar
---
@@ -11,13 +10,29 @@ The sidecar is a containerized Flask project that uses the `cedarling_python` bi
## Docker setup
-- Ensure that you have installed [docker](https://docs.docker.com/engine/install/) and [docker compose](https://docs.docker.com/compose/install/).
-- Clone the [Janssen](https://github.com/JanssenProject/jans) repository
-- Navigate to `jans/jans-cedarling/flask-sidecar`
-- Edit the provided `secrets/bootstrap.json` file to your specifications. The configuration keys are described [here](https://github.com/JanssenProject/jans/blob/ffe9f493e4a5c6b05f2adeeb8a6eba7eb83b103e/jans-cedarling/bindings/cedarling_python/cedarling_python.pyi#L9).
-- Run `docker compose up`
- - For cloud deployments, please use the provided Dockerfile and pass your bootstrap configuration via the environment variable `CEDARLING_BOOTSTRAP_CONFIG_FILE`.
-- The sidecar runs on port 5000. OpenAPI documentation is available at `http://0.0.0.0:5000/swagger-ui`
+- Ensure that you have installed [docker](https://docs.docker.com/engine/install/)
+- Create a file called `bootstrap.json`. You may use this [sample](https://github.com/JanssenProject/jans/blob/main/jans-cedarling/flask-sidecar/secrets/bootstrap.json) file.
+- Modify the bootstrap file to your specifications. In particular you need to provide a link to your policy store in `CEDARLING_POLICY_STORE_URI`. The configuration keys are described [here](https://github.com/JanssenProject/jans/blob/main/jans-cedarling/bindings/cedarling_python/cedarling_python.pyi#L10).
+- Pull the docker image:
+ ```
+ docker pull ghcr.io/janssenproject/jans/cedarling-flask-sidecar:1.3.0-1
+ ```
+- Run the docker image, replacing `` with the absolute path to your bootstrap file:
+
+ ```bash
+ docker run -d \
+ -e APP_MODE='development' \
+ -e CEDARLING_BOOTSTRAP_CONFIG_FILE=/bootstrap.json \
+ -e SIDECAR_DEBUG_RESPONSE=False \
+ --mount type=bind,src=,dst=/bootstrap.json \
+ -p 5000:5000\
+ ghcr.io/janssenproject/jans/cedarling-flask-sidecar:1.3.0-1
+ ```
+
+ - `SIDECAR_DEBUG_RESPONSE` is an option that will cause the sidecar to return extra diagnostic information for each query if set to `True`. This may be useful to check which policies are being used to reach a decision.
+ - Take note of the output of the command. This is the container ID of the sidecar.
+- The sidecar runs in the background on port 5000. OpenAPI documentation is available at `http://0.0.0.0:5000/swagger-ui`
+- To stop the sidecar, run `docker container stop `
## Usage
@@ -25,11 +40,16 @@ The sidecar has one endpoint: `/cedarling/evaluation`.
Example request to the evaluation endpoint:
-```
+```json
{
"subject": {
- "type": "string",
- "id": "string"
+ "type": "JWT",
+ "id": "cedarling",
+ "properties": {
+ "access_token": "",
+ "id_token": "",
+ "userinfo_token": ""
+ }
},
"resource": {
"type": "Jans::Application",
@@ -48,9 +68,6 @@ Example request to the evaluation endpoint:
"name": "Jans::Action::\"Read\""
},
"context": {
- "access_token": "...",
- "id_token": "...",
- "userinfo_token": "...",
"device_health": [
"Healthy"
],
@@ -69,13 +86,13 @@ Example request to the evaluation endpoint:
}
```
-Cedarling requires OpenID Userinfo, Access, and ID tokens to construct the principal entity, as described [here](../cedarling-authz.md). As per AuthZen specification, these values are sent in the `context` field of the payload. Conversely, the `subject` field is currently not used by cedarling. These 3 tokens are subsequently removed from the context object before it is passed to cedarling.
+Cedarling requires OpenID Userinfo, Access, and ID tokens to construct the principal entity, as described [here](./cedarling-authz.md). These values are sent in the subject field's properties.
Upon creating the principal, action, resource, and context entities, cedarling will evaluate these entities against the policies defined in the policy store. Then it will return a true/false decision. If the decision is false, the sidecar will analyze cedarling diagnostics and provide additional information for the admin.
Example of `true` case:
-```
+```json
{
"decision": true
}
@@ -83,7 +100,7 @@ Example of `true` case:
Example of `false` case:
-```
+```json
{
"context": {
"reason_admin": {
@@ -99,4 +116,4 @@ Example of `false` case:
}
```
-In this example both the person and workload evaluations were `DENY`, so the decision was false. Additional information is returned in the `context` field as per AuthZen specification.
+In this example both the person and workload evaluations were `DENY`, so the decision was false. Additional information is returned in the `context` field.
diff --git a/docs/cedarling/cedarling-wasm.md b/docs/cedarling/cedarling-wasm.md
new file mode 100644
index 00000000000..b58712cf220
--- /dev/null
+++ b/docs/cedarling/cedarling-wasm.md
@@ -0,0 +1,228 @@
+---
+tags:
+ - cedarling
+ - wasm
+---
+
+# WASM for Cedarling
+
+Cedarling provides a binding for JavaScript programs via the `wasm-pack` tool. This allows browser developers to use the cedarling crate in their code directly.
+
+## Requirements
+
+- Rust 1.63 or greater
+- Installed `wasm-pack` via `cargo`
+- clang with `wasm` target support
+
+## Building
+
+- Install `wasm-pack` by:
+
+ ```sh
+ cargo install wasm-pack
+ ```
+
+- Build cedarling `wasm` in release:
+
+ ```bash
+ wasm-pack build --release --target web
+ ```
+
+ `wasm-pack` automatically make optimization of `wasm` binary file, using `wasm-opt`.
+- Get result in the `pkg` folder.
+
+## Including in projects
+
+For using result files in browser project you need make result `pkg` folder accessible for loading in the browser so that you can later import the corresponding file from the browser.
+
+Here is example of code snippet:
+
+```html
+
+```
+
+## Usage
+
+Before usage make sure that you have completed `Building` steps.
+You can find usage examples in the following locations:
+
+- `jans-cedarling/bindings/cedarling_wasm/index.html`: A simple example demonstrating basic usage.
+- `jans-cedarling/bindings/cedarling_wasm/cedarling_app.html`: A fully featured `Cedarling` browser app where you can test and validate your configuration.
+
+### Defined API
+
+```ts
+/**
+ * Create a new instance of the Cedarling application.
+ * This function can take as config parameter the eather `Map` other `Object`
+ */
+export function init(config: any): Promise;
+
+/**
+ * The instance of the Cedarling application.
+ */
+export class Cedarling {
+ /**
+ * Create a new instance of the Cedarling application.
+ * Assume that config is `Object`
+ */
+ static new(config: object): Promise;
+ /**
+ * Create a new instance of the Cedarling application.
+ * Assume that config is `Map`
+ */
+ static new_from_map(config: Map): Promise;
+ /**
+ * Authorize request
+ * makes authorization decision based on the [`Request`]
+ */
+ authorize(request: any): Promise;
+ /**
+ * Get logs and remove them from the storage.
+ * Returns `Array` of `Map`
+ */
+ pop_logs(): Array;
+ /**
+ * Get specific log entry.
+ * Returns `Map` with values or `null`.
+ */
+ get_log_by_id(id: string): any;
+ /**
+ * Returns a list of all log ids.
+ * Returns `Array` of `String`
+ */
+ get_log_ids(): Array;
+}
+
+/**
+ * A WASM wrapper for the Rust `cedarling::AuthorizeResult` struct.
+ * Represents the result of an authorization request.
+ */
+export class AuthorizeResult {
+ /**
+ * Convert `AuthorizeResult` to json string value
+ */
+ json_string(): string;
+ /**
+ * Result of authorization where principal is `Jans::Workload`
+ */
+ workload?: AuthorizeResultResponse;
+ /**
+ * Result of authorization where principal is `Jans::User`
+ */
+ person?: AuthorizeResultResponse;
+ /**
+ * Result of authorization
+ * true means `ALLOW`
+ * false means `Deny`
+ *
+ * this field is [`bool`] type to be compatible with [authzen Access Evaluation Decision](https://openid.github.io/authzen/#section-6.2.1).
+ */
+ decision: boolean;
+}
+
+/**
+ * A WASM wrapper for the Rust `cedar_policy::Response` struct.
+ * Represents the result of an authorization request.
+ */
+export class AuthorizeResultResponse {
+ /**
+ * Authorization decision
+ */
+ readonly decision: boolean;
+ /**
+ * Diagnostics providing more information on how this decision was reached
+ */
+ readonly diagnostics: Diagnostics;
+}
+
+/**
+ * Diagnostics
+ * ===========
+ *
+ * Provides detailed information about how a policy decision was made, including policies that contributed to the decision and any errors encountered during evaluation.
+ */
+export class Diagnostics {
+ /**
+ * `PolicyId`s of the policies that contributed to the decision.
+ * If no policies applied to the request, this set will be empty.
+ *
+ * The ids should be treated as unordered,
+ */
+ readonly reason: (string)[];
+ /**
+ * Errors that occurred during authorization. The errors should be
+ * treated as unordered, since policies may be evaluated in any order.
+ */
+ readonly errors: (PolicyEvaluationError)[];
+}
+
+/**
+ * PolicyEvaluationError
+ * =====================
+ *
+ * Represents an error that occurred when evaluating a Cedar policy.
+ */
+export class PolicyEvaluationError {
+ /**
+ * Id of the policy with an error
+ */
+ readonly id: string;
+ /**
+ * Underlying evaluation error string representation
+ */
+ readonly error: string;
+}
+```
diff --git a/docs/cedarling/python/README.md b/docs/cedarling/python/README.md
index 134fd3d4510..630a29de18c 100644
--- a/docs/cedarling/python/README.md
+++ b/docs/cedarling/python/README.md
@@ -33,14 +33,18 @@ The recommended way to include cedarling in a Python project is to compile it to
```
- The wheel will be available in `target/wheels/`
-## Including in projects
+- If you are developing a simple project in a `venv` setup, you can run `maturin develop --release` and maturin will install the wheel into the currently activated virtual environment. After that, you may run your code directly from the command line.
-If you are developing a simple project in a `venv` setup, in the previous section you can run `maturin develop --release` and maturin will install the wheel into the currently activated virtual environment. After that, you may run your code directly from the command line.
+## Including in projects
In case of more complicated projects with a dependency manager such as [poetry](https://python-poetry.org/), you can either install the wheel via the command line:
```
poetry add path/to/wheel.whl
```
+Or install it into the virtual environment managed by poetry:
+```
+poetry run pip install path/to/wheel.whl
+```
or include it as a static dependency in the [dependencies](https://python-poetry.org/docs/pyproject/#dependencies-and-dependency-groups) section of your `pyproject.toml`:
```
...
diff --git a/docs/cedarling/python/usage.md b/docs/cedarling/python/usage.md
index 9b531d569e0..c9b21738e1c 100644
--- a/docs/cedarling/python/usage.md
+++ b/docs/cedarling/python/usage.md
@@ -16,12 +16,8 @@ In this example, we will show an example Python script that calls the `cedarling
```
(venv) $ python example.py
-Policy store location not provided, use 'CEDARLING_LOCAL_POLICY_STORE' environment variable
-Used default policy store path: example_files/policy-store.json
-
-{"id":"0193414e-9672-786a-986c-57f48d41c4e4","time":1731967489,"log_type":"System","pdp_id":"c0ec33ff-9482-4bdc-83f6-2925a41a3280","msg":"configuration parsed successfully"}
-{"id":"0193414e-9672-786a-986c-57f5379086c3","time":1731967489,"log_type":"System","pdp_id":"c0ec33ff-9482-4bdc-83f6-2925a41a3280","msg":"Cedarling Authz initialized successfully","application_id":"TestApp"}
-{"id":"0193414e-9676-7d8a-b55b-3f0097355851","time":1731967489,"log_type":"Decision","pdp_id":"c0ec33ff-9482-4bdc-83f6-2925a41a3280","msg":"Result of authorize.","application_id":"TestApp","action":"Jans::Action::\"Read\"","resource":"Jans::Application::\"some_id\"","context":{"user_agent":"Linux","operating_system":"Linux","network_type":"Local","network":"127.0.0.1","geolocation":["America"],"fraud_indicators":["Allowed"],"device_health":["Healthy"],"current_time":1731967489},"person_principal":"Jans::User::\"qzxn1Scrb9lWtGxVedMCky-Ql_ILspZaQA6fyuYktw0\"","person_diagnostics":{"reason":["840da5d85403f35ea76519ed1a18a33989f855bf1cf8"],"errors":[]},"person_decision":"ALLOW","workload_principal":"Jans::Workload::\"d7f71bea-c38d-4caf-a1ba-e43c74a11a62\"","workload_diagnostics":{"reason":["444da5d85403f35ea76519ed1a18a33989f855bf1cf8"],"errors":[]},"workload_decision":"ALLOW","authorized":true}
+{"request_id":"019474da-12ee-7315-bb12-35f46a9bc2b2","timestamp":"2025-01-17T09:20:36.334Z","log_kind":"System","pdp_id":"4cf7864b-50d4-4492-8cd1-3ddb424e2711","level":"INFO","msg":"Cedarling Authz initialized successfully","application_id":"My App","cedar_lang_version":"4.1.0","cedar_sdk_version":"4.2.2"}
+{"request_id":"019474da-12f3-74f1-8f3e-da7624806135","timestamp":"2025-01-17T09:20:36.339Z","log_kind":"Decision","pdp_id":"4cf7864b-50d4-4492-8cd1-3ddb424e2711","policystore_id":"gICAgcHJpbmNpcGFsIGlz","policystore_version":"undefined","principal":"User & Workload","User":{"email":{"domain":"jans.test","uid":"admin"},"sub":"qzxn1Scrb9lWtGxVedMCky-Ql_ILspZaQA6fyuYktw0"},"Workload":{"client_id":"d7f71bea-c38d-4caf-a1ba-e43c74a11a62"},"diagnostics":{"reason":[{"id":"840da5d85403f35ea76519ed1a18a33989f855bf1cf8","description":"simple policy example for principal user"}],"errors":[]},"action":"Jans::Action::\"Read\"","resource":"Jans::Application::\"some_id\"","decision":"ALLOW","tokens":{"id_token":{"jti":"ijLZO1ooRyWrgIn7cIdNyA"},"Userinfo":{"jti":"OIn3g1SPSDSKAYDzENVoug"},"access":{"jti":"uZUh1hDUQo6PFkBPnwpGzg"}},"decision_time_ms":3}
Result of workload authorization: ALLOW
Policy ID used:
444da5d85403f35ea76519ed1a18a33989f855bf1cf8
@@ -35,7 +31,8 @@ Errors during authorization: 0
## Explanation
-Cedarling creates principal entities from the access, ID and userinfo tokens. The action, resource and context entities are declared in code. These four entities together form the `PARC` format that cedarling evaluates against policies provided in the policy store. The principal entities can be either User, Workload or Role. After forming the entities, cedarling evaluates them against the policies provided in the policy store. If entity is explicitly permitted by a policy, the result of the evaluation is `ALLOW`, otherwise it is `DENY`.
+Cedarling creates principal entities from either the access, ID and userinfo tokens, or a combination of the three depending on bootstrap configurations. For example, to create a Workload entity only one token is sufficient. But to create a user entity at least ID or Userinfo tokens are needed. This is defined by `CEDARLING_USER_AUTHZ` and `CEDARLING_WORKLOAD_AUTHZ` in the [bootstrap configuration](https://github.com/JanssenProject/jans/blob/main/jans-cedarling/bindings/cedarling_python/cedarling_python.pyi#L10). Cedarling will make a best attempt to create entities based on tokens provided; if it is unable to do so it will raise an `EntitiesError` exception.
+The action, resource and context entities are declared in code. These four entities together form the `PARC` format that cedarling evaluates against policies provided in the policy store. The principal entities can be either User, Workload or Role. After forming the entities, cedarling evaluates them against the policies provided in the policy store. If entity is explicitly permitted by a policy, the result of the evaluation is `ALLOW`, otherwise it is `DENY`.
In this case there are two policies in the store, one for User entities and one for Workload entities:
@@ -91,9 +88,7 @@ context = {
}
request = Request(
- access_token,
- id_token,
- userinfo_token,
+ tokens=Tokens(access_token, id_token, userinfo_token),
action=action,
resource=resource, context=context)
@@ -101,7 +96,7 @@ authorize_result = instance.authorize(request)
assert authorize_result.is_allowed()
```
-Cedarling will return `is_allowed()` as `True` only if both the User and Workload entity evaluations are `ALLOW`.
+Cedarling will return `is_allowed()` as `True` only if the authorization queries set in the bootstrap return `True`. In case of the example, both `CEDARLING_USER_AUTHZ` and `CEDARLING_WORKLOAD_AUTHZ` were set to `enabled`, so cedarling will only return True if both user and workload evaluations are true.
## Exposed functions
diff --git a/docs/contribute/testing.md b/docs/contribute/testing.md
index 3fc5672b60a..debd5321cb4 100644
--- a/docs/contribute/testing.md
+++ b/docs/contribute/testing.md
@@ -109,7 +109,7 @@ As part of pre-release QA check, we run a set of [manual sanity checks](#sanity-
- Review functioning of `.well-known` endpoints for OpenId, Fido, UMA, SCIM modules
- Test device authentication flow using TUI
-- Test password authentication flow using Janssen Server Tent
+- Test password authentication flow using Jans Tarp
- Test Agama project deployment and functioning
### Post-release QA checklist
diff --git a/docs/janssen-server/auth-server/oauth-features/dpop.md b/docs/janssen-server/auth-server/oauth-features/dpop.md
index 9b8133278b3..3b5739455e6 100644
--- a/docs/janssen-server/auth-server/oauth-features/dpop.md
+++ b/docs/janssen-server/auth-server/oauth-features/dpop.md
@@ -102,12 +102,14 @@ recommended in the
Following properties of the Janssen Server can be used to tailor the behavior concerning DPoP.
-- [dpopJtiCacheTime](https://docs.jans.io/head/admin/reference/json/properties/janssenauthserver-properties/#dpopjticachetime)
-- [dpopSigningAlgValuesSupported](https://docs.jans.io/head/admin/reference/json/properties/janssenauthserver-properties/#dpopsigningalgvaluessupported)
-- [dpopTimeframe](https://docs.jans.io/head/admin/reference/json/properties/janssenauthserver-properties/#dpoptimeframe)
-- [dpopUseNonce](https://docs.jans.io/head/admin/reference/json/properties/janssenauthserver-properties/#dpopusenonce)
-- [dpopNonceCacheTime](https://docs.jans.io/head/admin/reference/json/properties/janssenauthserver-properties/#dpopnoncecachetime)
-- [dpopJktForceForAuthorizationCode]((https://docs.jans.io/head/admin/reference/json/properties/janssenauthserver-properties/#dpopjktforceforauthorizationcode))
+- [dpopJtiCacheTime](../../../janssen-server/reference/json/properties/janssenauthserver-properties.md#dpopjticachetime)
+- [dpopSigningAlgValuesSupported](../../../janssen-server/reference/json/properties/janssenauthserver-properties.md#dpopsigningalgvaluessupported)
+- [dpopTimeframe](../../../janssen-server/reference/json/properties/janssenauthserver-properties.md#dpoptimeframe)
+- [dpopUseNonce](../../../janssen-server/reference/json/properties/janssenauthserver-properties.md#dpopusenonce)
+- [dpopNonceCacheTime](../../../janssen-server/reference/json/properties/janssenauthserver-properties.md#dpopnoncecachetime)
+- [dpopJktForceForAuthorizationCode](../../../janssen-server/reference/json/properties/janssenauthserver-properties.md#dpopjktforceforauthorizationcode)
+
+
## Have questions in the meantime?
diff --git a/docs/janssen-server/config-guide/scim-config/user-config.md b/docs/janssen-server/config-guide/scim-config/user-config.md
index 131a7bba914..b38a3f7f819 100644
--- a/docs/janssen-server/config-guide/scim-config/user-config.md
+++ b/docs/janssen-server/config-guide/scim-config/user-config.md
@@ -454,6 +454,25 @@ To modify any user properties, find the user from search box and hit `Enter` to
![update-user](../../../assets/jans-tui-update-user.png)
+### Change User Password
+No chance to recover user password, but you can change.
+To change password of a user navigate/or search user and press key `p` when the target user is higlighted.
+In the figure below, passowrd of user **sakamura** is being changed.
+
+![Change User Password](../../../assets/tui-user-change-password.png)
+
+Once you write new password (it will be displayed while you type), go to button `< Save >` and press Enter.
+
+### Manage User FIDO Devices
+To view and manage users registered FIDO devices, first navigate/or search user and press key `f` on the keyboard.
+If user has any registered FIDO device, a popup will appears as in image below:
+
+![User FIDO Devices](../../../assets/tui-ser-fido-device-list.png)
+
+You can veiw details of a device by pressing Enter. To delete a device press key `d`, you will be
+prompted for confirmation.
+
+
### Delete User
To delete user, bring the control on the specific user row and press `delete` or `d` key from keyboard. It will show a pop-up for confirmation as below:
diff --git a/docs/janssen-server/developer/agama/faq.md b/docs/janssen-server/developer/agama/faq.md
index f5b763206c1..c7f85d29cf5 100644
--- a/docs/janssen-server/developer/agama/faq.md
+++ b/docs/janssen-server/developer/agama/faq.md
@@ -129,12 +129,6 @@ We plan to offer a debugger in the future. In the meantime, you can do `printf`-
## Miscellaneous
-### Does the engine support AJAX?
-
-If you require a flow with no page refreshes, it could be implemented using AJAX calls as long as they align to the [POST-REDIRECT-GET](./advanced-usages.md#flow-advance-and-navigation) pattern, where a form is submitted, and as response a 302/303 HTTP redirection is obtained. Your Javascript code must also render UI elements in accordance with the data obtained by following the redirect (GET). Also, care must be taken in order to process server errors, timeouts, etc. In general, this requires a considerable amount of effort.
-
-If you require AJAX to consume a resource (service) residing in the same domain of your server, there is no restriction - the engine is not involved. Interaction with external domains may require to setup CORS configuration appropriately in the authentication server.
-
### How to launch a flow?
A flow is launched by issuing an authentication request in a browser as explained [here](./jans-agama-engine.md#launching-flows).
@@ -195,3 +189,7 @@ Note the localization context (language, country, etc.) used in such a call is b
### Can Agama code be called from Java?
No. These two languages are supposed to play roles that should not be mixed, check [here](./agama-best-practices.md#about-flow-design).
+
+### How to run flows from native applications instead of web browsers?
+
+There is a separate doc page covering this aspect [here](./native-applications.md).
diff --git a/docs/janssen-server/developer/agama/native-applications.md b/docs/janssen-server/developer/agama/native-applications.md
new file mode 100644
index 00000000000..e0688606cb7
--- /dev/null
+++ b/docs/janssen-server/developer/agama/native-applications.md
@@ -0,0 +1,494 @@
+---
+tags:
+ - developer
+ - agama
+ - native apps
+ - challenge endpoint
+---
+
+# Agama flows in native applications
+
+Agama is a framework primarily focused on web flows, however, with the [Authorization Challenge](../../../script-catalog/authorization_challenge/authorization-challenge.md) endpoint of Jans Server, developers can now run their flows outside the browser. This makes possible to offer secure, multi-step authentication flows from desktop and mobile applications without resorting to mechanisms like Web Views that substantially degrade the user experience.
+
+Additionally, the same already-familiar tools for authoring and deploying Agama projects can be used for the job. Moreover, the flows built for the web can be run in the native world without modification, requiring only to code the respective native UI and the logic that interacts with the Authorization Challenge endpoint, called "the endpoint" hereafter.
+
+In this document, we present an overview of how the endpoint works to make your Agama flows run without a web browser. Preliminar acquaintance with the following topics is recommended:
+
+- Agama [DSL](../../../agama/introduction.md#dsl) and `.gama` [format](../../../agama/gama-format.md)
+- Agama projects [deployment](../../config-guide/auth-server-config/agama-project-configuration.md) in the Janssen Server
+- [Execution rules](../../../agama/execution-rules.md) in the Jans Agama [engine](./jans-agama-engine.md)
+- A basic understanding of [OAuth 2.0 for First-Party Applications](https://www.ietf.org/archive/id/draft-parecki-oauth-first-party-apps-02.html)
+
+## How do flows actually run?
+
+Before getting into the technicalities, let's cover some key preliminar concepts.
+
+The engine - the piece of software that actually runs flows - is eminently driven by HTTP requests. This is unsurprising because the main "consumers" of the engine are web browsers. When targetting native apps, the engine remains the same, and flows still run at the server side. This means native apps won't hold any business logic, or make computations of significance.
+
+The [RRF](../../../agama/language-reference.md#rrf) (render-reply-fetch) Agama instruction is of paramount importance in flows. In a regular web setting, it involves three steps:
+
+- Injecting some data to a UI template in order to generate HTML markup. This is known as _rendering_
+- Reply the markup to the web browser - this will display a web page
+- At the server side, retrieve data the user may have provided in his interaction with the page. This is, _fetch_
+
+In a native setting no HTML markup is suppossed to be generated and replied - it's the app that is in charge of displaying the UI now. For this purpose, it will receive (from the endpoint) the data that would be originally injected into the template. Most of times, this will carry information gathered at earlier stages of the flow and that is relevant to properly show or update the UI.
+
+Likewise, the "data submission" for the _fetch_ phase of RRF is performed by the app too. In this case, the relevant data grabbed from the user interaction is sent to the server side (via challenge endpoint) and becomes the result of the RRF (the value for the variable on the left-hand side of the instruction). Note both the input ("injected" data) and the output (result) is specified in JSON format.
+
+Once the _fetch_ occurs, the flow proceeds its execution until another RRF instruction is hit, where the procedure described above takes place again.
+
+Note this approach has two big benefits:
+
+1. Regular web flows can be reused in the native world without modifications
+1. The mindset for flows design remain the same
+
+There is a subtle exception regarding the first statement and has to do with flows containing RFAC instructions. [RFAC](../../../agama/language-reference.md#rfac) is used to redirect to external sites, and as such, it requires a web browser. In the case of native apps, flows will crash once an RFAC instruction is hit.
+
+### Inversion of control in apps
+
+The above concepts bring an important constraint to app design that should be accounted before undertaking any project: control is inverted.
+
+Normally, an app "knows" exactly what to do at each step of its workflow, and eventually delegates data retrieval tasks to the server side. When using the endpoint, the server side drives the logic: the app does not "take decisions" and instead "reacts" to the received data. This will be demostrated later through a practical example.
+
+## About the example: OTP via e-mail
+
+To avoid a rather abstract explanation, we'll use an example to illustrate the steps required to run a flow from a native app. Suppose an authentication flow operating in the following manner:
+
+- A username is prompted
+- If the corresponding user has no e-mail associated to his account, the flow ends with an error message
+- If the user has exactly one e-mail in his profile, a random one-time passcode (OTP) is sent to his registered address
+- If the user has more than one e-mail, a screen is shown to pick the address where he would like the OTP be sent to
+- The user is prompted to enter the passcode sent. If supplied correctly, the flow ends and the user is authenticated, otherwise the flow ends with an error
+
+This hypothetical flow is simple but will give you a good idea on how to interact with the endpoint.
+
+### The flow code
+
+The below depicts the implementation:
+
+![co.acme.flows.emailOtp](../../../assets/agama/challenge-flow.png)
+
+
+
+Flow `co.acme.flows.emailOtp` is self-explanatory and does not require further insight. Note the templates referenced in RRF directives don't necessarily have to exist, however, the template names will be included in the output of the endpoint as the flow executes. This serves as a hint or reference for the app to know the current point of execution and determine what should be shown in the UI. It will be more clearly seen in the next section.
+
+## Running the flow
+
+### Requisites
+
+To be able to run an Agama flow from a native app using the endpoint, it is required to register an OAuth Client in the Jans server with at least the `authorization_challenge` scope. The process of client registration is beyond the scope of this document.
+
+All HTTP requests exemplified here make use of `curl`. Ensure this tool is familiar to you.
+
+### Workflow
+
+Requests to the endpoint are all issued to the URL `https:///jans-auth/restv1/authorize-challenge` using the POST verb. Responses will contain JSON content whose structure will vary depending on the result of the operation as we will see.
+
+Once the first request is sent, the flow will start and all instructions will be executed until an RRF is found. Here the flow will be paused, and the endpoint will respond with the data that was passed to RRF: the template path and the "injected" data. Let's start issuing real requests now.
+
+### Initial request
+
+In the first request, at least the following parameters must be passed:
+
+|Name|Value|
+|-|-|
+|`acr_values`|agama_challenge|
+|`use_auth_session`|true|
+|`client_id`|The client identifier of a previously registered client|
+|`flow_name`|The qualified name of the flow to launch|
+
+So in our example, it may look like:
+
+```
+curl -i -d acr_values=agama_challenge -d use_auth_session=true
+ -d flow_name=co.acme.flows.emailOtp -d client_id=
+ https:///jans-auth/restv1/authorize-challenge
+```
+
+!!! Note
+ This command, as all others following has been split into several lines for better readability.
+
+The response will look like:
+
+```
+HTTP/1.1 401 Unauthorized
+Content-Type: application/json
+...
+
+{
+ "error": "flow_paused"
+ "flow_paused": {
+ "_template": "username-prompt.ftl"
+ },
+ "auth_session": "BmAiCeArLdAa0",
+}
+```
+
+While this may look like something wrong happened, it is not really the case. This is derived from the spec the endpoint adheres to, where the authorization server must report every intermediate response as an error with a 401 status code.
+
+The value of the `error` property references a section that contains the template path. Here it corresponds to the first RRF instruction reached in the execution (line 4 in the flow's code). Particularly this RRF was not invoked passing two parameters, so there is only one property inside the `flow_paused` JSON object.
+
+Note the presence of `auth_session`. This value allows the authorization server to associate subsequent requests issued by the app with this specific flow execution.
+
+Based on this response, the app should render UI elements in order to capture the username. Here, `username-prompt.ftl` serves as a hint for the app to know the point of execution the flow is at currently.
+
+### Subsequent requests
+
+From here onwards, requests must contain the following parameters:
+
+|Name|Value|
+|-|-|
+|`use_auth_session`|true|
+|`auth_session`|The value obtained in the previous request|
+|`data`|A JSON object value which will become the result of the RRF instruction the flow is paused at|
+
+!!! Note
+ Whenever a request is missing the `auth_session` param, it is assumed the [inital request](#initial-request) is being attempted.
+
+Let's assume the user entered `Joan` as username in the app. A request like the below can then be issued so the variable `obj` at line 4 is assigned a value:
+
+```
+curl -i -d auth_session=BmAiCeArLdAa0 -d use_auth_session=true
+ --data-urlencode data='{ "username": "Joan" }'
+ https:///jans-auth/restv1/authorize-challenge
+```
+
+This will make the flow advance until the next RRF is reached. Suppose the user Joan was found to have two e-mail addresses: `joan@doe.com` and `joan@deere.com`. This will make the flow hit line 23. The response will look as follows:
+
+```
+HTTP/1.1 401 Unauthorized
+Content-Type: application/json
+...
+
+{
+ "error": "flow_paused"
+ "flow_paused": {
+ "_template": "email-prompt.ftl",
+ "addresses": [ "joan@doe.com", "joan@deere.com" ]
+ },
+ "auth_session": "BmAiCeArLdAa0",
+}
+```
+
+Note the `flow_paused` section has the contents of the object prepared in line 22.
+
+Based on this response, now the app should show a selection list for the user to pick one of these addresses. Once the selection is made, a new request can be issued:
+
+```
+curl -i -d auth_session=BmAiCeArLdAa0 -d use_auth_session=true
+ --data-urlencode data='{ "email": "joan@doe.com" }'
+ https:///jans-auth/restv1/authorize-challenge
+```
+
+The flow will continue and the hypothetical message will be sent to `joan@doe.com` (line 28). Then the next RRF is reached (line 32) and we get as response:
+
+```
+HTTP/1.1 401 Unauthorized
+Content-Type: application/json
+...
+
+{
+ "error": "flow_paused"
+ "flow_paused": {
+ "_template": "passcode-prompt.ftl"
+ },
+ "auth_session": "BmAiCeArLdAa0",
+}
+```
+
+The app must now update the UI so the passcode is prompted. When ready, a new request comes:
+
+```
+curl -i -d auth_session=BmAiCeArLdAa0 -d use_auth_session=true
+ --data-urlencode data='{ "otp": "123456" }'
+ https:///jans-auth/restv1/authorize-challenge
+```
+
+Assuming the entered code (123456) was correct, the response would look like:
+
+```
+HTTP/1.1 401 Unauthorized
+Content-Type: application/json
+...
+
+{
+ "error": "flow_finished",
+ "flow_finished": {
+ "data": { "userId": "Joan" },
+ "success": true
+ },
+ "auth_session": "efb10525-6c43-4e50-88ab-92461c258526"
+}
+```
+
+This means we have hit line 37.
+
+When a `Finish` instruction is reached it is fully executed and the error reported in the response changes to `flow_finished`. What is left now is binding the user identified by `userId` (Joan) to the authorization request we have been handling (`BmAiCeArLdAa0`). This is how the user actually gets authenticated.
+
+### Final request
+
+To authenticate the user, we issue one last request:
+
+```
+curl -i -d auth_session=BmAiCeArLdAa0 -d use_auth_session=true
+ https:///jans-auth/restv1/authorize-challenge
+```
+
+Note parameter `data` is not needed. As response we obtain:
+
+```
+HTTP/1.1 200 OK
+Content-Type: application/json
+...
+
+{ "authorization_code" : "SplxlOBeZQQYbYS6WxSbIA" }
+
+```
+
+Once an authorization code has been obtained, the app can request an access token. This topic is beyond the scope of this document.
+
+At this point, the app can update the UI giving the user access to the actual app contents. No more requests are expected to be received by the endpoint with the given `auth_session` value.
+
+## Understanding errors
+
+So far we have been following the "happy" path in the example flow where all assumptions are met. This is unrealistic so here we offer an overview of how the endpoint behaves when abnormal conditions come up.
+
+!!! Note
+ In this section, we stick to the terminology found [here](../../../agama/execution-rules.md#flows-lifecycle).
+
+### Missing parameters
+
+Assume the following request is issued:
+
+```
+curl -i -d use_auth_session=true -d acr_values=agama_challenge -d client_id=
+ https:///jans-auth/restv1/authorize-challenge
+```
+
+This lacks the name of the flow to launch. The response is:
+
+```
+HTTP/1.1 400 Bad Request
+Content-Type: application/json
+...
+
+{
+ "error": "missing_param",
+ "missing_param": { "description": "Parameter 'flow_name' missing in request" }
+}
+```
+
+### Failed flows
+
+Many times, flows simply fail as a way to reject access. This is achived in Agama by using code like:
+
+```
+obj = { success: false, error: "You are too suspicious" }
+Finish obj
+```
+
+In this case, the response looks like:
+
+```
+HTTP/1.1 401 Unauthorized
+Content-Type: application/json
+...
+
+{
+ "error": "flow_finished",
+ "flow_finished": {
+ "success": false,
+ "error": "You are too suspicious"
+ }
+}
+```
+
+Note `auth_session` is not replied. As such, no more requests to the endpoint should be made passing the `auth_session` value obtained earlier.
+
+### Engine errors
+
+There are several conditions under which the engine produces errors. In these cases, the HTTP error emitted by the engine is included in the endpoint response. As in previous error scenarios, no `auth_session` is replied.
+
+#### Flow timeout
+
+With native apps, [timeout](./jans-agama-engine.md#how-timeouts-work) of flows obeys the same rules of the web scenario. The only difference is the server property employed for the timeout calculation, namely, `authorizationChallengeSessionLifetimeInSeconds`. If absent, it defaults to one day.
+
+Here is how a flow timeout is reported:
+
+```
+HTTP/1.1 500 Server Error
+Content-Type: application/json
+...
+
+{
+ "error": "engine_error",
+ "engine_error": {
+ "description": "Unexpected response to https:///jans-auth/fl/...",
+ "body": {
+ "message": "You have exceeded the amount of time required to complete your authentication",
+ "timeout": true
+ },
+ "contentType": "application/json",
+ "status": 410
+ }
+}
+```
+
+#### Crashed flow
+
+When a flow crashes, the error is reported in similar way the timeout is reported. Here are some examples:
+
+1. An attempt to access a property or index of a `null` variable in Agama code
+
+ ```
+ HTTP/1.1 500 Server Error
+ Content-Type: application/json
+ ...
+
+ {
+ "error": "engine_error",
+ "engine_error": {
+ "description": "Unexpected response to https:///jans-auth/fl/...",
+ "body": {
+ "title": "An unexpected error ocurred",
+ "message": "TypeError: Cannot read property \"x\" from null"
+ },
+ "contentType": "application/json",
+ "status": 500
+ }
+ }
+ ```
+
+1. A variable does not meet the expected shape for a given Agama directive
+
+ ```
+ HTTP/1.1 500 Server Error
+ Content-Type: application/json
+ ...
+
+ {
+ "error": "engine_error",
+ "engine_error": {
+ "description": "Unexpected response to https:///jans-auth/fl/...",
+ "body": {
+ "title": "An unexpected error ocurred",
+ "message": "TypeError: Data passed to RRF was not a map or Java equivalent"
+ },
+ "contentType": "application/json",
+ "status": 500
+ }
+ }
+ ```
+
+1. Indexing a string in Java beyond length
+
+ ```
+ HTTP/1.1 500 Server Error
+ Content-Type: application/json
+ ...
+
+ {
+
+ "error": "engine_error",
+ "engine_error": {
+ "description": "Unexpected response to https:///jans-auth/fl/...",
+ "body": {
+ "title": "An unexpected error ocurred",
+ "message": "String index out of range: 100"
+ },
+ "contentType": "application/json",
+ "status": 500
+ }
+ }
+ ```
+
+### Other errors
+
+There are a variety of miscelaneous errors. Here we describe the most common.
+
+#### Finished flows with problems of user identification
+
+When a `Finish` instruction does not include a reference to a user identifier, or if the referenced user does not exist, the endpoint responds like:
+
+```
+HTTP/1.1 500 Server Error
+Content-Type: application/json
+...
+
+{
+ "error": "unexpected_error",
+ "unexpected_error": { "description": "Unable to determine identity of user" }
+}
+```
+
+#### Attempt to launch an unknown flow
+
+If the initial request references an inexisting flow or one that has been flagged as [not launchable directly](../../../agama/gama-format.md#metadata) by clients.
+
+```
+HTTP/1.1 500 Server Error
+Content-Type: application/json
+...
+
+{
+ "unexpected_error": {"description": "Flow ... does not exist or cannot be launched an application"},
+ "error": "unexpected_error"
+}
+```
+
+#### Agama is disabled
+
+If the Agama engine is disabled, the following is generated upon the first request:
+
+```
+HTTP/1.1 500 Server Error
+Content-Type: application/json
+...
+
+{
+ "error": "unexpected_error",
+ "unexpected_error": { "description": "Agama engine is disabled" }
+}
+```
diff --git a/docs/janssen-server/developer/agama/quick-start-using-agama-lab.md b/docs/janssen-server/developer/agama/quick-start-using-agama-lab.md
index 57d0a9597f6..df56dce9e18 100644
--- a/docs/janssen-server/developer/agama/quick-start-using-agama-lab.md
+++ b/docs/janssen-server/developer/agama/quick-start-using-agama-lab.md
@@ -387,7 +387,7 @@ Server deployment
## Test
-1. [Setup](https://github.com/JanssenProject/jans/tree/main/demos/jans-tent) Janssen Tent
+1. [Setup](https://github.com/JanssenProject/jans/tree/v1.2.0/demos/jans-tent) Janssen Tent
2. Change the configuration as given below in `config.py`
```
diff --git a/docs/janssen-server/install/vm-install/rhel.md b/docs/janssen-server/install/vm-install/rhel.md
index 8c4709aef3b..730d2c26989 100644
--- a/docs/janssen-server/install/vm-install/rhel.md
+++ b/docs/janssen-server/install/vm-install/rhel.md
@@ -41,7 +41,7 @@ sudo rpm -import automation-jans-public-gpg.asc
[Releases](https://github.com/JanssenProject/jans/releases)
```
-wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans-replace-janssen-version-stable.el8.x86_64.rpm -P ~/
+wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans-replace-janssen-version.el8.x86_64.rpm -P ~/
```
- Verify integrity of the downloaded package using published `sha256sum`.
@@ -49,7 +49,7 @@ wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-v
Download `sha256sum` file for the package
```shell
- wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans-replace-janssen-version-stable.el8.x86_64.rpm.sha256sum -P ~/
+ wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans-replace-janssen-version.el8.x86_64.rpm.sha256sum -P ~/
```
Check the hash if it is matching.
diff --git a/docs/janssen-server/install/vm-install/suse.md b/docs/janssen-server/install/vm-install/suse.md
index d5768e4ae78..4f08242ea54 100644
--- a/docs/janssen-server/install/vm-install/suse.md
+++ b/docs/janssen-server/install/vm-install/suse.md
@@ -53,7 +53,7 @@ sudo rpm -import automation-jans-public-gpg.asc
[Releases](https://github.com/JanssenProject/jans/releases)
```shell
-wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans-replace-janssen-version-stable.suse15.x86_64.rpm
+wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans-replace-janssen-version.suse15.x86_64.rpm
```
- Verify integrity of the downloaded package using published `sha256sum`.
@@ -61,7 +61,7 @@ wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-v
Download `sha256sum` file for the package
```shell
-wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans-replace-janssen-version-stable.suse15.x86_64.rpm.sha256sum
+wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans-replace-janssen-version.suse15.x86_64.rpm.sha256sum
```
Check the hash if it is matching. You may need to change your working directory
diff --git a/docs/janssen-server/install/vm-install/ubuntu.md b/docs/janssen-server/install/vm-install/ubuntu.md
index 1fb6963db3c..1db461aa491 100644
--- a/docs/janssen-server/install/vm-install/ubuntu.md
+++ b/docs/janssen-server/install/vm-install/ubuntu.md
@@ -38,7 +38,7 @@ sudo gpg --import automation-jans-public-gpg.asc;
[Releases](https://github.com/JanssenProject/jans/releases)
```
-wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans_replace-janssen-version-stable.ubuntu22.04_amd64.deb -P /tmp
+wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans_replace-janssen-version.ubuntu22.04_amd64.deb -P /tmp
```
- Verify integrity of the downloaded package by verifying published `sha256sum`.
@@ -46,7 +46,7 @@ wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-v
Download `sha256sum` file for the package
```shell
- wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans_replace-janssen-version-stable.ubuntu22.04_amd64.deb.sha256sum -P /tmp
+ wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans_replace-janssen-version.ubuntu22.04_amd64.deb.sha256sum -P /tmp
```
Check the hash if it is matching.
@@ -74,7 +74,7 @@ sudo apt install ./jans_replace-janssen-version.ubuntu22.04_amd64.deb
[Releases](https://github.com/JanssenProject/jans/releases)
```
-wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans_replace-janssen-version-stable.ubuntu20.04_amd64.deb -P /tmp
+wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans_replace-janssen-version.ubuntu20.04_amd64.deb -P /tmp
```
- Verify integrity of the downloaded package by verifying published `sha256sum`.
@@ -82,7 +82,7 @@ wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-v
Download `sha256sum` file for the package
```shell
- wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans_replace-janssen-version-stable.ubuntu20.04_amd64.deb.sha256sum -P /tmp
+ wget https://github.com/JanssenProject/jans/releases/download/vreplace-janssen-version/jans_replace-janssen-version.ubuntu20.04_amd64.deb.sha256sum -P /tmp
```
Check the hash if it is matching.
diff --git a/docs/janssen-server/link/jans-kc-link.md b/docs/janssen-server/link/jans-kc-link.md
deleted file mode 100644
index be86886c5d9..00000000000
--- a/docs/janssen-server/link/jans-kc-link.md
+++ /dev/null
@@ -1,105 +0,0 @@
----
-tags:
- - administration
- - link
- - keycloak
----
-
-# Jans Keycloak Link
-
-The Jans Keycloak Link is a [Jans Link](README.md) module that provides
-synchronization services to update the Janssen User Store from an external
-Keycloak instance.
-
-Jans Keycloak Link accesses Keycloak data via Keycloak API. A new
-client needs to be created on Keycloak in order to authorize Jans Keycloak Link
-for API access. The client can be configured to use one of the two
-authentication mechanisms:
-
-- [Client Credentials Grant](#using-client-credentials-grant)
-- [Resource Owner Password Credentials Grant](#using-resource-owner-password-credentials-grant)
-
-## Using Client Credentials Grant
-
-### Create Client on Keycloak
-
-- Create a new OpenId Connect client from Keycloak administration console
-- Configure this client as having `confidential` access type by enabling `client
- authentication`
-- Enable `Service Accounts Enabled` flag, which enables client credentials grant
- ![](../../assets/jans-kc-link-client-2.png)
-- Go to the tab `Service accounts roles`, assign role `admin` to the client using
- `Assign role` button
- ![](../../assets/jans-kc-link-client-4.png)
-- Keep a note of the client ID and client secret. This detail will be required
- to be added to the Janssen server
-
-### Configure Jans Keycloak Link Module
-
-On the Janssen server, Jans Keycloak Link module configuration needs to be
-updated to be able to connect with Keycloak server.
-
-- Using [TUI](../config-guide/config-tools/jans-tui/README.md), update the
- Jans KC Link module configuration. Navigate to
- `Jans KC Link` -> `Keycloak Configuration`, and configure following
- parameters:
- - `Server URL`: Keycloak Server URL
- - `Realm`: Keycloak Realm
- - `Client ID`: ID of the newly created client on Keycloak
- - `Client Secret`: Client secret of the Keycloak client
- - `Grant Type`: Set this as _client_credentials_
-
- ![](../../assets/tui-kc-link-kc-config-client-cred.png)
-- [Test](#test-the-integration) the integration
-
-## Using Resource Owner Password Credentials Grant
-
-!!! Note
-Use of this grant type is generally discouraged and [removed from OAuth
-2.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#name-differences-from-oauth-20).
-
-### Configure Client on Keycloak
-
-- Create a new OpenId Connect client from Keycloak administration console
-- Configure this client as having `direct access grant`
- ![](../../assets/jans-kc-link-client-3.png)
-- Create a user in the Keycloak server. The user should have permission to
- access Keycloak API in the Keycloak. For the instructions in this document,
- We will use the default Keycloak user which is `admin`.
-
-### Configure Jans Keycloak Link Module
-
-On the Janssen server, Jans Keycloak Link module configuration needs to be
-updated to be able to connect with Keycloak server.
-
-- Using [TUI](../config-guide/config-tools/jans-tui/README.md), update the
- Jans KC Link module configuration. Navigate to
- `Jans KC Link` -> `Keycloak Configuration`, and configure following
- parameters:
- - `Server URL`: Keycloak Server URL
- - `Realm`: Keycloak Realm
- - `Client ID`: ID of the newly created client on Keycloak
- - `Grant Type`: Set this as _password_
- - `Username`: Set this as _admin_
- - `Password`: Password of _admin_ user
-
- ![](../../assets/tui-kc-link-kc-config-ropc.png)
-- [Test](#test-the-integration) the integration
-
-## Test The Integration
-
-To check if the integration is working, you can create a user on Keycloak server.
-This user should reflect in Janssen Server after the polling interval has passed.
-
-![](../../assets/jans-kc-link-user-create.png)
-
-Use [TUI](../config-guide/config-tools/jans-tui/README.md) to see the list of
-available users in Janssen Server.
-
-![](../../assets/jans-kc-link-user-in-jans.png)
-
-## Want to contribute?
-
-If you have content you'd like to contribute to this page in the meantime,
-you can get started with
-our [Contribution guide](https://docs.jans.io/head/CONTRIBUTING/).
diff --git a/docs/janssen-server/link/jans-keycloak-link.md b/docs/janssen-server/link/jans-keycloak-link.md
index 528f5ae2c94..be86886c5d9 100644
--- a/docs/janssen-server/link/jans-keycloak-link.md
+++ b/docs/janssen-server/link/jans-keycloak-link.md
@@ -5,15 +5,101 @@ tags:
- keycloak
---
+# Jans Keycloak Link
-## This content is in progress
+The Jans Keycloak Link is a [Jans Link](README.md) module that provides
+synchronization services to update the Janssen User Store from an external
+Keycloak instance.
-The Janssen Project documentation is currently in development. Topic pages are being created in order of broadest relevance, and this page is coming in the near future.
+Jans Keycloak Link accesses Keycloak data via Keycloak API. A new
+client needs to be created on Keycloak in order to authorize Jans Keycloak Link
+for API access. The client can be configured to use one of the two
+authentication mechanisms:
-## Have questions in the meantime?
+- [Client Credentials Grant](#using-client-credentials-grant)
+- [Resource Owner Password Credentials Grant](#using-resource-owner-password-credentials-grant)
-While this documentation is in progress, you can ask questions through [GitHub Discussions](https://github.com/JanssenProject/jans/discussions) or the [community chat on Gitter](https://gitter.im/JanssenProject/Lobby). Any questions you have will help determine what information our documentation should cover.
+## Using Client Credentials Grant
+
+### Create Client on Keycloak
+
+- Create a new OpenId Connect client from Keycloak administration console
+- Configure this client as having `confidential` access type by enabling `client
+ authentication`
+- Enable `Service Accounts Enabled` flag, which enables client credentials grant
+ ![](../../assets/jans-kc-link-client-2.png)
+- Go to the tab `Service accounts roles`, assign role `admin` to the client using
+ `Assign role` button
+ ![](../../assets/jans-kc-link-client-4.png)
+- Keep a note of the client ID and client secret. This detail will be required
+ to be added to the Janssen server
+
+### Configure Jans Keycloak Link Module
+
+On the Janssen server, Jans Keycloak Link module configuration needs to be
+updated to be able to connect with Keycloak server.
+
+- Using [TUI](../config-guide/config-tools/jans-tui/README.md), update the
+ Jans KC Link module configuration. Navigate to
+ `Jans KC Link` -> `Keycloak Configuration`, and configure following
+ parameters:
+ - `Server URL`: Keycloak Server URL
+ - `Realm`: Keycloak Realm
+ - `Client ID`: ID of the newly created client on Keycloak
+ - `Client Secret`: Client secret of the Keycloak client
+ - `Grant Type`: Set this as _client_credentials_
+
+ ![](../../assets/tui-kc-link-kc-config-client-cred.png)
+- [Test](#test-the-integration) the integration
+
+## Using Resource Owner Password Credentials Grant
+
+!!! Note
+Use of this grant type is generally discouraged and [removed from OAuth
+2.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#name-differences-from-oauth-20).
+
+### Configure Client on Keycloak
+
+- Create a new OpenId Connect client from Keycloak administration console
+- Configure this client as having `direct access grant`
+ ![](../../assets/jans-kc-link-client-3.png)
+- Create a user in the Keycloak server. The user should have permission to
+ access Keycloak API in the Keycloak. For the instructions in this document,
+ We will use the default Keycloak user which is `admin`.
+
+### Configure Jans Keycloak Link Module
+
+On the Janssen server, Jans Keycloak Link module configuration needs to be
+updated to be able to connect with Keycloak server.
+
+- Using [TUI](../config-guide/config-tools/jans-tui/README.md), update the
+ Jans KC Link module configuration. Navigate to
+ `Jans KC Link` -> `Keycloak Configuration`, and configure following
+ parameters:
+ - `Server URL`: Keycloak Server URL
+ - `Realm`: Keycloak Realm
+ - `Client ID`: ID of the newly created client on Keycloak
+ - `Grant Type`: Set this as _password_
+ - `Username`: Set this as _admin_
+ - `Password`: Password of _admin_ user
+
+ ![](../../assets/tui-kc-link-kc-config-ropc.png)
+- [Test](#test-the-integration) the integration
+
+## Test The Integration
+
+To check if the integration is working, you can create a user on Keycloak server.
+This user should reflect in Janssen Server after the polling interval has passed.
+
+![](../../assets/jans-kc-link-user-create.png)
+
+Use [TUI](../config-guide/config-tools/jans-tui/README.md) to see the list of
+available users in Janssen Server.
+
+![](../../assets/jans-kc-link-user-in-jans.png)
## Want to contribute?
-If you have content you'd like to contribute to this page in the meantime, you can get started with our [Contribution guide](https://docs.jans.io/head/CONTRIBUTING/).
\ No newline at end of file
+If you have content you'd like to contribute to this page in the meantime,
+you can get started with
+our [Contribution guide](https://docs.jans.io/head/CONTRIBUTING/).
diff --git a/docs/janssen-server/reference/kubernetes/config-secret-keys.md b/docs/janssen-server/reference/kubernetes/config-secret-keys.md
index d6a91c77504..e24d4e504b5 100644
--- a/docs/janssen-server/reference/kubernetes/config-secret-keys.md
+++ b/docs/janssen-server/reference/kubernetes/config-secret-keys.md
@@ -9,7 +9,10 @@ tags:
## Overview
-The `config` job creates a set of configuration (contains `secrets` and `configmaps`) used by all Janssen services.
+The `config` job creates a set of configurations (contains `secrets` and `configmaps`) used by all Janssen services.
+
+!!! Note
+ We assume Janssen is installed in a namespace called `jans`
## Configmaps
@@ -27,7 +30,7 @@ Note that each key in configmaps is based on the schema below:
{
"city": {
"type": "string",
- "description": "Locality name (.e.g city)",
+ "description": "Locality name (e.g. city)",
"example": "Austin"
},
"country_code": {
@@ -502,10 +505,8 @@ Note that each key in secrets is based on the schema below:
## Example decoding secrets
### Opening `base64-decoded` secrets
-!!! Note
- We assume Jans is installed in a namespace called `jans`
-1. Get the `tls-certificate` from backend secret
+1. Get the `tls-certificate` from the backend secret
```bash
kubectl get secret tls-certificate -n jans -o yaml
@@ -525,17 +526,22 @@ Note that each key in secrets is based on the schema below:
## Using Configuration Schema
-As mentioned earlier, the `config` job creates configuration. Behind the scene, a Kubernetes' Secret object is created during the deployment to pre-populate `secrets` and `configmaps`.
+As mentioned earlier, the `config` job creates a set of configurations.
-### Default configuration
+This happens by using a Kubernetes secret named `-configuration-file` that gets created during the helm chart installation.
+
+It contains a JSON schema with the necessary `secrets` and `configmaps` to install Janssen services.
+
+This secret is then mounted by the `config` job.
-By default, the configuration only contains necessary `secrets` and `configmaps` to install Jans services.
+
+### Default configuration
```yaml
apiVersion: v1
kind: Secret
metadata:
- name: jans-configuration-file
+ name: janssen-configuration-file
namespace: jans
labels:
APP_NAME: configurator
@@ -563,7 +569,7 @@ stringData:
}
```
-Note that `_secret` may contain other keys depending on persistence, secrets/configmaps backend, etc. See examples below:
+Note that `_secret` may contain other keys depending on the persistence used, the backend of the secrets/configmaps, etc. For example:
1. Secrets/configmaps backend is set to `google`:
@@ -594,23 +600,22 @@ Note that `_secret` may contain other keys depending on persistence, secrets/con
### Custom configuration
-The default configuration is sufficient for most of the time. If there's a requirement to use custom or reusing existing configuration, user may create a custom Kubernetes object.
+The default configuration schema is sufficient for most of the time. However, if there's a requirement to use a custom configuration or reusing an existing configuration, you can create a Kubernetes secret with the custom configuration schema.
!!! Warning
The custom configuration schema is a BETA feature.
-1. Prepare YAML file:
+1. Prepare the YAML file containing the custom configuration schema. We will name it `custom-configuration-schema.yaml`:
```yaml
- # custom-configuration-schema.yaml
apiVersion: v1
kind: Secret
metadata:
- name: custom-configuration-file
+ name: custom-configuration-schema
namespace: jans
type: Opaque
stringData:
- custom-configuration.json: |-
+ configuration.json: |-
{
"_configmap": {
"hostname": "demoexample.jans.io",
@@ -628,19 +633,69 @@ The default configuration is sufficient for most of the time. If there's a requi
}
```
-1. Create Kubernetes secrets:
+1. Create the Kubernetes secret:
```bash
- kubernetes -n jans create secret generic custom-configuration-schema --from-file=custom-configuration.json
+ kubectl -n jans apply -f custom-configuration-schema.yaml
```
1. Specify the secret in `values.yaml`:
```yaml
global:
- cnConfiguratorConfigurationFile: /etc/jans/conf/custom-configuration.json
cnConfiguratorCustomSchema:
secretName: custom-configuration-schema
```
-1. Install the Jans charts.
+1. Install the Janssen helm chart.
+
+## Encrypting Configuration Schema
+
+The encryption uses [Helm-specific](https://helm.sh/docs/chart_template_guide/function_list/#encryptaes) implementation of AES-256 CBC mode.
+
+### Default configuration
+
+The [default configuration](#default-configuration) schema can be encrypted by specifying 32 alphanumeric characters to `cnConfiguratorKey` attribute (the default value is an empty string).
+
+```yaml
+global:
+ cnConfiguratorKey: "VMtVyFha8CfppdDGQSw8zEnfKXRvksAD"
+```
+
+The following example is what an encrypted default configuration looks like:
+
+```yaml
+apiVersion: v1
+kind: Secret
+metadata:
+ name: janssen-configuration-file
+ namespace: jans
+stringData:
+ configuration.json: |-
+ sxySo+redacted+generated+by+helm/TNpE5PoUR2+JxXiHiLq8X5ibexJcfjAN0fKlqRvU=
+```
+
+### Custom configuration
+
+If you are using a [custom configuration](#custom-configuration) schema, you will need to generate the string using [sprig-aes](https://pypi.org/project/sprig-aes/) CLI and paste it into a YAML manifest.
+
+```yaml
+# custom-configuration-schema.yaml
+apiVersion: v1
+kind: Secret
+metadata:
+ name: custom-configuration-schema
+ namespace: jans
+type: Opaque
+stringData:
+ configuration.json: |-
+ sxySo+redacted+generated+by+sprigaes+JxXiHiLq8X5ibexJcfjAN0fKlqRvU=
+```
+
+Add the `key` used when encrypting using sprig-aes.
+
+```yaml
+global:
+ cnConfiguratorKey: "VMtVyFha8CfppdDGQSw8zEnfKXRvksAD"
+```
+
diff --git a/docs/janssen-server/reference/kubernetes/docker-jans-certmanager.md b/docs/janssen-server/reference/kubernetes/docker-jans-certmanager.md
index adb12653d6a..f835942ca00 100644
--- a/docs/janssen-server/reference/kubernetes/docker-jans-certmanager.md
+++ b/docs/janssen-server/reference/kubernetes/docker-jans-certmanager.md
@@ -186,7 +186,7 @@ spec:
spec:
containers:
- name: auth-key-rotation
- image: ghcr.io/janssenproject/jans/certmanager:0.0.0-nightly
+ image: ghcr.io/janssenproject/jans/certmanager:1.3.0-1
resources:
requests:
memory: "300Mi"
diff --git a/docs/janssen-server/reference/kubernetes/helm-chart.md b/docs/janssen-server/reference/kubernetes/helm-chart.md
index 44a994a6b75..45a843062a3 100644
--- a/docs/janssen-server/reference/kubernetes/helm-chart.md
+++ b/docs/janssen-server/reference/kubernetes/helm-chart.md
@@ -6,7 +6,7 @@ tags:
---
# janssen
-![Version: 0.0.0-nightly](https://img.shields.io/badge/Version-0.0.0--nightly-informational?style=flat-square) ![AppVersion: 0.0.0-nightly](https://img.shields.io/badge/AppVersion-0.0.0--nightly-informational?style=flat-square)
+![Version: 1.3.0](https://img.shields.io/badge/Version-1.3.0-informational?style=flat-square) ![AppVersion: 1.3.0](https://img.shields.io/badge/AppVersion-1.3.0-informational?style=flat-square)
Janssen Access and Identity Management Microservices Chart. This chart deploys each janssen microservice as a separate deployment.
@@ -29,26 +29,26 @@ Kubernetes: `>=v1.22.0-0`
| Repository | Name | Version |
|------------|------|---------|
-| | auth-server | 0.0.0-nightly |
-| | auth-server-key-rotation | 0.0.0-nightly |
-| | casa | 0.0.0-nightly |
-| | cn-istio-ingress | 0.0.0-nightly |
-| | config | 0.0.0-nightly |
-| | config-api | 0.0.0-nightly |
-| | fido2 | 0.0.0-nightly |
-| | kc-scheduler | 0.0.0-nightly |
-| | link | 0.0.0-nightly |
-| | nginx-ingress | 0.0.0-nightly |
-| | persistence | 0.0.0-nightly |
-| | saml | 0.0.0-nightly |
-| | scim | 0.0.0-nightly |
+| | auth-server | 1.3.0 |
+| | auth-server-key-rotation | 1.3.0 |
+| | casa | 1.3.0 |
+| | cn-istio-ingress | 1.3.0 |
+| | config | 1.3.0 |
+| | config-api | 1.3.0 |
+| | fido2 | 1.3.0 |
+| | kc-scheduler | 1.3.0 |
+| | link | 1.3.0 |
+| | nginx-ingress | 1.3.0 |
+| | persistence | 1.3.0 |
+| | saml | 1.3.0 |
+| | scim | 1.3.0 |
## Values
| Key | Type | Default | Description |
|-----|------|---------|-------------|
-| auth-server | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","hpa":{"behavior":{},"enabled":true,"maxReplicas":10,"metrics":[],"minReplicas":1,"targetCPUUtilizationPercentage":50},"image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/auth-server","tag":"0.0.0-nightly"},"lifecycle":{},"livenessProbe":{"exec":{"command":["python3","/app/scripts/healthcheck.py"]},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5},"pdb":{"enabled":true,"maxUnavailable":"90%"},"readinessProbe":{"exec":{"command":["python3","/app/scripts/healthcheck.py"]},"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5},"replicas":1,"resources":{"limits":{"cpu":"2500m","memory":"2500Mi"},"requests":{"cpu":"2500m","memory":"2500Mi"}},"topologySpreadConstraints":{},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | OAuth Authorization Server, the OpenID Connect Provider, the UMA Authorization Server--this is the main Internet facing component of Janssen. It's the service that returns tokens, JWT's and identity assertions. This service must be Internet facing. |
-| auth-server-key-rotation | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/certmanager","tag":"0.0.0-nightly"},"keysLife":48,"keysPushDelay":0,"keysPushStrategy":"NEWER","keysStrategy":"NEWER","lifecycle":{},"resources":{"limits":{"cpu":"300m","memory":"300Mi"},"requests":{"cpu":"300m","memory":"300Mi"}},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | Responsible for regenerating auth-keys per x hours |
+| auth-server | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","hpa":{"behavior":{},"enabled":true,"maxReplicas":10,"metrics":[],"minReplicas":1,"targetCPUUtilizationPercentage":50},"image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/auth-server","tag":"1.3.0-1"},"lifecycle":{},"livenessProbe":{"exec":{"command":["python3","/app/scripts/healthcheck.py"]},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5},"pdb":{"enabled":true,"maxUnavailable":"90%"},"readinessProbe":{"exec":{"command":["python3","/app/scripts/healthcheck.py"]},"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5},"replicas":1,"resources":{"limits":{"cpu":"2500m","memory":"2500Mi"},"requests":{"cpu":"2500m","memory":"2500Mi"}},"topologySpreadConstraints":{},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | OAuth Authorization Server, the OpenID Connect Provider, the UMA Authorization Server--this is the main Internet facing component of Janssen. It's the service that returns tokens, JWT's and identity assertions. This service must be Internet facing. |
+| auth-server-key-rotation | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/certmanager","tag":"1.3.0-1"},"keysLife":48,"keysPushDelay":0,"keysPushStrategy":"NEWER","keysStrategy":"NEWER","lifecycle":{},"resources":{"limits":{"cpu":"300m","memory":"300Mi"},"requests":{"cpu":"300m","memory":"300Mi"}},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | Responsible for regenerating auth-keys per x hours |
| auth-server-key-rotation.additionalAnnotations | object | `{}` | Additional annotations that will be added across the gateway in the format of {cert-manager.io/issuer: "letsencrypt-prod"} |
| auth-server-key-rotation.additionalLabels | object | `{}` | Additional labels that will be added across the gateway in the format of {mylabel: "myapp"} |
| auth-server-key-rotation.customCommand | list | `[]` | Add custom job's command. If passed, it will override the default conditional command. |
@@ -58,7 +58,7 @@ Kubernetes: `>=v1.22.0-0`
| auth-server-key-rotation.image.pullPolicy | string | `"IfNotPresent"` | Image pullPolicy to use for deploying. |
| auth-server-key-rotation.image.pullSecrets | list | `[]` | Image Pull Secrets |
| auth-server-key-rotation.image.repository | string | `"ghcr.io/janssenproject/jans/certmanager"` | Image to use for deploying. |
-| auth-server-key-rotation.image.tag | string | `"0.0.0-nightly"` | Image tag to use for deploying. |
+| auth-server-key-rotation.image.tag | string | `"1.3.0-1"` | Image tag to use for deploying. |
| auth-server-key-rotation.keysLife | int | `48` | Auth server key rotation keys life in hours |
| auth-server-key-rotation.keysPushDelay | int | `0` | Delay (in seconds) before pushing private keys to Auth server |
| auth-server-key-rotation.keysPushStrategy | string | `"NEWER"` | Set key selection strategy after pushing private keys to Auth server (only takes effect when keysPushDelay value is greater than 0) |
@@ -85,7 +85,7 @@ Kubernetes: `>=v1.22.0-0`
| auth-server.image.pullPolicy | string | `"IfNotPresent"` | Image pullPolicy to use for deploying. |
| auth-server.image.pullSecrets | list | `[]` | Image Pull Secrets |
| auth-server.image.repository | string | `"ghcr.io/janssenproject/jans/auth-server"` | Image to use for deploying. |
-| auth-server.image.tag | string | `"0.0.0-nightly"` | Image tag to use for deploying. |
+| auth-server.image.tag | string | `"1.3.0-1"` | Image tag to use for deploying. |
| auth-server.livenessProbe | object | `{"exec":{"command":["python3","/app/scripts/healthcheck.py"]},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5}` | Configure the liveness healthcheck for the auth server if needed. |
| auth-server.livenessProbe.exec | object | `{"command":["python3","/app/scripts/healthcheck.py"]}` | Executes the python3 healthcheck. https://github.com/JanssenProject/docker-jans-auth-server/blob/master/scripts/healthcheck.py |
| auth-server.pdb | object | `{"enabled":true,"maxUnavailable":"90%"}` | Configure the PodDisruptionBudget |
@@ -102,7 +102,7 @@ Kubernetes: `>=v1.22.0-0`
| auth-server.usrEnvs.secret | object | `{}` | Add custom secret envs to the service variable1: value1 |
| auth-server.volumeMounts | list | `[]` | Configure any additional volumesMounts that need to be attached to the containers |
| auth-server.volumes | list | `[]` | Configure any additional volumes that need to be attached to the pod |
-| casa | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","hpa":{"behavior":{},"enabled":true,"maxReplicas":10,"metrics":[],"minReplicas":1,"targetCPUUtilizationPercentage":50},"image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/casa","tag":"0.0.0-nightly"},"lifecycle":{},"livenessProbe":{"httpGet":{"path":"/jans-casa/health-check","port":"http-casa"},"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5},"pdb":{"enabled":true,"maxUnavailable":"90%"},"readinessProbe":{"httpGet":{"path":"/jans-casa/health-check","port":"http-casa"},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5},"replicas":1,"resources":{"limits":{"cpu":"500m","memory":"500Mi"},"requests":{"cpu":"500m","memory":"500Mi"}},"topologySpreadConstraints":{},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | Janssen Casa ("Casa") is a self-service web portal for end-users to manage authentication and authorization preferences for their account in a Janssen Auth Server. |
+| casa | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","hpa":{"behavior":{},"enabled":true,"maxReplicas":10,"metrics":[],"minReplicas":1,"targetCPUUtilizationPercentage":50},"image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/casa","tag":"1.3.0-1"},"lifecycle":{},"livenessProbe":{"httpGet":{"path":"/jans-casa/health-check","port":"http-casa"},"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5},"pdb":{"enabled":true,"maxUnavailable":"90%"},"readinessProbe":{"httpGet":{"path":"/jans-casa/health-check","port":"http-casa"},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5},"replicas":1,"resources":{"limits":{"cpu":"500m","memory":"500Mi"},"requests":{"cpu":"500m","memory":"500Mi"}},"topologySpreadConstraints":{},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | Janssen Casa ("Casa") is a self-service web portal for end-users to manage authentication and authorization preferences for their account in a Janssen Auth Server. |
| casa.additionalAnnotations | object | `{}` | Additional annotations that will be added across the gateway in the format of {cert-manager.io/issuer: "letsencrypt-prod"} |
| casa.additionalLabels | object | `{}` | Additional labels that will be added across the gateway in the format of {mylabel: "myapp"} |
| casa.customCommand | list | `[]` | Add custom pod's command. If passed, it will override the default conditional command. |
@@ -115,7 +115,7 @@ Kubernetes: `>=v1.22.0-0`
| casa.image.pullPolicy | string | `"IfNotPresent"` | Image pullPolicy to use for deploying. |
| casa.image.pullSecrets | list | `[]` | Image Pull Secrets |
| casa.image.repository | string | `"ghcr.io/janssenproject/jans/casa"` | Image to use for deploying. |
-| casa.image.tag | string | `"0.0.0-nightly"` | Image tag to use for deploying. |
+| casa.image.tag | string | `"1.3.0-1"` | Image tag to use for deploying. |
| casa.livenessProbe | object | `{"httpGet":{"path":"/jans-casa/health-check","port":"http-casa"},"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5}` | Configure the liveness healthcheck for casa if needed. |
| casa.livenessProbe.httpGet.path | string | `"/jans-casa/health-check"` | http liveness probe endpoint |
| casa.pdb | object | `{"enabled":true,"maxUnavailable":"90%"}` | Configure the PodDisruptionBudget |
@@ -133,8 +133,8 @@ Kubernetes: `>=v1.22.0-0`
| casa.usrEnvs.secret | object | `{}` | Add custom secret envs to the service variable1: value1 |
| casa.volumeMounts | list | `[]` | Configure any additional volumesMounts that need to be attached to the containers |
| casa.volumes | list | `[]` | Configure any additional volumes that need to be attached to the pod |
-| config | object | `{"additionalAnnotations":{},"additionalLabels":{},"adminPassword":"Test1234#","city":"Austin","configmap":{"cnAwsAccessKeyId":"","cnAwsDefaultRegion":"us-west-1","cnAwsProfile":"janssen","cnAwsSecretAccessKey":"","cnAwsSecretsEndpointUrl":"","cnAwsSecretsNamePrefix":"janssen","cnAwsSecretsReplicaRegions":[],"cnCacheType":"NATIVE_PERSISTENCE","cnConfigKubernetesConfigMap":"cn","cnGoogleProjectId":"google-project-to-save-config-and-secrets-to","cnGoogleSecretManagerServiceAccount":"SWFtTm90YVNlcnZpY2VBY2NvdW50Q2hhbmdlTWV0b09uZQo=","cnGoogleSecretNamePrefix":"janssen","cnGoogleSecretVersionId":"latest","cnJettyRequestHeaderSize":8192,"cnMaxRamPercent":"75.0","cnMessageType":"DISABLED","cnOpaUrl":"http://opa.opa.svc.cluster.cluster.local:8181/v1","cnPersistenceHybridMapping":"{}","cnRedisSentinelGroup":"","cnRedisSslTruststore":"","cnRedisType":"STANDALONE","cnRedisUrl":"redis.redis.svc.cluster.local:6379","cnRedisUseSsl":false,"cnScimProtectionMode":"OAUTH","cnSecretKubernetesSecret":"cn","cnSqlDbDialect":"mysql","cnSqlDbHost":"my-release-mysql.default.svc.cluster.local","cnSqlDbName":"jans","cnSqlDbPort":3306,"cnSqlDbSchema":"","cnSqlDbTimezone":"UTC","cnSqlDbUser":"jans","cnSqldbUserPassword":"Test1234#","cnVaultAddr":"http://localhost:8200","cnVaultAppRolePath":"approle","cnVaultKvPath":"secret","cnVaultNamespace":"","cnVaultPrefix":"jans","cnVaultRoleId":"","cnVaultRoleIdFile":"/etc/certs/vault_role_id","cnVaultSecretId":"","cnVaultSecretIdFile":"/etc/certs/vault_secret_id","cnVaultVerify":false,"kcAdminPassword":"Test1234#","kcAdminUsername":"admin","kcDbPassword":"Test1234#","kcDbSchema":"keycloak","kcDbUrlDatabase":"keycloak","kcDbUrlHost":"mysql.kc.svc.cluster.local","kcDbUrlPort":3306,"kcDbUrlProperties":"?useUnicode=true&characterEncoding=UTF-8&character_set_server=utf8mb4","kcDbUsername":"keycloak","kcDbVendor":"mysql","kcLogLevel":"INFO","lbAddr":"","quarkusTransactionEnableRecovery":true},"countryCode":"US","customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","email":"support@jans.io","image":{"pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/configurator","tag":"0.0.0-nightly"},"lifecycle":{},"orgName":"Janssen","redisPassword":"P@assw0rd","resources":{"limits":{"cpu":"300m","memory":"300Mi"},"requests":{"cpu":"300m","memory":"300Mi"}},"salt":"","state":"TX","usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | Configuration parameters for setup and initial configuration secret and config layers used by Janssen services. |
-| config-api | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","hpa":{"behavior":{},"enabled":true,"maxReplicas":10,"metrics":[],"minReplicas":1,"targetCPUUtilizationPercentage":50},"image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/config-api","tag":"0.0.0-nightly"},"lifecycle":{},"livenessProbe":{"httpGet":{"path":"/jans-config-api/api/v1/health/live","port":8074},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5},"pdb":{"enabled":true,"maxUnavailable":"90%"},"readinessProbe":{"httpGet":{"path":"jans-config-api/api/v1/health/ready","port":8074},"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5},"replicas":1,"resources":{"limits":{"cpu":"1000m","memory":"1200Mi"},"requests":{"cpu":"1000m","memory":"1200Mi"}},"topologySpreadConstraints":{},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | Config Api endpoints can be used to configure the auth-server, which is an open-source OpenID Connect Provider (OP) and UMA Authorization Server (AS). |
+| config | object | `{"additionalAnnotations":{},"additionalLabels":{},"adminPassword":"Test1234#","city":"Austin","configmap":{"cnAwsAccessKeyId":"","cnAwsDefaultRegion":"us-west-1","cnAwsProfile":"janssen","cnAwsSecretAccessKey":"","cnAwsSecretsEndpointUrl":"","cnAwsSecretsNamePrefix":"janssen","cnAwsSecretsReplicaRegions":[],"cnCacheType":"NATIVE_PERSISTENCE","cnConfigKubernetesConfigMap":"cn","cnGoogleProjectId":"google-project-to-save-config-and-secrets-to","cnGoogleSecretManagerServiceAccount":"SWFtTm90YVNlcnZpY2VBY2NvdW50Q2hhbmdlTWV0b09uZQo=","cnGoogleSecretNamePrefix":"janssen","cnGoogleSecretVersionId":"latest","cnJettyRequestHeaderSize":8192,"cnMaxRamPercent":"75.0","cnMessageType":"DISABLED","cnOpaUrl":"http://opa.opa.svc.cluster.cluster.local:8181/v1","cnPersistenceHybridMapping":"{}","cnRedisSentinelGroup":"","cnRedisSslTruststore":"","cnRedisType":"STANDALONE","cnRedisUrl":"redis.redis.svc.cluster.local:6379","cnRedisUseSsl":false,"cnScimProtectionMode":"OAUTH","cnSecretKubernetesSecret":"cn","cnSqlDbDialect":"mysql","cnSqlDbHost":"my-release-mysql.default.svc.cluster.local","cnSqlDbName":"jans","cnSqlDbPort":3306,"cnSqlDbSchema":"","cnSqlDbTimezone":"UTC","cnSqlDbUser":"jans","cnSqldbUserPassword":"Test1234#","cnVaultAddr":"http://localhost:8200","cnVaultAppRolePath":"approle","cnVaultKvPath":"secret","cnVaultNamespace":"","cnVaultPrefix":"jans","cnVaultRoleId":"","cnVaultRoleIdFile":"/etc/certs/vault_role_id","cnVaultSecretId":"","cnVaultSecretIdFile":"/etc/certs/vault_secret_id","cnVaultVerify":false,"kcAdminPassword":"Test1234#","kcAdminUsername":"admin","kcDbPassword":"Test1234#","kcDbSchema":"keycloak","kcDbUrlDatabase":"keycloak","kcDbUrlHost":"mysql.kc.svc.cluster.local","kcDbUrlPort":3306,"kcDbUrlProperties":"?useUnicode=true&characterEncoding=UTF-8&character_set_server=utf8mb4","kcDbUsername":"keycloak","kcDbVendor":"mysql","kcLogLevel":"INFO","lbAddr":"","quarkusTransactionEnableRecovery":true},"countryCode":"US","customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","email":"support@jans.io","image":{"pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/configurator","tag":"1.3.0-1"},"lifecycle":{},"orgName":"Janssen","redisPassword":"P@assw0rd","resources":{"limits":{"cpu":"300m","memory":"300Mi"},"requests":{"cpu":"300m","memory":"300Mi"}},"salt":"","state":"TX","usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | Configuration parameters for setup and initial configuration secret and config layers used by Janssen services. |
+| config-api | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","hpa":{"behavior":{},"enabled":true,"maxReplicas":10,"metrics":[],"minReplicas":1,"targetCPUUtilizationPercentage":50},"image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/config-api","tag":"1.3.0-1"},"lifecycle":{},"livenessProbe":{"httpGet":{"path":"/jans-config-api/api/v1/health/live","port":8074},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5},"pdb":{"enabled":true,"maxUnavailable":"90%"},"readinessProbe":{"httpGet":{"path":"jans-config-api/api/v1/health/ready","port":8074},"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5},"replicas":1,"resources":{"limits":{"cpu":"1000m","memory":"1200Mi"},"requests":{"cpu":"1000m","memory":"1200Mi"}},"topologySpreadConstraints":{},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | Config Api endpoints can be used to configure the auth-server, which is an open-source OpenID Connect Provider (OP) and UMA Authorization Server (AS). |
| config-api.additionalAnnotations | object | `{}` | Additional annotations that will be added across the gateway in the format of {cert-manager.io/issuer: "letsencrypt-prod"} |
| config-api.additionalLabels | object | `{}` | Additional labels that will be added across the gateway in the format of {mylabel: "myapp"} |
| config-api.customCommand | list | `[]` | Add custom pod's command. If passed, it will override the default conditional command. |
@@ -147,7 +147,7 @@ Kubernetes: `>=v1.22.0-0`
| config-api.image.pullPolicy | string | `"IfNotPresent"` | Image pullPolicy to use for deploying. |
| config-api.image.pullSecrets | list | `[]` | Image Pull Secrets |
| config-api.image.repository | string | `"ghcr.io/janssenproject/jans/config-api"` | Image to use for deploying. |
-| config-api.image.tag | string | `"0.0.0-nightly"` | Image tag to use for deploying. |
+| config-api.image.tag | string | `"1.3.0-1"` | Image tag to use for deploying. |
| config-api.livenessProbe | object | `{"httpGet":{"path":"/jans-config-api/api/v1/health/live","port":8074},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5}` | Configure the liveness healthcheck for the auth server if needed. |
| config-api.livenessProbe.httpGet | object | `{"path":"/jans-config-api/api/v1/health/live","port":8074}` | http liveness probe endpoint |
| config-api.pdb | object | `{"enabled":true,"maxUnavailable":"90%"}` | Configure the PodDisruptionBudget |
@@ -194,14 +194,14 @@ Kubernetes: `>=v1.22.0-0`
| config.configmap.cnSqlDbUser | string | `"jans"` | SQL database username. |
| config.configmap.cnSqldbUserPassword | string | `"Test1234#"` | SQL password injected the secrets . |
| config.configmap.cnVaultAddr | string | `"http://localhost:8200"` | Base URL of Vault. |
-| config.configmap.cnVaultAppRolePath | string | `"approle"` | Path to Vault AppRole. |
+| config.configmap.cnVaultAppRolePath | string | `"approle"` | Path to the Vault AppRole. |
| config.configmap.cnVaultKvPath | string | `"secret"` | Path to Vault KV secrets engine. |
| config.configmap.cnVaultNamespace | string | `""` | Vault namespace used to access the secrets. |
| config.configmap.cnVaultPrefix | string | `"jans"` | Base prefix name used to access secrets. |
| config.configmap.cnVaultRoleId | string | `""` | Vault AppRole RoleID. |
-| config.configmap.cnVaultRoleIdFile | string | `"/etc/certs/vault_role_id"` | Path to file contains Vault AppRole role ID. |
+| config.configmap.cnVaultRoleIdFile | string | `"/etc/certs/vault_role_id"` | Path to the file that contains Vault AppRole role ID. |
| config.configmap.cnVaultSecretId | string | `""` | Vault AppRole SecretID. |
-| config.configmap.cnVaultSecretIdFile | string | `"/etc/certs/vault_secret_id"` | Path to file contains Vault AppRole secret ID. |
+| config.configmap.cnVaultSecretIdFile | string | `"/etc/certs/vault_secret_id"` | Path to the file that contains Vault AppRole secret ID. |
| config.configmap.cnVaultVerify | bool | `false` | Verify connection to Vault. |
| config.configmap.kcAdminPassword | string | `"Test1234#"` | Keycloak admin UI password |
| config.configmap.kcAdminUsername | string | `"admin"` | Keycloak admin UI username |
@@ -224,7 +224,7 @@ Kubernetes: `>=v1.22.0-0`
| config.email | string | `"support@jans.io"` | Email address of the administrator usually. Used for certificate creation. |
| config.image.pullSecrets | list | `[]` | Image Pull Secrets |
| config.image.repository | string | `"ghcr.io/janssenproject/jans/configurator"` | Image to use for deploying. |
-| config.image.tag | string | `"0.0.0-nightly"` | Image tag to use for deploying. |
+| config.image.tag | string | `"1.3.0-1"` | Image tag to use for deploying. |
| config.orgName | string | `"Janssen"` | Organization name. Used for certificate creation. |
| config.redisPassword | string | `"P@assw0rd"` | Redis admin password if `config.configmap.cnCacheType` is set to `REDIS`. |
| config.resources | object | `{"limits":{"cpu":"300m","memory":"300Mi"},"requests":{"cpu":"300m","memory":"300Mi"}}` | Resource specs. |
@@ -239,7 +239,7 @@ Kubernetes: `>=v1.22.0-0`
| config.usrEnvs.secret | object | `{}` | Add custom secret envs to the service. variable1: value1 |
| config.volumeMounts | list | `[]` | Configure any additional volumesMounts that need to be attached to the containers |
| config.volumes | list | `[]` | Configure any additional volumes that need to be attached to the pod |
-| fido2 | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","hpa":{"behavior":{},"enabled":true,"maxReplicas":10,"metrics":[],"minReplicas":1,"targetCPUUtilizationPercentage":50},"image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/fido2","tag":"0.0.0-nightly"},"lifecycle":{},"livenessProbe":{"httpGet":{"path":"/jans-fido2/sys/health-check","port":"http-fido2"},"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5},"pdb":{"enabled":true,"maxUnavailable":"90%"},"readinessProbe":{"httpGet":{"path":"/jans-fido2/sys/health-check","port":"http-fido2"},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5},"replicas":1,"resources":{"limits":{"cpu":"500m","memory":"500Mi"},"requests":{"cpu":"500m","memory":"500Mi"}},"service":{"name":"http-fido2","port":8080},"topologySpreadConstraints":{},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | FIDO 2.0 (FIDO2) is an open authentication standard that enables leveraging common devices to authenticate to online services in both mobile and desktop environments. |
+| fido2 | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","hpa":{"behavior":{},"enabled":true,"maxReplicas":10,"metrics":[],"minReplicas":1,"targetCPUUtilizationPercentage":50},"image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/fido2","tag":"1.3.0-1"},"lifecycle":{},"livenessProbe":{"httpGet":{"path":"/jans-fido2/sys/health-check","port":"http-fido2"},"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5},"pdb":{"enabled":true,"maxUnavailable":"90%"},"readinessProbe":{"httpGet":{"path":"/jans-fido2/sys/health-check","port":"http-fido2"},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5},"replicas":1,"resources":{"limits":{"cpu":"500m","memory":"500Mi"},"requests":{"cpu":"500m","memory":"500Mi"}},"service":{"name":"http-fido2","port":8080},"topologySpreadConstraints":{},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | FIDO 2.0 (FIDO2) is an open authentication standard that enables leveraging common devices to authenticate to online services in both mobile and desktop environments. |
| fido2.additionalAnnotations | object | `{}` | Additional annotations that will be added across the gateway in the format of {cert-manager.io/issuer: "letsencrypt-prod"} |
| fido2.additionalLabels | object | `{}` | Additional labels that will be added across the gateway in the format of {mylabel: "myapp"} |
| fido2.customCommand | list | `[]` | Add custom pod's command. If passed, it will override the default conditional command. |
@@ -252,7 +252,7 @@ Kubernetes: `>=v1.22.0-0`
| fido2.image.pullPolicy | string | `"IfNotPresent"` | Image pullPolicy to use for deploying. |
| fido2.image.pullSecrets | list | `[]` | Image Pull Secrets |
| fido2.image.repository | string | `"ghcr.io/janssenproject/jans/fido2"` | Image to use for deploying. |
-| fido2.image.tag | string | `"0.0.0-nightly"` | Image tag to use for deploying. |
+| fido2.image.tag | string | `"1.3.0-1"` | Image tag to use for deploying. |
| fido2.livenessProbe | object | `{"httpGet":{"path":"/jans-fido2/sys/health-check","port":"http-fido2"},"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5}` | Configure the liveness healthcheck for the fido2 if needed. |
| fido2.livenessProbe.httpGet | object | `{"path":"/jans-fido2/sys/health-check","port":"http-fido2"}` | http liveness probe endpoint |
| fido2.pdb | object | `{"enabled":true,"maxUnavailable":"90%"}` | Configure the PodDisruptionBudget |
@@ -271,7 +271,7 @@ Kubernetes: `>=v1.22.0-0`
| fido2.usrEnvs.secret | object | `{}` | Add custom secret envs to the service variable1: value1 |
| fido2.volumeMounts | list | `[]` | Configure any additional volumesMounts that need to be attached to the containers |
| fido2.volumes | list | `[]` | Configure any additional volumes that need to be attached to the pod |
-| global | object | `{"alb":{"ingress":false},"auth-server":{"appLoggers":{"auditStatsLogLevel":"INFO","auditStatsLogTarget":"FILE","authLogLevel":"INFO","authLogTarget":"STDOUT","enableStdoutLogPrefix":"true","httpLogLevel":"INFO","httpLogTarget":"FILE","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"authEncKeys":"RSA1_5 RSA-OAEP","authServerServiceName":"auth-server","authSigKeys":"RS256 RS384 RS512 ES256 ES384 ES512 PS256 PS384 PS512","cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"authServerAdditionalAnnotations":{},"authServerEnabled":true,"authServerLabels":{},"deviceCodeAdditionalAnnotations":{},"deviceCodeEnabled":true,"deviceCodeLabels":{},"firebaseMessagingAdditionalAnnotations":{},"firebaseMessagingEnabled":true,"firebaseMessagingLabels":{},"lockAdditionalAnnotations":{},"lockConfigAdditionalAnnotations":{},"lockConfigEnabled":false,"lockConfigLabels":{},"lockEnabled":false,"lockLabels":{},"openidAdditionalAnnotations":{},"openidConfigEnabled":true,"openidConfigLabels":{},"u2fAdditionalAnnotations":{},"u2fConfigEnabled":true,"u2fConfigLabels":{},"uma2AdditionalAnnotations":{},"uma2ConfigEnabled":true,"uma2ConfigLabels":{},"webdiscoveryAdditionalAnnotations":{},"webdiscoveryEnabled":true,"webdiscoveryLabels":{},"webfingerAdditionalAnnotations":{},"webfingerEnabled":true,"webfingerLabels":{}},"lockEnabled":false},"auth-server-key-rotation":{"customAnnotations":{"cronjob":{},"secret":{},"service":{}},"enabled":true,"initKeysLife":48},"awsStorageType":"io1","azureStorageAccountType":"Standard_LRS","azureStorageKind":"Managed","casa":{"appLoggers":{"casaLogLevel":"INFO","casaLogTarget":"STDOUT","enableStdoutLogPrefix":"true","timerLogLevel":"INFO","timerLogTarget":"FILE"},"casaServiceName":"casa","cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"casaAdditionalAnnotations":{},"casaEnabled":false,"casaLabels":{}}},"cloud":{"testEnviroment":false},"cnAwsConfigFile":"/etc/jans/conf/aws_config_file","cnAwsSecretsReplicaRegionsFile":"/etc/jans/conf/aws_secrets_replica_regions","cnAwsSharedCredentialsFile":"/etc/jans/conf/aws_shared_credential_file","cnConfiguratorConfigurationFile":"/etc/jans/conf/configuration.json","cnConfiguratorCustomSchema":{"secretName":""},"cnConfiguratorDumpFile":"/etc/jans/conf/configuration.out.json","cnDocumentStoreType":"DB","cnGoogleApplicationCredentials":"/etc/jans/conf/google-credentials.json","cnPersistenceType":"sql","cnPrometheusPort":"","cnSqlPasswordFile":"/etc/jans/conf/sql_password","config":{"customAnnotations":{"clusterRoleBinding":{},"configMap":{},"job":{},"role":{},"roleBinding":{},"secret":{},"service":{},"serviceAccount":{}},"enabled":true},"config-api":{"appLoggers":{"configApiLogLevel":"INFO","configApiLogTarget":"STDOUT","enableStdoutLogPrefix":"true","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","configApiServerServiceName":"config-api","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"configApiAdditionalAnnotations":{},"configApiEnabled":true,"configApiLabels":{}},"plugins":"fido2,scim,user-mgt"},"configAdapterName":"kubernetes","configSecretAdapter":"kubernetes","fido2":{"appLoggers":{"enableStdoutLogPrefix":"true","fido2LogLevel":"INFO","fido2LogTarget":"STDOUT","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"fido2ServiceName":"fido2","ingress":{"fido2AdditionalAnnotations":{},"fido2ConfigAdditionalAnnotations":{},"fido2ConfigEnabled":false,"fido2ConfigLabels":{},"fido2Enabled":false,"fido2Labels":{}}},"fqdn":"demoexample.jans.io","gcePdStorageType":"pd-standard","isFqdnRegistered":false,"istio":{"additionalAnnotations":{},"additionalLabels":{},"enabled":false,"gateways":[],"ingress":false,"namespace":"istio-system"},"jobTtlSecondsAfterFinished":300,"kc-scheduler":{"enabled":false},"lbIp":"22.22.22.22","link":{"appLoggers":{"enableStdoutLogPrefix":"true","linkLogLevel":"INFO","linkLogTarget":"STDOUT","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"service":{},"virtualService":{}},"enabled":false,"ingress":{"linkEnabled":true},"linkServiceName":"link"},"nginx-ingress":{"enabled":true},"persistence":{"customAnnotations":{"job":{},"secret":{},"service":{}},"enabled":true},"saml":{"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":false,"ingress":{"samlAdditionalAnnotations":{},"samlEnabled":false,"samlLabels":{}},"samlServiceName":"saml"},"scim":{"appLoggers":{"enableStdoutLogPrefix":"true","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scimLogLevel":"INFO","scimLogTarget":"STDOUT","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"scimAdditionalAnnotations":{},"scimConfigAdditionalAnnotations":{},"scimConfigEnabled":false,"scimConfigLabels":{},"scimEnabled":false,"scimLabels":{}},"scimServiceName":"scim"},"serviceAccountName":"default","storageClass":{"allowVolumeExpansion":true,"allowedTopologies":[],"mountOptions":["debug"],"parameters":{},"provisioner":"microk8s.io/hostpath","reclaimPolicy":"Retain","volumeBindingMode":"WaitForFirstConsumer"},"usrEnvs":{"normal":{},"secret":{}}}` | Parameters used globally across all services helm charts. |
+| global | object | `{"alb":{"ingress":false},"auth-server":{"appLoggers":{"auditStatsLogLevel":"INFO","auditStatsLogTarget":"FILE","authLogLevel":"INFO","authLogTarget":"STDOUT","enableStdoutLogPrefix":"true","httpLogLevel":"INFO","httpLogTarget":"FILE","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"authEncKeys":"RSA1_5 RSA-OAEP","authServerServiceName":"auth-server","authSigKeys":"RS256 RS384 RS512 ES256 ES384 ES512 PS256 PS384 PS512","cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"authServerAdditionalAnnotations":{},"authServerEnabled":true,"authServerLabels":{},"authzenAdditionalAnnotations":{},"authzenConfigEnabled":true,"authzenConfigLabels":{},"deviceCodeAdditionalAnnotations":{},"deviceCodeEnabled":true,"deviceCodeLabels":{},"firebaseMessagingAdditionalAnnotations":{},"firebaseMessagingEnabled":true,"firebaseMessagingLabels":{},"lockAdditionalAnnotations":{},"lockConfigAdditionalAnnotations":{},"lockConfigEnabled":false,"lockConfigLabels":{},"lockEnabled":false,"lockLabels":{},"openidAdditionalAnnotations":{},"openidConfigEnabled":true,"openidConfigLabels":{},"u2fAdditionalAnnotations":{},"u2fConfigEnabled":true,"u2fConfigLabels":{},"uma2AdditionalAnnotations":{},"uma2ConfigEnabled":true,"uma2ConfigLabels":{},"webdiscoveryAdditionalAnnotations":{},"webdiscoveryEnabled":true,"webdiscoveryLabels":{},"webfingerAdditionalAnnotations":{},"webfingerEnabled":true,"webfingerLabels":{}},"lockEnabled":false},"auth-server-key-rotation":{"customAnnotations":{"cronjob":{},"secret":{},"service":{}},"enabled":true,"initKeysLife":48},"awsStorageType":"io1","azureStorageAccountType":"Standard_LRS","azureStorageKind":"Managed","casa":{"appLoggers":{"casaLogLevel":"INFO","casaLogTarget":"STDOUT","enableStdoutLogPrefix":"true","timerLogLevel":"INFO","timerLogTarget":"FILE"},"casaServiceName":"casa","cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"casaAdditionalAnnotations":{},"casaEnabled":false,"casaLabels":{}}},"cloud":{"testEnviroment":false},"cnAwsConfigFile":"/etc/jans/conf/aws_config_file","cnAwsSecretsReplicaRegionsFile":"/etc/jans/conf/aws_secrets_replica_regions","cnAwsSharedCredentialsFile":"/etc/jans/conf/aws_shared_credential_file","cnConfiguratorConfigurationFile":"/etc/jans/conf/configuration.json","cnConfiguratorCustomSchema":{"secretName":""},"cnConfiguratorDumpFile":"/etc/jans/conf/configuration.out.json","cnConfiguratorKey":"","cnConfiguratorKeyFile":"/etc/jans/conf/configuration.key","cnDocumentStoreType":"DB","cnGoogleApplicationCredentials":"/etc/jans/conf/google-credentials.json","cnPersistenceType":"sql","cnPrometheusPort":"","cnSqlPasswordFile":"/etc/jans/conf/sql_password","config":{"customAnnotations":{"clusterRoleBinding":{},"configMap":{},"job":{},"role":{},"roleBinding":{},"secret":{},"service":{},"serviceAccount":{}},"enabled":true},"config-api":{"appLoggers":{"configApiLogLevel":"INFO","configApiLogTarget":"STDOUT","enableStdoutLogPrefix":"true","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","configApiServerServiceName":"config-api","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"configApiAdditionalAnnotations":{},"configApiEnabled":true,"configApiLabels":{}},"plugins":"fido2,scim,user-mgt"},"configAdapterName":"kubernetes","configSecretAdapter":"kubernetes","fido2":{"appLoggers":{"enableStdoutLogPrefix":"true","fido2LogLevel":"INFO","fido2LogTarget":"STDOUT","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"fido2ServiceName":"fido2","ingress":{"fido2AdditionalAnnotations":{},"fido2ConfigAdditionalAnnotations":{},"fido2ConfigEnabled":false,"fido2ConfigLabels":{},"fido2Enabled":false,"fido2Labels":{},"fido2WebauthnAdditionalAnnotations":{},"fido2WebauthnEnabled":false,"fido2WebauthnLabels":{}}},"fqdn":"demoexample.jans.io","gcePdStorageType":"pd-standard","isFqdnRegistered":false,"istio":{"additionalAnnotations":{},"additionalLabels":{},"enabled":false,"gateways":[],"ingress":false,"namespace":"istio-system"},"jobTtlSecondsAfterFinished":300,"kc-scheduler":{"enabled":false},"lbIp":"22.22.22.22","link":{"appLoggers":{"enableStdoutLogPrefix":"true","linkLogLevel":"INFO","linkLogTarget":"STDOUT","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"service":{},"virtualService":{}},"enabled":false,"ingress":{"linkAdditionalAnnotations":{},"linkEnabled":true,"linkLabels":{}},"linkServiceName":"link"},"nginx-ingress":{"enabled":true},"persistence":{"customAnnotations":{"job":{},"secret":{},"service":{}},"enabled":true},"saml":{"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":false,"ingress":{"samlAdditionalAnnotations":{},"samlEnabled":false,"samlLabels":{}},"samlServiceName":"saml"},"scim":{"appLoggers":{"enableStdoutLogPrefix":"true","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scimLogLevel":"INFO","scimLogTarget":"STDOUT","scriptLogLevel":"INFO","scriptLogTarget":"FILE"},"cnCustomJavaOptions":"","customAnnotations":{"deployment":{},"destinationRule":{},"horizontalPodAutoscaler":{},"pod":{},"podDisruptionBudget":{},"secret":{},"service":{},"virtualService":{}},"enabled":true,"ingress":{"scimAdditionalAnnotations":{},"scimConfigAdditionalAnnotations":{},"scimConfigEnabled":false,"scimConfigLabels":{},"scimEnabled":false,"scimLabels":{}},"scimServiceName":"scim"},"serviceAccountName":"default","storageClass":{"allowVolumeExpansion":true,"allowedTopologies":[],"mountOptions":["debug"],"parameters":{},"provisioner":"microk8s.io/hostpath","reclaimPolicy":"Retain","volumeBindingMode":"WaitForFirstConsumer"},"usrEnvs":{"normal":{},"secret":{}}}` | Parameters used globally across all services helm charts. |
| global.alb.ingress | bool | `false` | Activates ALB ingress |
| global.auth-server-key-rotation.enabled | bool | `true` | Boolean flag to enable/disable the auth-server-key rotation cronjob chart. |
| global.auth-server-key-rotation.initKeysLife | int | `48` | The initial auth server key rotation keys life in hours |
@@ -294,10 +294,13 @@ Kubernetes: `>=v1.22.0-0`
| global.auth-server.authSigKeys | string | `"RS256 RS384 RS512 ES256 ES384 ES512 PS256 PS384 PS512"` | space-separated key algorithm for signing (default to `RS256 RS384 RS512 ES256 ES384 ES512 PS256 PS384 PS512`) |
| global.auth-server.cnCustomJavaOptions | string | `""` | passing custom java options to auth-server. Notice you do not need to pass in any loggers options as they are introduced below in appLoggers. DO NOT PASS JAVA_OPTIONS in envs. |
| global.auth-server.enabled | bool | `true` | Boolean flag to enable/disable auth-server chart. You should never set this to false. |
-| global.auth-server.ingress | object | `{"authServerAdditionalAnnotations":{},"authServerEnabled":true,"authServerLabels":{},"deviceCodeAdditionalAnnotations":{},"deviceCodeEnabled":true,"deviceCodeLabels":{},"firebaseMessagingAdditionalAnnotations":{},"firebaseMessagingEnabled":true,"firebaseMessagingLabels":{},"lockAdditionalAnnotations":{},"lockConfigAdditionalAnnotations":{},"lockConfigEnabled":false,"lockConfigLabels":{},"lockEnabled":false,"lockLabels":{},"openidAdditionalAnnotations":{},"openidConfigEnabled":true,"openidConfigLabels":{},"u2fAdditionalAnnotations":{},"u2fConfigEnabled":true,"u2fConfigLabels":{},"uma2AdditionalAnnotations":{},"uma2ConfigEnabled":true,"uma2ConfigLabels":{},"webdiscoveryAdditionalAnnotations":{},"webdiscoveryEnabled":true,"webdiscoveryLabels":{},"webfingerAdditionalAnnotations":{},"webfingerEnabled":true,"webfingerLabels":{}}` | Enable endpoints in either istio or nginx ingress depending on users choice |
+| global.auth-server.ingress | object | `{"authServerAdditionalAnnotations":{},"authServerEnabled":true,"authServerLabels":{},"authzenAdditionalAnnotations":{},"authzenConfigEnabled":true,"authzenConfigLabels":{},"deviceCodeAdditionalAnnotations":{},"deviceCodeEnabled":true,"deviceCodeLabels":{},"firebaseMessagingAdditionalAnnotations":{},"firebaseMessagingEnabled":true,"firebaseMessagingLabels":{},"lockAdditionalAnnotations":{},"lockConfigAdditionalAnnotations":{},"lockConfigEnabled":false,"lockConfigLabels":{},"lockEnabled":false,"lockLabels":{},"openidAdditionalAnnotations":{},"openidConfigEnabled":true,"openidConfigLabels":{},"u2fAdditionalAnnotations":{},"u2fConfigEnabled":true,"u2fConfigLabels":{},"uma2AdditionalAnnotations":{},"uma2ConfigEnabled":true,"uma2ConfigLabels":{},"webdiscoveryAdditionalAnnotations":{},"webdiscoveryEnabled":true,"webdiscoveryLabels":{},"webfingerAdditionalAnnotations":{},"webfingerEnabled":true,"webfingerLabels":{}}` | Enable endpoints in either istio or nginx ingress depending on users choice |
| global.auth-server.ingress.authServerAdditionalAnnotations | object | `{}` | Auth server ingress resource additional annotations. |
| global.auth-server.ingress.authServerEnabled | bool | `true` | Enable Auth server endpoints /jans-auth |
| global.auth-server.ingress.authServerLabels | object | `{}` | Auth server ingress resource labels. key app is taken |
+| global.auth-server.ingress.authzenAdditionalAnnotations | object | `{}` | authzen config ingress resource additional annotations. |
+| global.auth-server.ingress.authzenConfigEnabled | bool | `true` | Enable endpoint /.well-known/authzen-configuration |
+| global.auth-server.ingress.authzenConfigLabels | object | `{}` | authzen config ingress resource labels. key app is taken |
| global.auth-server.ingress.deviceCodeAdditionalAnnotations | object | `{}` | device-code ingress resource additional annotations. |
| global.auth-server.ingress.deviceCodeEnabled | bool | `true` | Enable endpoint /device-code |
| global.auth-server.ingress.deviceCodeLabels | object | `{}` | device-code ingress resource labels. key app is taken |
@@ -343,15 +346,17 @@ Kubernetes: `>=v1.22.0-0`
| global.casa.ingress.casaEnabled | bool | `false` | Enable casa endpoints /casa |
| global.casa.ingress.casaLabels | object | `{}` | Casa ingress resource labels. key app is taken |
| global.cloud.testEnviroment | bool | `false` | Boolean flag if enabled will strip resources requests and limits from all services. |
-| global.cnConfiguratorConfigurationFile | string | `"/etc/jans/conf/configuration.json"` | Path to configuration schema file |
-| global.cnConfiguratorCustomSchema | object | `{"secretName":""}` | Use custom configuration schema in existing secrets. Note, the secrets has to contain the key configuration.json or any basename as specified in cnConfiguratorConfigurationFile. |
-| global.cnConfiguratorCustomSchema.secretName | string | `""` | The name of the secrets used for storing custom configuration schema. |
-| global.cnConfiguratorDumpFile | string | `"/etc/jans/conf/configuration.out.json"` | Path to dumped configuration schema file |
+| global.cnConfiguratorConfigurationFile | string | `"/etc/jans/conf/configuration.json"` | Path to the configuration schema file |
+| global.cnConfiguratorCustomSchema | object | `{"secretName":""}` | Use custom configuration schema in existing Kubernetes secret. Note that the secret has to contain the configuration.json key or any basename as specified in cnConfiguratorConfigurationFile. |
+| global.cnConfiguratorCustomSchema.secretName | string | `""` | The name of the Kubernetes secret used for storing custom configuration schema. |
+| global.cnConfiguratorDumpFile | string | `"/etc/jans/conf/configuration.out.json"` | Path to the dumped configuration schema file |
+| global.cnConfiguratorKey | string | `""` | Key to encrypt/decrypt configuration schema file using AES-256 CBC mode. Set the value to empty string to disable encryption/decryption, or 32 alphanumeric characters to enable it. |
+| global.cnConfiguratorKeyFile | string | `"/etc/jans/conf/configuration.key"` | Path to the file that contains the key to encrypt/decrypt the configuration schema file. |
| global.cnDocumentStoreType | string | `"DB"` | Document store type to use for shibboleth files DB. |
| global.cnGoogleApplicationCredentials | string | `"/etc/jans/conf/google-credentials.json"` | Base64 encoded service account. The sa must have roles/secretmanager.admin to use Google secrets. Leave as this is a sensible default. |
| global.cnPersistenceType | string | `"sql"` | Persistence backend to run Janssen with hybrid|sql |
| global.cnPrometheusPort | string | `""` | Port used by Prometheus JMX agent (default to empty string). To enable Prometheus JMX agent, set the value to a number. |
-| global.cnSqlPasswordFile | string | `"/etc/jans/conf/sql_password"` | Path to SQL password file |
+| global.cnSqlPasswordFile | string | `"/etc/jans/conf/sql_password"` | Path to the SQL password file |
| global.config-api.appLoggers | object | `{"configApiLogLevel":"INFO","configApiLogTarget":"STDOUT","enableStdoutLogPrefix":"true","persistenceDurationLogLevel":"INFO","persistenceDurationLogTarget":"FILE","persistenceLogLevel":"INFO","persistenceLogTarget":"FILE","scriptLogLevel":"INFO","scriptLogTarget":"FILE"}` | App loggers can be configured to define where the logs will be redirected to and the level of each in which it should be displayed. |
| global.config-api.appLoggers.configApiLogLevel | string | `"INFO"` | configapi.log level |
| global.config-api.appLoggers.configApiLogTarget | string | `"STDOUT"` | configapi.log target |
@@ -385,13 +390,16 @@ Kubernetes: `>=v1.22.0-0`
| global.fido2.cnCustomJavaOptions | string | `""` | passing custom java options to fido2. Notice you do not need to pass in any loggers options as they are introduced below in appLoggers. DO NOT PASS JAVA_OPTIONS in envs. |
| global.fido2.enabled | bool | `true` | Boolean flag to enable/disable the fido2 chart. |
| global.fido2.fido2ServiceName | string | `"fido2"` | Name of the fido2 service. Please keep it as default. |
-| global.fido2.ingress | object | `{"fido2AdditionalAnnotations":{},"fido2ConfigAdditionalAnnotations":{},"fido2ConfigEnabled":false,"fido2ConfigLabels":{},"fido2Enabled":false,"fido2Labels":{}}` | Enable endpoints in either istio or nginx ingress depending on users choice |
+| global.fido2.ingress | object | `{"fido2AdditionalAnnotations":{},"fido2ConfigAdditionalAnnotations":{},"fido2ConfigEnabled":false,"fido2ConfigLabels":{},"fido2Enabled":false,"fido2Labels":{},"fido2WebauthnAdditionalAnnotations":{},"fido2WebauthnEnabled":false,"fido2WebauthnLabels":{}}` | Enable endpoints in either istio or nginx ingress depending on users choice |
| global.fido2.ingress.fido2AdditionalAnnotations | object | `{}` | fido2 ingress resource additional annotations. |
| global.fido2.ingress.fido2ConfigAdditionalAnnotations | object | `{}` | fido2 config ingress resource additional annotations. |
| global.fido2.ingress.fido2ConfigEnabled | bool | `false` | Enable endpoint /.well-known/fido2-configuration |
| global.fido2.ingress.fido2ConfigLabels | object | `{}` | fido2 config ingress resource labels. key app is taken |
| global.fido2.ingress.fido2Enabled | bool | `false` | Enable endpoint /jans-fido2 |
| global.fido2.ingress.fido2Labels | object | `{}` | fido2 ingress resource labels. key app is taken |
+| global.fido2.ingress.fido2WebauthnAdditionalAnnotations | object | `{}` | fido2 webauthn ingress resource additional annotations. |
+| global.fido2.ingress.fido2WebauthnEnabled | bool | `false` | Enable endpoint /.well-known/webauthn |
+| global.fido2.ingress.fido2WebauthnLabels | object | `{}` | fido2 webauthn ingress resource labels. key app is taken |
| global.fqdn | string | `"demoexample.jans.io"` | Fully qualified domain name to be used for Janssen installation. This address will be used to reach Janssen services. |
| global.gcePdStorageType | string | `"pd-standard"` | GCE storage kind if using Google disks |
| global.isFqdnRegistered | bool | `false` | Boolean flag to enable mapping global.lbIp to global.fqdn inside pods on clouds that provide static ip for load balancers. On cloud that provide only addresses to the LB this flag will enable a script to actively scan config.configmap.lbAddr and update the hosts file inside the pods automatically. |
@@ -416,7 +424,9 @@ Kubernetes: `>=v1.22.0-0`
| global.link.appLoggers.scriptLogTarget | string | `"FILE"` | cacherefresh_script.log target |
| global.link.cnCustomJavaOptions | string | `""` | passing custom java options to link. Notice you do not need to pass in any loggers options as they are introduced below in appLoggers. DO NOT PASS JAVA_OPTIONS in envs. |
| global.link.enabled | bool | `false` | Boolean flag to enable/disable the link chart. |
-| global.link.ingress | object | `{"linkEnabled":true}` | Enable endpoints in either istio or nginx ingress depending on users choice |
+| global.link.ingress | object | `{"linkAdditionalAnnotations":{},"linkEnabled":true,"linkLabels":{}}` | Enable endpoints in either istio or nginx ingress depending on users choice |
+| global.link.ingress.linkAdditionalAnnotations | object | `{}` | link ingress resource additional annotations. |
+| global.link.ingress.linkLabels | object | `{}` | link ingress resource labels. key app is taken |
| global.link.linkServiceName | string | `"link"` | Name of the link service. Please keep it as default. |
| global.nginx-ingress.enabled | bool | `true` | Boolean flag to enable/disable the nginx-ingress definitions chart. |
| global.persistence.enabled | bool | `true` | Boolean flag to enable/disable the persistence chart. |
@@ -444,7 +454,7 @@ Kubernetes: `>=v1.22.0-0`
| global.scim.ingress.scimConfigEnabled | bool | `false` | Enable endpoint /.well-known/scim-configuration |
| global.scim.ingress.scimConfigLabels | object | `{}` | SCIM config ingress resource labels. key app is taken |
| global.scim.ingress.scimEnabled | bool | `false` | Enable SCIM endpoints /jans-scim |
-| global.scim.ingress.scimLabels | object | `{}` | SCIM config ingress resource labels. key app is taken |
+| global.scim.ingress.scimLabels | object | `{}` | SCIM ingress resource labels. key app is taken |
| global.scim.scimServiceName | string | `"scim"` | Name of the scim service. Please keep it as default. |
| global.serviceAccountName | string | `"default"` | service account used by Kubernetes resources |
| global.storageClass | object | `{"allowVolumeExpansion":true,"allowedTopologies":[],"mountOptions":["debug"],"parameters":{},"provisioner":"microk8s.io/hostpath","reclaimPolicy":"Retain","volumeBindingMode":"WaitForFirstConsumer"}` | StorageClass section. This is not currently used by the openbanking distribution. You may specify custom parameters as needed. |
@@ -452,7 +462,7 @@ Kubernetes: `>=v1.22.0-0`
| global.usrEnvs | object | `{"normal":{},"secret":{}}` | Add custom normal and secret envs to the service. Envs defined in global.userEnvs will be globally available to all services |
| global.usrEnvs.normal | object | `{}` | Add custom normal envs to the service. variable1: value1 |
| global.usrEnvs.secret | object | `{}` | Add custom secret envs to the service. variable1: value1 |
-| kc-scheduler | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/kc-scheduler","tag":"0.0.0-nightly"},"interval":10,"lifecycle":{},"resources":{"limits":{"cpu":"300m","memory":"300Mi"},"requests":{"cpu":"300m","memory":"300Mi"}},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | Responsible for synchronizing Keycloak SAML clients |
+| kc-scheduler | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/kc-scheduler","tag":"1.3.0-1"},"interval":10,"lifecycle":{},"resources":{"limits":{"cpu":"300m","memory":"300Mi"},"requests":{"cpu":"300m","memory":"300Mi"}},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | Responsible for synchronizing Keycloak SAML clients |
| kc-scheduler.additionalAnnotations | object | `{}` | Additional annotations that will be added across the gateway in the format of {cert-manager.io/issuer: "letsencrypt-prod"} |
| kc-scheduler.additionalLabels | object | `{}` | Additional labels that will be added across the gateway in the format of {mylabel: "myapp"} |
| kc-scheduler.customCommand | list | `[]` | Add custom job's command. If passed, it will override the default conditional command. |
@@ -462,7 +472,7 @@ Kubernetes: `>=v1.22.0-0`
| kc-scheduler.image.pullPolicy | string | `"IfNotPresent"` | Image pullPolicy to use for deploying. |
| kc-scheduler.image.pullSecrets | list | `[]` | Image Pull Secrets |
| kc-scheduler.image.repository | string | `"ghcr.io/janssenproject/jans/kc-scheduler"` | Image to use for deploying. |
-| kc-scheduler.image.tag | string | `"0.0.0-nightly"` | Image tag to use for deploying. |
+| kc-scheduler.image.tag | string | `"1.3.0-1"` | Image tag to use for deploying. |
| kc-scheduler.interval | int | `10` | Interval of running the scheduler (in minutes) |
| kc-scheduler.resources | object | `{"limits":{"cpu":"300m","memory":"300Mi"},"requests":{"cpu":"300m","memory":"300Mi"}}` | Resource specs. |
| kc-scheduler.resources.limits.cpu | string | `"300m"` | CPU limit. |
@@ -474,7 +484,7 @@ Kubernetes: `>=v1.22.0-0`
| kc-scheduler.usrEnvs.secret | object | `{}` | Add custom secret envs to the service variable1: value1 |
| kc-scheduler.volumeMounts | list | `[]` | Configure any additional volumesMounts that need to be attached to the containers |
| kc-scheduler.volumes | list | `[]` | Configure any additional volumes that need to be attached to the pod |
-| link | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","hpa":{"behavior":{},"enabled":true,"maxReplicas":10,"metrics":[],"minReplicas":1,"targetCPUUtilizationPercentage":50},"image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/link","tag":"0.0.0-nightly"},"lifecycle":{},"livenessProbe":{"exec":{"command":["python3","/app/scripts/healthcheck.py"]},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5},"pdb":{"enabled":true,"maxUnavailable":"90%"},"readinessProbe":{"exec":{"command":["python3","/app/scripts/healthcheck.py"]},"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5},"replicas":1,"resources":{"limits":{"cpu":"500m","memory":"1200Mi"},"requests":{"cpu":"500m","memory":"1200Mi"}},"topologySpreadConstraints":{},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | Link. |
+| link | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","hpa":{"behavior":{},"enabled":true,"maxReplicas":10,"metrics":[],"minReplicas":1,"targetCPUUtilizationPercentage":50},"image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/link","tag":"1.3.0-1"},"lifecycle":{},"livenessProbe":{"exec":{"command":["python3","/app/scripts/healthcheck.py"]},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5},"pdb":{"enabled":true,"maxUnavailable":"90%"},"readinessProbe":{"exec":{"command":["python3","/app/scripts/healthcheck.py"]},"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5},"replicas":1,"resources":{"limits":{"cpu":"500m","memory":"1200Mi"},"requests":{"cpu":"500m","memory":"1200Mi"}},"topologySpreadConstraints":{},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | Link. |
| link.additionalAnnotations | object | `{}` | Additional annotations that will be added across the gateway in the format of {cert-manager.io/issuer: "letsencrypt-prod"} |
| link.additionalLabels | object | `{}` | Additional labels that will be added across the gateway in the format of {mylabel: "myapp"} |
| link.customCommand | list | `[]` | Add custom pod's command. If passed, it will override the default conditional command. |
@@ -487,7 +497,7 @@ Kubernetes: `>=v1.22.0-0`
| link.image.pullPolicy | string | `"IfNotPresent"` | Image pullPolicy to use for deploying. |
| link.image.pullSecrets | list | `[]` | Image Pull Secrets |
| link.image.repository | string | `"ghcr.io/janssenproject/jans/link"` | Image to use for deploying. |
-| link.image.tag | string | `"0.0.0-nightly"` | Image tag to use for deploying. |
+| link.image.tag | string | `"1.3.0-1"` | Image tag to use for deploying. |
| link.livenessProbe | object | `{"exec":{"command":["python3","/app/scripts/healthcheck.py"]},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5}` | Configure the liveness healthcheck for the auth server if needed. |
| link.livenessProbe.exec | object | `{"command":["python3","/app/scripts/healthcheck.py"]}` | http liveness probe endpoint |
| link.pdb | object | `{"enabled":true,"maxUnavailable":"90%"}` | Configure the PodDisruptionBudget |
@@ -508,7 +518,7 @@ Kubernetes: `>=v1.22.0-0`
| nginx-ingress.ingress.additionalAnnotations | object | `{}` | Additional annotations that will be added across all ingress definitions in the format of {cert-manager.io/issuer: "letsencrypt-prod"} Enable client certificate authentication nginx.ingress.kubernetes.io/auth-tls-verify-client: "optional" Create the secret containing the trusted ca certificates nginx.ingress.kubernetes.io/auth-tls-secret: "janssen/tls-certificate" Specify the verification depth in the client certificates chain nginx.ingress.kubernetes.io/auth-tls-verify-depth: "1" Specify if certificates are passed to upstream server nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true" |
| nginx-ingress.ingress.additionalLabels | object | `{}` | Additional labels that will be added across all ingress definitions in the format of {mylabel: "myapp"} |
| nginx-ingress.ingress.tls | list | `[{"hosts":["demoexample.jans.io"],"secretName":"tls-certificate"}]` | Secrets holding HTTPS CA cert and key. |
-| persistence | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/persistence-loader","tag":"0.0.0-nightly"},"lifecycle":{},"resources":{"limits":{"cpu":"300m","memory":"300Mi"},"requests":{"cpu":"300m","memory":"300Mi"}},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | Job to generate data and initial config for Janssen Server persistence layer. |
+| persistence | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/persistence-loader","tag":"1.3.0-1"},"lifecycle":{},"resources":{"limits":{"cpu":"300m","memory":"300Mi"},"requests":{"cpu":"300m","memory":"300Mi"}},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | Job to generate data and initial config for Janssen Server persistence layer. |
| persistence.additionalAnnotations | object | `{}` | Additional annotations that will be added across the gateway in the format of {cert-manager.io/issuer: "letsencrypt-prod"} |
| persistence.additionalLabels | object | `{}` | Additional labels that will be added across the gateway in the format of {mylabel: "myapp"} |
| persistence.customCommand | list | `[]` | Add custom job's command. If passed, it will override the default conditional command. |
@@ -518,7 +528,7 @@ Kubernetes: `>=v1.22.0-0`
| persistence.image.pullPolicy | string | `"IfNotPresent"` | Image pullPolicy to use for deploying. |
| persistence.image.pullSecrets | list | `[]` | Image Pull Secrets |
| persistence.image.repository | string | `"ghcr.io/janssenproject/jans/persistence-loader"` | Image to use for deploying. |
-| persistence.image.tag | string | `"0.0.0-nightly"` | Image tag to use for deploying. |
+| persistence.image.tag | string | `"1.3.0-1"` | Image tag to use for deploying. |
| persistence.resources | object | `{"limits":{"cpu":"300m","memory":"300Mi"},"requests":{"cpu":"300m","memory":"300Mi"}}` | Resource specs. |
| persistence.resources.limits.cpu | string | `"300m"` | CPU limit |
| persistence.resources.limits.memory | string | `"300Mi"` | Memory limit. |
@@ -529,7 +539,7 @@ Kubernetes: `>=v1.22.0-0`
| persistence.usrEnvs.secret | object | `{}` | Add custom secret envs to the service variable1: value1 |
| persistence.volumeMounts | list | `[]` | Configure any additional volumesMounts that need to be attached to the containers |
| persistence.volumes | list | `[]` | Configure any additional volumes that need to be attached to the pod |
-| saml | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","hpa":{"behavior":{},"enabled":true,"maxReplicas":10,"metrics":[],"minReplicas":1,"targetCPUUtilizationPercentage":50},"image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/saml","tag":"0.0.0-nightly"},"lifecycle":{},"livenessProbe":{"exec":{"command":["python3","/app/scripts/healthcheck.py"]},"failureThreshold":10,"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5},"pdb":{"enabled":true,"maxUnavailable":"90%"},"readinessProbe":{"exec":{"command":["python3","/app/scripts/healthcheck.py"]},"failureThreshold":10,"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5},"replicas":1,"resources":{"limits":{"cpu":"500m","memory":"1200Mi"},"requests":{"cpu":"500m","memory":"1200Mi"}},"topologySpreadConstraints":{},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | SAML. |
+| saml | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","hpa":{"behavior":{},"enabled":true,"maxReplicas":10,"metrics":[],"minReplicas":1,"targetCPUUtilizationPercentage":50},"image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/saml","tag":"1.3.0-1"},"lifecycle":{},"livenessProbe":{"exec":{"command":["python3","/app/scripts/healthcheck.py"]},"failureThreshold":10,"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5},"pdb":{"enabled":true,"maxUnavailable":"90%"},"readinessProbe":{"exec":{"command":["python3","/app/scripts/healthcheck.py"]},"failureThreshold":10,"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5},"replicas":1,"resources":{"limits":{"cpu":"500m","memory":"1200Mi"},"requests":{"cpu":"500m","memory":"1200Mi"}},"topologySpreadConstraints":{},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | SAML. |
| saml.additionalAnnotations | object | `{}` | Additional annotations that will be added across the gateway in the format of {cert-manager.io/issuer: "letsencrypt-prod"} |
| saml.additionalLabels | object | `{}` | Additional labels that will be added across the gateway in the format of {mylabel: "myapp"} |
| saml.customCommand | list | `[]` | Add custom pod's command. If passed, it will override the default conditional command. |
@@ -542,7 +552,7 @@ Kubernetes: `>=v1.22.0-0`
| saml.image.pullPolicy | string | `"IfNotPresent"` | Image pullPolicy to use for deploying. |
| saml.image.pullSecrets | list | `[]` | Image Pull Secrets |
| saml.image.repository | string | `"ghcr.io/janssenproject/jans/saml"` | Image to use for deploying. |
-| saml.image.tag | string | `"0.0.0-nightly"` | Image tag to use for deploying. |
+| saml.image.tag | string | `"1.3.0-1"` | Image tag to use for deploying. |
| saml.livenessProbe | object | `{"exec":{"command":["python3","/app/scripts/healthcheck.py"]},"failureThreshold":10,"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5}` | Configure the liveness healthcheck for the auth server if needed. |
| saml.livenessProbe.exec | object | `{"command":["python3","/app/scripts/healthcheck.py"]}` | http liveness probe endpoint |
| saml.pdb | object | `{"enabled":true,"maxUnavailable":"90%"}` | Configure the PodDisruptionBudget |
@@ -559,7 +569,7 @@ Kubernetes: `>=v1.22.0-0`
| saml.usrEnvs.secret | object | `{}` | Add custom secret envs to the service variable1: value1 |
| saml.volumeMounts | list | `[]` | Configure any additional volumesMounts that need to be attached to the containers |
| saml.volumes | list | `[]` | Configure any additional volumes that need to be attached to the pod |
-| scim | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","hpa":{"behavior":{},"enabled":true,"maxReplicas":10,"metrics":[],"minReplicas":1,"targetCPUUtilizationPercentage":50},"image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/scim","tag":"0.0.0-nightly"},"lifecycle":{},"livenessProbe":{"httpGet":{"path":"/jans-scim/sys/health-check","port":8080},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5},"pdb":{"enabled":true,"maxUnavailable":"90%"},"readinessProbe":{"httpGet":{"path":"/jans-scim/sys/health-check","port":8080},"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5},"replicas":1,"resources":{"limits":{"cpu":"1000m","memory":"1200Mi"},"requests":{"cpu":"1000m","memory":"1200Mi"}},"service":{"name":"http-scim","port":8080},"topologySpreadConstraints":{},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | System for Cross-domain Identity Management (SCIM) version 2.0 |
+| scim | object | `{"additionalAnnotations":{},"additionalLabels":{},"customCommand":[],"customScripts":[],"dnsConfig":{},"dnsPolicy":"","hpa":{"behavior":{},"enabled":true,"maxReplicas":10,"metrics":[],"minReplicas":1,"targetCPUUtilizationPercentage":50},"image":{"pullPolicy":"IfNotPresent","pullSecrets":[],"repository":"ghcr.io/janssenproject/jans/scim","tag":"1.3.0-1"},"lifecycle":{},"livenessProbe":{"httpGet":{"path":"/jans-scim/sys/health-check","port":8080},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5},"pdb":{"enabled":true,"maxUnavailable":"90%"},"readinessProbe":{"httpGet":{"path":"/jans-scim/sys/health-check","port":8080},"initialDelaySeconds":25,"periodSeconds":25,"timeoutSeconds":5},"replicas":1,"resources":{"limits":{"cpu":"1000m","memory":"1200Mi"},"requests":{"cpu":"1000m","memory":"1200Mi"}},"service":{"name":"http-scim","port":8080},"topologySpreadConstraints":{},"usrEnvs":{"normal":{},"secret":{}},"volumeMounts":[],"volumes":[]}` | System for Cross-domain Identity Management (SCIM) version 2.0 |
| scim.additionalAnnotations | object | `{}` | Additional annotations that will be added across the gateway in the format of {cert-manager.io/issuer: "letsencrypt-prod"} |
| scim.additionalLabels | object | `{}` | Additional labels that will be added across the gateway in the format of {mylabel: "myapp"} |
| scim.customCommand | list | `[]` | Add custom pod's command. If passed, it will override the default conditional command. |
@@ -572,7 +582,7 @@ Kubernetes: `>=v1.22.0-0`
| scim.image.pullPolicy | string | `"IfNotPresent"` | Image pullPolicy to use for deploying. |
| scim.image.pullSecrets | list | `[]` | Image Pull Secrets |
| scim.image.repository | string | `"ghcr.io/janssenproject/jans/scim"` | Image to use for deploying. |
-| scim.image.tag | string | `"0.0.0-nightly"` | Image tag to use for deploying. |
+| scim.image.tag | string | `"1.3.0-1"` | Image tag to use for deploying. |
| scim.livenessProbe | object | `{"httpGet":{"path":"/jans-scim/sys/health-check","port":8080},"initialDelaySeconds":30,"periodSeconds":30,"timeoutSeconds":5}` | Configure the liveness healthcheck for SCIM if needed. |
| scim.livenessProbe.httpGet.path | string | `"/jans-scim/sys/health-check"` | http liveness probe endpoint |
| scim.pdb | object | `{"enabled":true,"maxUnavailable":"90%"}` | Configure the PodDisruptionBudget |
@@ -591,6 +601,3 @@ Kubernetes: `>=v1.22.0-0`
| scim.usrEnvs.secret | object | `{}` | Add custom secret envs to the service variable1: value1 |
| scim.volumeMounts | list | `[]` | Configure any additional volumesMounts that need to be attached to the containers |
| scim.volumes | list | `[]` | Configure any additional volumes that need to be attached to the pod |
-
-----------------------------------------------
-Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2)
diff --git a/docs/janssen-server/reference/learning-reference.md b/docs/janssen-server/reference/learning-reference.md
index 28cf9f0e483..5d73a9d824a 100644
--- a/docs/janssen-server/reference/learning-reference.md
+++ b/docs/janssen-server/reference/learning-reference.md
@@ -20,7 +20,6 @@ help us keep it updated by raising a PR.
- [An intro to Janssen IDP Terraform Provider - Part1](https://medium.com/@moabu/an-intro-to-janssen-idp-terraform-provider-part-1-cff350526f17)
- [An intro to Janssen IDP Terraform Provider - Part2](https://medium.com/@moabu/an-intro-to-janssen-idp-terraform-provider-part-2-basic-example-d88c48607293)
-- [Jans-tent to test Single-Sign-On with your Authorization Server](https://medium.com/@imshakil/jans-tent-to-test-single-sign-on-with-your-authorization-server-d153c873c5d1)
- [Janssen + Mod Auth openidc module to Test OpenID Connect Single Sign-on (SSO) and Single Single Logout (SLO)](https://medium.com/@imshakil/janssen-mod-auth-openidc-module-to-test-openid-connect-single-sign-on-sso-and-single-single-48fcd7b894b7)
- [Enhancing Secure Mobile Authentication with OAuth, Dynamic Client Registration, and DPoP](https://medium.com/@arnab.bdutta/dcr-attestation-and-dpop-for-first-party-native-applications-3e86a837196e)
diff --git a/docs/janssen-server/usermgmt/usermgmt-scim.md b/docs/janssen-server/usermgmt/usermgmt-scim.md
index e3afa2275ec..618dee88c42 100644
--- a/docs/janssen-server/usermgmt/usermgmt-scim.md
+++ b/docs/janssen-server/usermgmt/usermgmt-scim.md
@@ -64,386 +64,50 @@ Please see [here](https://docs.jans.io/v1.0.14/admin/scim/logs/) besides
To know more about OAuth protection mode please visit [here](https://docs.jans.io/v1.0.14/admin/scim/oauth-protection/).
The SCIM API endpoints are by default protected by (Bearer) OAuth 2.0 tokens. Depending on the operation, these tokens must have certain scopes for the operations to be authorized. We need a client to get Bearer token.
### Get SCIM Client
-Let's obtain the credentials of this client first. In TUI, navigate to `Auth Server > Clients`. In the search field type SCIM (uppercase). Highlight the row that matches a client named "SCIM Client" and press Enter. To see in `JSON` formate please press `d`.
-From the "Basic" section, grab the "client id" and "client secret". This secret is encrypted, to decrypt it, in a terminal run `/opt/jans/bin/encode.py -D ENCRYPTED-SECRET-HERE`.
+
+You can refer to [here](../../janssen-server/config-guide/scim-config/user-config.md#get-scim-client) for this topic
+
### Get Access token
-Let's get a token,
-```
-curl -k -u 'CLIENT_ID:DECRYPTED_CLIENT_SECRET' -k -d grant_type=client_credentials -d scope='https://jans.io/scim/users.read https://jans.io/scim/users write' https:///jans-auth/restv1/token > /tmp/token.json
-```
-In response `token.json` we will get `access_token`
-```
-{
-"access_token":"11a76589-7955-4247-9ca5-f3ad7884305...",
-"scope":"https://jans.io/scim/users.read",
-"token_type":"Bearer",
-"expires_in":299
-}
-```
+You can refer to [here](../../janssen-server/config-guide/scim-config/user-config.md#get-access-token) for this topic
### Retrive existing User
-To get an existing user
-
-```
-curl -k -G -H 'Authorization: Bearer ACCESS_TOKEN' --data-urlencode 'filter=displayName co "Admin"' https:///jans-scim/restv1/v2/Users > /tmp/user.json
-```
-In response `user.json` we will get
-```
-{
- "schemas": [
- "urn:ietf:params:scim:api:messages:2.0:ListResponse"
- ],
- "totalResults": 1,
- "startIndex": 1,
- "itemsPerPage": 1,
- "Resources": [
- {
- "schemas": [
- "urn:ietf:params:scim:schemas:core:2.0:User"
- ],
- "id": "5fdbb720-a1fd-477f-af92-b7c054f02c98",
- "meta": {
- "resourceType": "User",
- "created": "2023-06-12T14:54:09.531Z",
- "location": "https://raju.jans13.me/jans-scim/restv1/v2/Users/5fdbb720-a1fd-477f-af92-b7c054f02c98"
- },
- "userName": "admin",
- "name": {
- "familyName": "...",
- "givenName": "...",
- "middleName": "...",
- "formatted": "..."
- },
- "displayName": "Admin",
- "active": true,
- "emails": [
- {
- "value": "example@gluu.org",
- "primary": false
- }
- ],
- "groups": [
- {
- "value": "60B7",
- "display": "Jannsen Manager Group",
- "type": "direct",
- "$ref": "https://raju.jans13.me/jans-scim/restv1/v2/Groups/60B7"
- }
- ]
- }
- ]
-}
-```
-
+You can refer to [here](../../janssen-server/config-guide/scim-config/user-config.md#retrive-existing-user) for this topic
## Creating Resource
### Create an User
-Let's start creating a dummy user. A client sends a POST request containing a "User" to the "/Users" endpoint.
-```
-POST /Users HTTP/1.1
-Host: example.com
-Accept: application/scim+json
-Content-Type: application/scim+json
-Authorization: Bearer h480djs93hd8..
-Content-Length: ...
-
-{
- "schemas": [
- "urn:ietf:params:scim:schemas:core:2.0:User"
- ],
- "userName": "bjensen",
- "externalId": "bjensen",
- "name": {
- "formatted": "Ms. Barbara J Jensen III",
- "familyName": "Jensen",
- "givenName": "Barbara"
- }
-}
-```
-Open a text editor and copy paste the json body, name as `input.json`.
-Hit on your terminal with bellow command.
-```
-curl -k -H 'Authorization: Bearer ACCESS_TOKEN' -H 'Content-Type: application/scim+json' -d @input.json -o output.json https:///jans-scim/restv1/v2/Users
-```
-response looks like
-```
-{
- "schemas": [
- "urn:ietf:params:scim:schemas:core:2.0:User"
- ],
- "id": "e3009115-b890-4d8b-bd63-bbfef34aa583",
- "externalId": "bjensen",
- "meta": {
- "resourceType": "User",
- "created": "2023-06-26T19:43:32.945Z",
- "lastModified": "2023-06-26T19:43:32.945Z",
- "location": "https://raju.jans13.me/jans-scim/restv1/v2/Users/e3009115-b890-4d8b-bd63-bbfef34aa583"
- },
- "userName": "bjensen",
- "name": {
- "familyName": "Jensen",
- "givenName": "Barbara",
- "formatted": "Ms. Barbara J Jensen III"
- }
-}
-```
-
-This new user has been given an `id`. If possible, inspect your `ou=people` branch and find the entry whose `inum` matches the `id` given. An easier option would be to via **Jans TUI** and go to `Users` and search "bjensen" to see the recently created user.
-### Updating a User(PUT)
+You can refer to [here](../../janssen-server/config-guide/scim-config/user-config.md#create-an-user) for this topic
-Overwrite your `input.json` with the following. Replace content in angle brackets accordingly:
-
-```
-{
- "schemas": [
- "urn:ietf:params:scim:schemas:core:2.0:User"
- ],
- "id": "e3009115-b890-4d8b-bd63-bbfef34aa583",
- "userName": "bjensen",
- "externalId": "bjensen",
- "name": {
- "formatted": "Ms. Barbara J Jensen III",
- "familyName": "Jensen",
- "givenName": "Barbara"
- },
- "displayName": "Jensen Barbara",
- "emails": [
- {
- "value": "jensen@example.com",
- "type": "work",
- "primary": true
- }
- ]
-}
-```
-
-PUT with curl:
-
-```
-curl -k -X PUT -H 'Authorization: Bearer ACCESS_TOKEN' -H 'Content-Type: application/scim+json' -d @input.json -o output.json https:///jans-scim/restv1/v2/Users/
-```
-
-Response `(output.json)` will show the same contents of a full retrieval.
-
-Please verify changes were applied whether by inspecting LDAP or issuing a GET. If you have followed the steps properly, you should notice a new e-mail added and the change in `displayName` attribute
+### Updating a User(PUT)
+You can refer to [here](../../janssen-server/config-guide/scim-config/user-config.md#updating-a-userput) for this topic
### Updating a User (PATCH)
-With patching, you can be very precise about the modifications you want to apply. Patching syntax follows JSON Patch spec (RFC 6902) closely. While it's not a must to read the RFC to learn how patch works, see section 3.5.2 of SCIM protocol (RFC 7644) to get the grasp.
-
-If you prefer reading code, [patch test cases](https://github.com/JanssenProject/jans/tree/main/jans-scim/client/src/test/java/io/jans/scim2/client/patch) found in the Java scim-client project are worth to look at.
-
-The following is a simple example that illustrates the kind of modifications developers can achieve via `PATCH`. Overwrite your `input.json` with the following:
-
-```
-{
- "schemas": [
- "urn:ietf:params:scim:api:messages:2.0:PatchOp"
- ],
- "Operations": [
- {
- "op": "replace",
- "value": {
- "name": {
- "givenName": "Joey"
- }
- }
- },
- {
- "op": "replace",
- "path": "emails[type eq \"work\" or primary eq false].value",
- "value": "jensen@example.com"
- },
- {
- "op": "add",
- "value": {
- "name": {
- "middleName": "Jhon"
- }
- }
- },
- {
- "op": "add",
- "value": {
- "emails": [
- {
- "primary": true,
- "value": "my@own.mail"
- }
- ],
- "phoneNumbers": [
- {
- "type": "home",
- "value": "5 123 8901"
- },
- {
- "value": "5 123 8902"
- }
- ]
- }
- },
- {
- "op": "remove",
- "path": "name.middleName"
- },
- {
- "op": "remove",
- "path": "phoneNumbers[value ew \"01\"].type"
- }
- ]
-}
-```
-
-A collection of modification are provided under "Operations". They are processed in order of appearance. Also, every operation has a type; patching supports add, remove and replace.
-
-The first operations states the following: replace the value of `givenName` subattribute (that belongs to complex attribute `name`) with the string "Joey".
-
-Operations are easier to understand when using a "path". The second operation replaces the value subattribute inside the complex multi-valued attribute emails. Inside the square brackets, we find a filter expression, so the replacement does not apply to all emails in the list but only to those matching the criterion.
-
-So the second operation can be read as "set the value of value subattribute to string `jensen@example.com` where the type subattribute of the `email` equals to string "work" or if primary attribute is false".
-
-The third operation is similar to the first. It sets the value of a subattribute which was unassigned (null). You could have used "replace" operation in this case and results would have been identical.
-
-The fourth operation is more interesting. It adds to the current list of emails a new one. It supplies a couple of subattributes for the email to include: primary and value. Additionally, we set the value of (previously unassigned) phoneNumbers multi-valued attribute passing a list of elements.
-
-In the fifth operation, we remove the `middleName` attribute that was set in operation three. Note how we make explicit the path of data to nullify: "name.middleName".
-
-The sixth operation allows us to remove a specific subattribute of `phoneNumbers`. The aim is to nullify the "type" of the item whose phone number value ends with "01". The remove operation can also be used to remove a complete item from a list, or empty the whole list by providing a suitable value for "path".
-
-Now let's see it in action:
-
-
-```
-curl -k -X PATCH -H 'Authorization: Bearer ACCESS_TOKEN' -H 'Content-Type: application/scim+json' -d @input.json -o output.json https:///jans-scim/restv1/v2/Users/
-```
-
-So far our resource look like this
-
-```
-{
- "schemas": [
- "urn:ietf:params:scim:schemas:core:2.0:User"
- ],
- "id": "e3009115-b890-4d8b-bd63-bbfef34aa583",
- "externalId": "bjensen",
- "meta": {
- "resourceType": "User",
- "created": "2023-06-26T19:43:32.945Z",
- "lastModified": "2023-06-26T22:34:27.465Z",
- "location": "https://raju.jans13.me/jans-scim/restv1/v2/Users/e3009115-b890-4d8b-bd63-bbfef34aa583"
- },
- "userName": "bjensen",
- "name": {
- "familyName": "Jensen",
- "givenName": "Joey",
- "formatted": "Ms. Barbara J Jensen III"
- },
- "displayName": "Jensen Barbara",
- "active": false,
- "emails": [
- {
- "value": "my@own.mail",
- "primary": true
- },
- {
- "value": "jensen@example.com",
- "type": "work",
- "primary": false
- }
- ],
- "phoneNumbers": [
- {
- "value": "5 123 8901"
- },
- {
- "value": "5 123 8902"
- }
- ]
-}
-```
-
-Note the primary subattribute accompanying `email` "my@own.mail" is false but when inserted we provided `true`. This is because the SCIM specification states that after modifications are applied to resources **(PUT or PATCH)**, there cannot be more than one item in a multi-valued attribute with primary value set as `true`.
-
-To see more sample `JSON` payloads, check the `.json` files used by the scim-client test cases referenced above.
-
-### Deleting Users
-For deleting, the `DELETE `method of `HTTP` is used.
+You can refer to [here](../../janssen-server/config-guide/scim-config/user-config.md#updating-a-user-patch) for this topic
-No input file is used in this case. A delete request could be the following:
-```
-curl -k -X DELETE -H 'Authorization: Bearer ACCESS_TOKEN' https:///jans-scim/restv1/v2/Users/
-```
-
-Use the inum of our dummy user, **Jensen Barbara**.
-
-Check your LDAP or via Jans TUI to see that **Bjensen** is gone.
+### Deleting Users
+You can refer to [here](../../janssen-server/config-guide/scim-config/user-config.md#deleting-users) for this topic
## How is SCIM data stored?
-SCIM [schema spec](https://datatracker.ietf.org/doc/html/rfc7643) does not use LDAP attribute names but a different naming convention for resource attributes (note this is not the case of custom attributes where the SCIM name used is that of the LDAP attribute).
-
-It is possible to determine if a given LDAP attribute is being mapped to a SCIM attribute. For that you need to check in Jans TUI `Auth-Server >> Attributes` and click on any attributes. Check `Include in SCIM Extension:` is `true` or `false`. Whenever you try to map any LDAP attribute to a SCIM attribute keep it's value `true`.
-
+You can refer to [here](../../janssen-server/scim/monitoring.md#how-is-scim-data-stored) for this topic
## FIDO Devices
-A FIDO device represents a user credential stored in the Jans Server database that is compliant with the [FIDO](https://fidoalliance.org/) standard. These devices are used as a second factor in a setting of strong authentication.
-
-FIDO devices were superseded by [FIDO 2](#fido-2-devices) devices in Jans Server.
+You can refer to [here](../../janssen-server/fido/monitoring.md#fido-devices) for this topic.
## FIDO 2 devices
-FIDO 2 devices are credentials that adhere to the more current Fido 2.0 initiative (WebAuthn + CTAP). Examples of FIDO 2 devices are USB security keys and Super Gluu devices.
-
-The SCIM endpoints for FIDO 2 allow application developers to query, update and delete already existing devices. Addition of devices do not take place through the service since this process requires direct end-user interaction, ie. device enrolling.
-
-The schema attributes for a device of this kind can be found by hitting the URL `https:///jans-scim/restv1/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:Fido2Device`
-
-To distinguish between regular FIDO2 and SuperGluu devices, note only SuperGluu entries have the attribute `deviceData` populated (i.e. not null)
-
-### Example: Querying Enrolled Devices
-
-Say we are interested in having a list of Super Gluu devices users have enrolled and whose operating system is iOS. We may issue a query like this:
-
-```
-curl -k -G -H 'Authorization: Bearer ACCESS_TOKEN' --data-urlencode
-'filter=deviceData co "ios"' -d count=10 https:///jans-scim/restv1/v2/Fido2Devices
-```
-
-The response will be like:
-
-```
-{
- "totalResults": ...,
- "itemsPerPage": ...,
- "startIndex": 1,
- "schemas": [
- "urn:ietf:params:scim:api:messages:2.0:ListResponse"
- ],
- "Resources": [
- {
- "id": "...",
- "meta": {...},
- "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Fido2Device"],
- "userId": "...",
- ...
- "deviceData": "{...}",
- "displayName": ...,
- },
- ...
- ]
-}
-```
+You can refer to [here](../../janssen-server/fido/monitoring.md#fido2-devices) for this topic.
## Potential performance issues with Group endpoints
diff --git a/docs/janssen-server/vm-ops/jans-command.md b/docs/janssen-server/vm-ops/jans-command.md
new file mode 100644
index 00000000000..dc3607af5f0
--- /dev/null
+++ b/docs/janssen-server/vm-ops/jans-command.md
@@ -0,0 +1,167 @@
+---
+tags:
+- administration
+- vm
+- operations
+- jans wrapper command
+---
+
+
+# Jans Command Overview
+
+The `jans` command is a top-level wrapper script for managing the Janssen Server.
+This guide provides an overview of its usage and available commands. List of
+available commands may change as more commands are added. To see the current
+list of commands available in your installation, run following command at the
+Janssen Server:
+
+```bash title="Command"
+jans
+```
+
+## Available Commands
+
+### Version and build information
+
+Displays the version and build information of the currently installed
+Janssen Server.
+
+```bash title="Command"
+jans version
+```
+![](../../assets/jans_version.png)
+
+
+### CLI
+
+Invokes the Janssen Command-Line Interface.
+
+```bash title="Command"
+jans cli
+```
+
+### TUI
+
+Launches the text-based user interface for Janssen.
+
+```bash title="Command"
+jans tui
+```
+
+### Logs
+
+Shows the log file paths for various Janssen Server modules.
+
+```bash title="Command"
+jans logs
+```
+![](../../assets/jans_logs.png)
+
+
+### Status
+
+Displays the status of Janssen Server module services.
+
+```bash title="Command"
+jans status
+```
+![](../../assets/jans_status.png)
+
+
+### Start
+
+Starts services for the Janssen Server.
+
+```bash title="Command"
+ jans start
+```
+![image](../../assets/jans_start.png)
+
+Start a specific service.
+
+```bash title="sample command"
+jans start -service=jans-config-api
+```
+
+```title="Sample Output"
+Executing sudo systemctl start jans-config-api
+```
+
+
+### Stop
+
+Stops services for the Janssen Server.
+```bash title="Command"
+jans stop
+```
+
+![](../../assets/jans_stop.png)
+
+Stop a specific service.
+
+```bash title="Command"
+jans stop -service=jans-config-api
+```
+```title="Sample Output"
+Executing sudo systemctl stop jans-config-api
+```
+
+
+
+
+### Restart
+
+Restart services for the Janssen Server.
+
+```bash title="Command"
+jans restart
+```
+![](../../assets/jans_restart.png)
+
+Restart a specific service.
+
+```bash title="Sample Command"
+jans restart -service=jans-config-api
+```
+
+```title="Sample Output"
+Executing sudo systemctl restart jans-config-api
+```
+
+
+
+### Health
+
+Retrieves health status from the Janssen services health-check endpoint.
+
+```bash title="Command"
+jans health
+```
+![image](../../assets/jans_health.png)
+
+Health check for specific service.
+
+```bash title="Command"
+jans health -service=
+```
+
+```title="sample Output"
+Checking health status for jans-config-api
+ Executing curl -s http://localhost:8074/jans-config-api/api/v1/health/live
+ Command output: {"name":"jans-config-api liveness","status":"UP"}
+```
+
+
+
+### Info
+
+Lists important URLs such as `.well-known` endpoints.
+
+```bash title="Command"
+jans info
+```
+![](../../assets/jans_info.png)
+
+
+
+
diff --git a/docs/script-catalog/authorization_challenge/AgamaChallenge.java b/docs/script-catalog/authorization_challenge/AgamaChallenge.java
new file mode 100644
index 00000000000..6c272dff776
--- /dev/null
+++ b/docs/script-catalog/authorization_challenge/AgamaChallenge.java
@@ -0,0 +1,338 @@
+import io.jans.as.common.model.common.User;
+import io.jans.as.common.model.session.AuthorizationChallengeSession;
+import io.jans.as.server.authorize.ws.rs.AuthorizationChallengeSessionService;
+import io.jans.as.server.service.UserService;
+import io.jans.as.server.service.external.context.ExternalScriptContext;
+import io.jans.model.SimpleCustomProperty;
+import io.jans.model.custom.script.model.CustomScript;
+import io.jans.model.custom.script.type.authzchallenge.AuthorizationChallengeType;
+import io.jans.orm.PersistenceEntryManager;
+import io.jans.service.cdi.util.CdiUtil;
+import io.jans.service.custom.script.CustomScriptManager;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.jans.agama.engine.model.*;
+import io.jans.agama.engine.misc.FlowUtils;
+import io.jans.agama.engine.service.AgamaPersistenceService;
+import io.jans.agama.NativeJansFlowBridge;
+import io.jans.agama.engine.client.MiniBrowser;
+import io.jans.as.model.configuration.AppConfiguration;
+import io.jans.as.model.util.Base64Util;
+import io.jans.as.server.authorize.ws.rs.AuthzRequest;
+import io.jans.util.*;
+
+import jakarta.servlet.ServletRequest;
+import java.io.IOException;
+import java.util.*;
+
+import org.json.*;
+
+import static io.jans.agama.engine.client.MiniBrowser.Outcome.*;
+
+public class AuthorizationChallenge implements AuthorizationChallengeType {
+
+ //private static final Logger log = LoggerFactory.getLogger(AuthorizationChallenge.class);
+ private static final Logger scriptLogger = LoggerFactory.getLogger(CustomScriptManager.class);
+
+ private String finishIdAttr;
+ private MiniBrowser miniBrowser;
+ private PersistenceEntryManager entryManager;
+ private AuthorizationChallengeSessionService deviceSessionService;
+
+ private boolean makeError(ExternalScriptContext context, AuthorizationChallengeSession deviceSessionObject,
+ boolean doRemoval, String errorId, JSONObject error, int status) {
+
+ JSONObject jobj = new JSONObject();
+ if (deviceSessionObject != null) {
+
+ if (doRemoval) {
+ entryManager.remove(deviceSessionObject.getDn(), AuthorizationChallengeSession.class);
+ } else {
+ jobj.put("auth_session", deviceSessionObject.getId());
+ }
+ }
+
+ String errId = errorId.toLowerCase();
+ jobj.put("error", errId);
+ jobj.put(errId, error);
+
+ context.createWebApplicationException(status, jobj.toString(2) + "\n");
+ return false;
+
+ }
+
+ private boolean makeUnexpectedError(ExternalScriptContext context, AuthorizationChallengeSession deviceSessionObject,
+ String description) {
+
+ JSONObject jobj = new JSONObject(Map.of("description", description));
+ return makeError(context, deviceSessionObject, true, "unexpected_error", jobj, 500);
+
+ }
+
+ private boolean makeMissingParamError(ExternalScriptContext context, String description) {
+
+ JSONObject jobj = new JSONObject(Map.of("description", description));
+ return makeError(context, null, false, "missing_param", jobj, 400);
+
+ }
+
+ private Pair prepareFlow(String sessionId, String flowName) {
+
+ String msg = null;
+ try {
+ String qn = null, inputs = null;
+
+ int i = flowName.indexOf("-");
+ if (i == -1) {
+ qn = flowName;
+ } else if (i == 0) {
+ msg = "Flow name is empty";
+ } else {
+ qn = flowName.substring(0, i);
+ scriptLogger.info("Parsing flow inputs");
+ inputs = Base64Util.base64urldecodeToString(flowName.substring(i + 1));
+ }
+
+ if (qn != null) {
+ NativeJansFlowBridge bridge = CdiUtil.bean(NativeJansFlowBridge.class);
+ Boolean running = bridge.prepareFlow(sessionId, qn, inputs, true);
+
+ if (running == null) {
+ msg = "Flow " + qn + " does not exist or cannot be launched from an application";
+ } else if (running) {
+ msg = "Flow is already in course";
+ } else {
+ return new Pair<>(bridge.getTriggerUrl(), null);
+ }
+ }
+
+ } catch (Exception e) {
+ msg = e.getMessage();
+ scriptLogger.error(msg, e);
+ }
+ return new Pair<>(null, msg);
+
+ }
+
+ private User extractUser(String userId) {
+
+ UserService userService = CdiUtil.bean(UserService.class);
+ List matchingUsers = userService.getUsersByAttribute(finishIdAttr, userId, true, 2);
+ int matches = matchingUsers.size();
+
+ if (matches != 1) {
+ if (matches == 0) {
+ scriptLogger.warn("No user matches the required condition: {}={}", finishIdAttr, userId);
+ } else {
+ scriptLogger.warn("Several users match the required condition: {}={}", finishIdAttr, userId);
+ }
+
+ return null;
+ }
+ return matchingUsers.get(0);
+
+ }
+
+ @Override
+ public boolean authorize(Object scriptContext) {
+
+ ExternalScriptContext context = (ExternalScriptContext) scriptContext;
+
+ if (!CdiUtil.bean(FlowUtils.class).serviceEnabled())
+ return makeUnexpectedError(context, null, "Agama engine is disabled");
+
+ AuthzRequest authRequest = context.getAuthzRequest();
+
+ if (!authRequest.isUseAuthorizationChallengeSession())
+ return makeMissingParamError(context, "Please set 'use_auth_session=true' in your request");
+
+ ServletRequest servletRequest = context.getHttpRequest();
+ AuthorizationChallengeSession deviceSessionObject = authRequest.getAuthorizationChallengeSessionObject();
+
+ boolean noSO = deviceSessionObject == null;
+ scriptLogger.debug("There IS{} device session object", noSO ? " NO" : "");
+
+ Map deviceSessionObjectAttrs = null;
+ String sessionId = null, url = null, payload = null;
+
+ if (noSO) {
+
+ String fname = servletRequest.getParameter("flow_name");
+ if (fname == null)
+ return makeMissingParamError(context, "Parameter 'flow_name' missing in request");
+
+ deviceSessionObject = deviceSessionService.newAuthorizationChallengeSession();
+ sessionId = deviceSessionObject.getId();
+
+ Pair pre = prepareFlow(sessionId, fname);
+ url = pre.getFirst();
+
+ if (url == null) return makeUnexpectedError(context, deviceSessionObject, pre.getSecond());
+
+ deviceSessionObjectAttrs = deviceSessionObject.getAttributes().getAttributes();
+ deviceSessionObjectAttrs.put("url", url);
+ deviceSessionObjectAttrs.put("client_id", servletRequest.getParameter("client_id"));
+ deviceSessionObjectAttrs.put("acr_values", servletRequest.getParameter("acr_values"));
+ deviceSessionObjectAttrs.put("scope", servletRequest.getParameter("scope"));
+
+ deviceSessionService.persist(deviceSessionObject);
+
+ } else {
+ sessionId = deviceSessionObject.getId();
+ deviceSessionObjectAttrs = deviceSessionObject.getAttributes().getAttributes();
+ String userId = deviceSessionObjectAttrs.get("userId");
+
+ if (userId != null) {
+ User user = extractUser(userId);
+
+ if (user == null)
+ return makeUnexpectedError(context, deviceSessionObject, "Unable to determine identity of user");
+
+ context.getExecutionContext().setUser(user);
+ scriptLogger.debug("User {} is authenticated successfully", user.getUserId());
+
+ entryManager.remove(deviceSessionObject.getDn(), AuthorizationChallengeSession.class);
+ return true;
+ }
+
+ url = deviceSessionObjectAttrs.get("url");
+ if (url == null)
+ return makeUnexpectedError(context, deviceSessionObject, "Illegal state - url is missing in device session object");
+
+ payload = servletRequest.getParameter("data");
+ if (payload == null)
+ return makeMissingParamError(context, "Parameter 'data' missing in request");
+ }
+
+ Pair p = miniBrowser.move(sessionId, url, payload);
+ MiniBrowser.Outcome result = p.getFirst();
+ String strRes = result.toString();
+ JSONObject jres = p.getSecond();
+
+ if (result == CLIENT_ERROR || result == ENGINE_ERROR) {
+ return makeError(context, deviceSessionObject, true, strRes, jres, 500);
+
+ } else if (result == FLOW_PAUSED){
+ url = p.getSecond().remove(MiniBrowser.FLOW_PAUSED_URL_KEY).toString();
+ deviceSessionObjectAttrs.put("url", url);
+ deviceSessionService.merge(deviceSessionObject);
+
+ scriptLogger.info("Next url will be {}", url);
+ return makeError(context, deviceSessionObject, false, strRes, jres, 401);
+
+ } else if (result == FLOW_FINISHED) {
+
+ try {
+ AgamaPersistenceService aps = CdiUtil.bean(AgamaPersistenceService.class);
+ FlowStatus fs = aps.getFlowStatus(sessionId);
+
+ if (fs == null)
+ return makeUnexpectedError(context, deviceSessionObject, "Flow is not running");
+
+ FlowResult fr = fs.getResult();
+ if (fr == null)
+ return makeUnexpectedError(context, deviceSessionObject,
+ "The flow finished but the resulting outcome was not found");
+
+ JSONObject jobj = new JSONObject(fr);
+ jobj.remove("aborted"); //just to avoid confusions and questions from users
+
+ if (!fr.isSuccess()) {
+ scriptLogger.info("Flow DID NOT finished successfully");
+ return makeError(context, deviceSessionObject, true, strRes, jobj, 401);
+ }
+
+ String userId = Optional.ofNullable(fr.getData()).map(d -> d.get("userId"))
+ .map(Object::toString).orElse(null);
+
+ if (userId == null)
+ return makeUnexpectedError(context, deviceSessionObject, "Unable to determine identity of user. " +
+ "No userId provided in flow result");
+
+ deviceSessionObjectAttrs.put("userId", userId);
+ deviceSessionService.merge(deviceSessionObject);
+ aps.terminateFlow(sessionId);
+
+ return makeError(context, deviceSessionObject, false, strRes, jobj, 401);
+
+ } catch (IOException e) {
+ return makeUnexpectedError(context, deviceSessionObject, e.getMessage());
+ }
+ } else {
+ return makeUnexpectedError(context, deviceSessionObject, "Illegal state - unexpected outcome " + strRes);
+ }
+
+ }
+
+ @Override
+ public boolean init(Map configurationAttributes) {
+ scriptLogger.info("Initialized Agama AuthorizationChallenge Java custom script");
+ return true;
+ }
+
+ @Override
+ public boolean init(CustomScript customScript, Map configurationAttributes) {
+
+ scriptLogger.info("Initialized Agama AuthorizationChallenge Java custom script.");
+ finishIdAttr = null;
+ String name = "finish_userid_db_attribute";
+ SimpleCustomProperty prop = configurationAttributes.get(name);
+
+ if (prop != null) {
+ finishIdAttr = prop.getValue2();
+ if (StringHelper.isEmpty(finishIdAttr)) {
+ finishIdAttr = null;
+ }
+ }
+
+ if (finishIdAttr == null) {
+ scriptLogger.info("Property '{}' is missing value", name);
+ return false;
+ }
+ scriptLogger.info("DB attribute '{}' will be used to map the identity of userId passed "+
+ "in Finish directives (if any)", finishIdAttr);
+
+ entryManager = CdiUtil.bean(PersistenceEntryManager.class);
+ deviceSessionService = CdiUtil.bean(AuthorizationChallengeSessionService.class);
+ miniBrowser = new MiniBrowser(CdiUtil.bean(AppConfiguration.class).getIssuer());
+ return true;
+
+ }
+
+ @Override
+ public boolean destroy(Map configurationAttributes) {
+ scriptLogger.info("Destroyed Agama AuthorizationChallenge Java custom script.");
+ return true;
+ }
+
+ @Override
+ public int getApiVersion() {
+ return 11;
+ }
+
+ @Override
+ public Map getAuthenticationMethodClaims(Object context) {
+ return Map.of();
+ }
+
+ @Override
+ public void prepareAuthzRequest(Object scriptContext) {
+
+ ExternalScriptContext context = (ExternalScriptContext) scriptContext;
+ AuthzRequest authRequest = context.getAuthzRequest();
+
+ AuthorizationChallengeSession sessionObject = authRequest.getAuthorizationChallengeSessionObject();
+ if (sessionObject != null) {
+ Map sessionAttributes = sessionObject.getAttributes().getAttributes();
+
+ // set scope from session into request object
+ String scopeFromSession = sessionAttributes.get("scope");
+ if (StringUtils.isNotBlank(scopeFromSession) && StringUtils.isBlank(authRequest.getScope())) {
+ authRequest.setScope(scopeFromSession);
+ }
+ }
+ }
+
+}
diff --git a/docs/script-catalog/authorization_challenge/AuthorizationChallenge.java b/docs/script-catalog/authorization_challenge/AuthorizationChallenge.java
index c0554577fed..1a7c02fb809 100644
--- a/docs/script-catalog/authorization_challenge/AuthorizationChallenge.java
+++ b/docs/script-catalog/authorization_challenge/AuthorizationChallenge.java
@@ -221,4 +221,19 @@ public int getApiVersion() {
public Map getAuthenticationMethodClaims(Object context) {
return new HashMap<>();
}
+
+ @Override
+ public void prepareAuthzRequest(Object scriptContext) {
+ ExternalScriptContext context = (ExternalScriptContext) scriptContext;
+ final AuthorizationChallengeSession sessionObject = context.getAuthzRequest().getAuthorizationChallengeSessionObject();
+ if (sessionObject != null) {
+ final Map sessionAttributes = sessionObject.getAttributes().getAttributes();
+
+ // set scope from session into request object
+ final String scopeFromSession = sessionAttributes.get("scope");
+ if (StringUtils.isNotBlank(scopeFromSession) && StringUtils.isBlank(context.getAuthzRequest().getScope())) {
+ context.getAuthzRequest().setScope(scopeFromSession);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/docs/script-catalog/authorization_challenge/authorization-challenge.md b/docs/script-catalog/authorization_challenge/authorization-challenge.md
index 5c92608d8ca..07cfd20aa6d 100644
--- a/docs/script-catalog/authorization_challenge/authorization-challenge.md
+++ b/docs/script-catalog/authorization_challenge/authorization-challenge.md
@@ -44,12 +44,15 @@ The Authorization Challenage script implements the [AuthorizationChallenageType]
|:-----|:------|
|`def authorize(self, context)`| Called when the request is received. |
|`def getAuthenticationMethodClaims(self, context)`| Called to get authn method claims. It is injected into `id_token`. Returns key-value map. |
+|`def prepareAuthzRequest(self, context)`| Prepared authorization request before `authorize` method. It's good place to restore data from session if needed. |
`authorize` method returns true/false which indicates to server whether to issue `authorization_code` in response or not.
If parameters is not present then error has to be created and `false` returned.
If all is good script has to return `true` and it's strongly recommended to set user `context.getExecutionContext().setUser(user);` so AS can keep tracking what exactly user is authenticated.
+`prepareAuthzRequest` should typically be used for authorization request manipulation before `authorize` method is invoked.
+Also if there is multi-step flow where some data are stored in `session` object, it is good place to restore data from session into request (please find example in sample below).
### Objects
| Object name | Object description |
@@ -458,6 +461,19 @@ public class AuthorizationChallenge implements AuthorizationChallengeType {
@Override
public Map getAuthenticationMethodClaims(Object context) {
return new HashMap<>();
+ }
+
+ @Override
+ public void prepareAuthzRequest(Object scriptContext) {
+ ExternalScriptContext context = (ExternalScriptContext) scriptContext;
+ final AuthorizationChallengeSession sessionObject = context.getAuthzRequest().getAuthorizationChallengeSessionObject();
+ if (sessionObject != null) {
+ final Map sessionAttributes = sessionObject.getAttributes().getAttributes();
+ final String scopeFromSession = sessionAttributes.get("scope");
+ if (StringUtils.isNotBlank(scopeFromSession) && StringUtils.isBlank(context.getAuthzRequest().getScope())) {
+ context.getAuthzRequest().setScope(scopeFromSession);
+ }
+ }
}
}
diff --git a/docs/script-catalog/authorization_challenge/multi_step/AuthorizationChallenge.java b/docs/script-catalog/authorization_challenge/multi_step/AuthorizationChallenge.java
index e310a0cb516..e64290519f6 100644
--- a/docs/script-catalog/authorization_challenge/multi_step/AuthorizationChallenge.java
+++ b/docs/script-catalog/authorization_challenge/multi_step/AuthorizationChallenge.java
@@ -196,4 +196,19 @@ public int getApiVersion() {
public Map getAuthenticationMethodClaims(Object context) {
return new HashMap<>();
}
+
+ @Override
+ public void prepareAuthzRequest(Object scriptContext) {
+ ExternalScriptContext context = (ExternalScriptContext) scriptContext;
+ final AuthorizationChallengeSession sessionObject = context.getAuthzRequest().getAuthorizationChallengeSessionObject();
+ if (sessionObject != null) {
+ final Map sessionAttributes = sessionObject.getAttributes().getAttributes();
+
+ // set scope from session into request object
+ final String scopeFromSession = sessionAttributes.get("scope");
+ if (StringUtils.isNotBlank(scopeFromSession) && StringUtils.isBlank(context.getAuthzRequest().getScope())) {
+ context.getAuthzRequest().setScope(scopeFromSession);
+ }
+ }
+ }
}
diff --git a/docs/script-catalog/consent_gathering/sample-script/ConsentGatheringSample.py b/docs/script-catalog/consent_gathering/sample-script/ConsentGatheringSample.py
index 40576be19b1..f0a48a0a49d 100644
--- a/docs/script-catalog/consent_gathering/sample-script/ConsentGatheringSample.py
+++ b/docs/script-catalog/consent_gathering/sample-script/ConsentGatheringSample.py
@@ -30,7 +30,7 @@ def destroy(self, configurationAttributes):
return True
def getApiVersion(self):
- return 1
+ return 11
# Main consent-gather method. Must return True (if gathering performed successfully) or False (if fail).
# All user entered values can be access via Map context.getPageAttributes()
diff --git a/docs/script-catalog/person_authentication/other/fortinet/README.md b/docs/script-catalog/person_authentication/other/fortinet/README.md
index 0d79f21d2c8..c1b44762186 100644
--- a/docs/script-catalog/person_authentication/other/fortinet/README.md
+++ b/docs/script-catalog/person_authentication/other/fortinet/README.md
@@ -5,7 +5,7 @@ This document explains how to configure the Gluu Server so that when a user logs
## Prerequisites
-- A Gluu Server (installation instructions [here](../../../../janssen-server/install/)) which will play the role of RADIUS client
+- A Gluu Server (installation instructions [here](../../../../janssen-server/install/README.md)) which will play the role of RADIUS client
- The [Fortinet script](https://github.com/GluuFederation/oxAuth/blob/master/Server/integrations/fortinet/FortinetExternalAuthenticator.py) (included in the default Gluu Server distribution);
- A Fortinet server which is the RADIUS server.
- The jradius-client [jar library](https://sourceforge.net/projects/jradius-client/files/) added to oxAuth
diff --git a/jans-auth-server/agama/engine/pom.xml b/jans-auth-server/agama/engine/pom.xml
index 0a73dab51d0..668d2deca51 100644
--- a/jans-auth-server/agama/engine/pom.xml
+++ b/jans-auth-server/agama/engine/pom.xml
@@ -9,7 +9,7 @@
io.jansjans-auth-server-parent
- 0.0.0-nightly
+ 1.3.0../../pom.xml
@@ -219,6 +219,15 @@
zip4j2.11.5
+
+
+ com.nimbusds
+ oauth2-oidc-sdk
+
+
+ org.json
+ json
+
@@ -237,11 +246,6 @@
${project.version}test
-
- org.json
- json
- test
- org.apache.logging.log4j
diff --git a/jans-auth-server/agama/engine/src/main/java/io/jans/agama/NativeJansFlowBridge.java b/jans-auth-server/agama/engine/src/main/java/io/jans/agama/NativeJansFlowBridge.java
index c334e4ce8f2..439c8987227 100644
--- a/jans-auth-server/agama/engine/src/main/java/io/jans/agama/NativeJansFlowBridge.java
+++ b/jans-auth-server/agama/engine/src/main/java/io/jans/agama/NativeJansFlowBridge.java
@@ -58,7 +58,7 @@ public Boolean prepareFlow(String sessionId, String qname, String jsonInput, boo
}
if (st == null) {
- int timeout = aps.getEffectiveFlowTimeout(qname);
+ int timeout = aps.getEffectiveFlowTimeout(qname, nativeClient);
if (timeout <= 0) throw new Exception("Flow timeout negative or zero. " +
"Check your AS configuration or flow definition");
long expireAt = System.currentTimeMillis() + 1000L * timeout;
diff --git a/jans-auth-server/agama/engine/src/main/java/io/jans/agama/engine/client/MiniBrowser.java b/jans-auth-server/agama/engine/src/main/java/io/jans/agama/engine/client/MiniBrowser.java
new file mode 100644
index 00000000000..6859380f3e2
--- /dev/null
+++ b/jans-auth-server/agama/engine/src/main/java/io/jans/agama/engine/client/MiniBrowser.java
@@ -0,0 +1,211 @@
+package io.jans.agama.engine.client;
+
+import com.nimbusds.oauth2.sdk.http.HTTPRequest;
+import com.nimbusds.oauth2.sdk.http.HTTPResponse;
+
+import io.jans.util.Pair;
+
+import jakarta.ws.rs.core.*;
+import jakarta.ws.rs.core.Response.Status.Family;
+import java.io.IOException;
+import java.net.*;
+import java.util.*;
+
+import org.json.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.nimbusds.oauth2.sdk.http.HTTPRequest.Method.*;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+class WebResponse {
+
+ private int status;
+ private String body;
+ private String contentType;
+
+ private WebResponse() { }
+
+ static WebResponse from(HTTPResponse response) {
+
+ WebResponse wr = new WebResponse();
+ wr.status = response.getStatusCode();
+ wr.contentType = response.getHeaderValue(HttpHeaders.CONTENT_TYPE);
+ wr.body = response.getBody();
+ return wr;
+
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+}
+
+/**
+ * A micro HTTP client capable of interacting with the Agama engine in order to run JSON-based
+ * flows. It lessens the effort of exchanging messages with the engine and serves as a utility
+ * to make possible the fact of running Agama flows in native applications.
+ * The 'move' method implements the POST-REDIRECT-GET pattern of the engine: at every step, the
+ * URL passed is supplied with the given JSON contents, and the HTTP redirect is followed. The
+ * method returns one of the possible outcomes (see Outcome enum) plus some JSON data.
+ * This is an explanation of outcomes: CLIENT_ERROR (problems to connect to the URL or read the
+ * response), ENGINE_ERROR (the flow crashed, timed out, or an RFAC instruction was reached),
+ * FLOW_FINISHED (a Finish instruction was executed), and FLOW_PAUSED (RRF instruction was hit).
+ * The JSON data returned by 'move' contains error data (CLIENT_ERROR or ENGINE_ERROR), the data
+ * associated to the Finish instruction (FLOW_FINISHED), or the data supplied to the RRF instruction
+ * (FLOW_PAUSED). Only in the last case the method may receive a subsequent invocation, where the
+ * JSON data to supply is supposed to emulate the output of the RRF execution, that is, the result
+ * of having submitted a UI form in the app (desktop or mobile).
+ * Thus, it is the native app that takes charge of the UI rendering by receiving the same data
+ * the equivalent Freemarker template would receive (in the web world), and the data of the form
+ * submission is built by the native app too. In this case the path to the UI template (in RRF)
+ * has no effect, but it is anyways included in the output of 'move' for reference.
+ */
+public class MiniBrowser {
+
+ public enum Outcome { CLIENT_ERROR, ENGINE_ERROR, FLOW_FINISHED, FLOW_PAUSED }
+
+ public static final String FLOW_PAUSED_URL_KEY = "_url";
+
+ private static final Logger logger = LoggerFactory.getLogger(MiniBrowser.class);
+
+ private String rootUrl;
+ private int connectionTimeout;
+ private int readTimeout;
+ private int maxErrorContentLength;
+
+ public MiniBrowser(String rootUrl) {
+ this(rootUrl, 3500, 3500, 4096);
+ }
+
+ public MiniBrowser(String rootUrl,
+ int connectionTimeout, int readTimeout, int maxErrorContentLength) {
+
+ this.rootUrl = rootUrl;
+ this.connectionTimeout = connectionTimeout;
+ this.readTimeout = readTimeout;
+ this.maxErrorContentLength = maxErrorContentLength;
+
+ }
+
+ public Pair move(String phantomSid, String relativeUrl, String jsonPayload) {
+
+ try {
+ return moveImpl(phantomSid, relativeUrl, jsonPayload);
+ } catch (Exception e) {
+ String error = e.getMessage();
+ logger.error(error, e);
+
+ JSONObject jobj = new JSONObject(Map.of("description", error));
+ return new Pair<>(Outcome.CLIENT_ERROR, jobj);
+ }
+
+ }
+
+ private Pair moveImpl(String phantomSid, String relativeUrl, String jsonPayload)
+ throws Exception {
+
+ String error = null;
+ String url = normalize(relativeUrl);
+ logger.info("Moving forward from {}", url);
+
+ HTTPResponse response = sendRequest(phantomSid, new URL(url), jsonPayload);
+ WebResponse wr = WebResponse.from(response);
+ int status = wr.getStatus();
+
+ if (Family.familyOf(status).equals(Family.REDIRECTION)) {
+ String location = response.getHeaderValue(HttpHeaders.LOCATION);
+
+ if (location != null) {
+ wr = null;
+ logger.info("Redirecting to {}", location);
+
+ response = sendRequest(phantomSid, new URL(normalize(location)), null);
+ wr = WebResponse.from(response);
+
+ if (MediaType.APPLICATION_JSON.equals(wr.getContentType()) && wr.getStatus() == 200) {
+
+ logger.info("Returning JSON contents");
+ JSONObject jobj = new JSONObject(wr.getBody());
+
+ jobj.put(FLOW_PAUSED_URL_KEY, location);
+ return new Pair<>(Outcome.FLOW_PAUSED, jobj);
+ }
+
+ error = "Expecting OK JSON response for " + location;
+
+ } else {
+ error = "Target of redirection is missing";
+ }
+ } else if (MediaType.APPLICATION_JSON.equals(wr.getContentType()) && status == 200) {
+
+ logger.info("Seems to have landed to the finish page");
+ JSONObject jobj = new JSONObject(wr.getBody());
+
+ if (jobj.has("success")) return new Pair<>(Outcome.FLOW_FINISHED, jobj);
+
+ error = "Unexpected response to " + url;
+
+ } else {
+ error = "Unexpected response to " + url;
+ }
+
+ logger.error(error);
+ JSONObject jobj = new JSONObject(Map.of("description", error));
+
+ String contentType = wr.getContentType();
+ jobj.put("status", wr.getStatus());
+ jobj.put("contentType", Optional.ofNullable((Object) contentType).orElse(JSONObject.NULL));
+
+ String body = wr.getBody();
+ if (body == null) {
+ jobj.put("body", JSONObject.NULL);
+ } else if (MediaType.APPLICATION_JSON.equals(contentType)) {
+ jobj.put("body", new JSONObject(body));
+ } else {
+ body = body.substring(0, Math.min(body.length(), maxErrorContentLength));
+ jobj.put("body", body);
+ }
+
+ return new Pair<>(Outcome.ENGINE_ERROR, jobj);
+
+ }
+
+ private HTTPResponse sendRequest(String phantomSid, URL url, String jsonPayload) throws IOException {
+
+ boolean noPayload = jsonPayload == null;
+ HTTPRequest request = new HTTPRequest(noPayload ? GET : POST, url);
+ request.setConnectTimeout(connectionTimeout);
+ request.setReadTimeout(readTimeout);
+ //Ideally, redirects should be followed, but cookies are lost between requests :(
+ request.setFollowRedirects(false);
+ //... and without following redirects, the content-type has to be passed at every request :(
+
+ //the presence of this header signals the engine not to read the incoming data as application/x-www-form-urlencoded
+ //and also to use the json version of engine's error pages
+ request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
+ //sending the session_id cookie helps maintain the state of the running flow between server/client
+ request.setHeader(HttpHeaders.COOKIE, String.format("session_id=%s;", phantomSid));
+
+ if (!noPayload) {
+ request.setBody(jsonPayload);
+ }
+ return request.send();
+
+ }
+
+ private String normalize(String relativeUrl) {
+ String url = relativeUrl.startsWith(rootUrl) ? "" : rootUrl;
+ return url + relativeUrl;
+ }
+
+}
diff --git a/jans-auth-server/agama/engine/src/main/java/io/jans/agama/engine/service/AgamaPersistenceService.java b/jans-auth-server/agama/engine/src/main/java/io/jans/agama/engine/service/AgamaPersistenceService.java
index 89e95632222..659a6de7da5 100644
--- a/jans-auth-server/agama/engine/src/main/java/io/jans/agama/engine/service/AgamaPersistenceService.java
+++ b/jans-auth-server/agama/engine/src/main/java/io/jans/agama/engine/service/AgamaPersistenceService.java
@@ -111,19 +111,25 @@ public boolean flowEnabled(String flowName) {
}
- public int getEffectiveFlowTimeout(String flowName) {
+ public int getEffectiveFlowTimeout(String flowName, boolean nativeClient) {
Flow fl = entryManager.findEntries(AGAMA_FLOWS_BASE, Flow.class,
Filter.createEqualityFilter(Flow.ATTR_NAMES.QNAME, flowName),
new String[]{ Flow.ATTR_NAMES.META }, 1).get(0);
- int unauth = appConfiguration.getSessionIdUnauthenticatedUnusedLifetime();
+ int unauth = appConfiguration.getSessionIdUnauthenticatedUnusedLifetime();
+ if (nativeClient) {
+ unauth = Optional.ofNullable(
+ appConfiguration.getAuthorizationChallengeSessionLifetimeInSeconds())
+ .orElse(unauth);
+ }
+
Integer flowTimeout = fl.getMetadata().getTimeout();
int timeout = Optional.ofNullable(flowTimeout).map(Integer::intValue).orElse(unauth);
return Math.min(unauth, timeout);
}
-
+
public Flow getFlow(String flowName, boolean full) throws IOException {
try {
diff --git a/jans-auth-server/agama/engine/src/test/java/io/jans/agama/test/BaseTest.java b/jans-auth-server/agama/engine/src/test/java/io/jans/agama/test/BaseTest.java
index acb5ae64f19..c8f799c9f30 100644
--- a/jans-auth-server/agama/engine/src/test/java/io/jans/agama/test/BaseTest.java
+++ b/jans-auth-server/agama/engine/src/test/java/io/jans/agama/test/BaseTest.java
@@ -155,11 +155,11 @@ void validateFinishPage(HtmlPage page, boolean success) {
}
void assertOK(Page page) {
- assertEquals(page.getWebResponse().getStatusCode(), WebResponse.OK);
+ assertEquals(page.getWebResponse().getStatusCode(), 200);
}
void assertServerError(Page page) {
- assertEquals(page.getWebResponse().getStatusCode(), WebResponse.INTERNAL_SERVER_ERROR);
+ assertEquals(page.getWebResponse().getStatusCode(), 500);
}
void assertTextContained(String text, String ...words) {
diff --git a/jans-auth-server/agama/engine/src/test/java/io/jans/agama/test/CustomConfigsFlowTest.java b/jans-auth-server/agama/engine/src/test/java/io/jans/agama/test/CustomConfigsFlowTest.java
index 83ce021096a..09c64fa2d65 100644
--- a/jans-auth-server/agama/engine/src/test/java/io/jans/agama/test/CustomConfigsFlowTest.java
+++ b/jans-auth-server/agama/engine/src/test/java/io/jans/agama/test/CustomConfigsFlowTest.java
@@ -25,10 +25,10 @@ public void withTimeout() {
int status = page.getWebResponse().getStatusCode();
String text = page.getVisibleText().toLowerCase();
- if (status == WebResponse.OK) {
+ if (status == 410) {
//See timeout.ftlh
assertTextContained(text, "took", "more", "expected");
- } else if (status == WebResponse.NOT_FOUND) {
+ } else if (status == 404) {
//See mismatch.ftlh
assertTextContained(text, "not", "found");
} else {
diff --git a/jans-auth-server/agama/inboundID/pom.xml b/jans-auth-server/agama/inboundID/pom.xml
index 95dd3a28b2d..86c6792934a 100644
--- a/jans-auth-server/agama/inboundID/pom.xml
+++ b/jans-auth-server/agama/inboundID/pom.xml
@@ -18,7 +18,7 @@
io.jansjans-auth-server-parent
- 0.0.0-nightly
+ 1.3.0../../pom.xml
diff --git a/jans-auth-server/agama/model/pom.xml b/jans-auth-server/agama/model/pom.xml
index 2c9a19fe788..650bbcda8b1 100644
--- a/jans-auth-server/agama/model/pom.xml
+++ b/jans-auth-server/agama/model/pom.xml
@@ -9,7 +9,7 @@
io.jansjans-auth-server-parent
- 0.0.0-nightly
+ 1.3.0../../pom.xml
diff --git a/jans-auth-server/client/pom.xml b/jans-auth-server/client/pom.xml
index e97a272ee7f..46e3fc9a2a1 100644
--- a/jans-auth-server/client/pom.xml
+++ b/jans-auth-server/client/pom.xml
@@ -8,7 +8,7 @@
io.jansjans-auth-server-parent
- 0.0.0-nightly
+ 1.3.0
diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/SelectAccountHttpTest.java b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/SelectAccountHttpTest.java
index 5f1788fb3d0..7e93a231e14 100644
--- a/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/SelectAccountHttpTest.java
+++ b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/SelectAccountHttpTest.java
@@ -20,7 +20,6 @@
import io.jans.as.model.jwt.JwtHeaderName;
import org.apache.logging.log4j.util.Strings;
import org.json.JSONArray;
-import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Parameters;
@@ -42,7 +41,7 @@ public class SelectAccountHttpTest extends BaseTest {
@BeforeTest
public void setUp() {
- driver = new HtmlUnitDriver(true);
+ startSelenium();
pageConfig = newPageConfig(driver);
}
diff --git a/jans-auth-server/common/pom.xml b/jans-auth-server/common/pom.xml
index e6445e1c69a..eb9267f0a56 100644
--- a/jans-auth-server/common/pom.xml
+++ b/jans-auth-server/common/pom.xml
@@ -8,7 +8,7 @@
io.jansjans-auth-server-parent
- 0.0.0-nightly
+ 1.3.0
diff --git a/jans-auth-server/model/pom.xml b/jans-auth-server/model/pom.xml
index 8a740c09428..f0f2ec88416 100644
--- a/jans-auth-server/model/pom.xml
+++ b/jans-auth-server/model/pom.xml
@@ -8,7 +8,7 @@
io.jansjans-auth-server-parent
- 0.0.0-nightly
+ 1.3.0
diff --git a/jans-auth-server/persistence-model/pom.xml b/jans-auth-server/persistence-model/pom.xml
index 598f9b7bb59..9e1fe618953 100644
--- a/jans-auth-server/persistence-model/pom.xml
+++ b/jans-auth-server/persistence-model/pom.xml
@@ -4,7 +4,7 @@
io.jansjans-auth-server-parent
- 0.0.0-nightly
+ 1.3.0jans-auth-persistence-modelPersistence model
diff --git a/jans-auth-server/pom.xml b/jans-auth-server/pom.xml
index 05999ad6882..5850e5200c8 100644
--- a/jans-auth-server/pom.xml
+++ b/jans-auth-server/pom.xml
@@ -5,7 +5,7 @@
io.jansjans-auth-server-parentpom
- 0.0.0-nightly
+ 1.3.0Jans authentication server parent
diff --git a/jans-auth-server/server-fips/pom.xml b/jans-auth-server/server-fips/pom.xml
index 207baf86397..e066e58e98a 100644
--- a/jans-auth-server/server-fips/pom.xml
+++ b/jans-auth-server/server-fips/pom.xml
@@ -9,7 +9,7 @@
io.jansjans-auth-server-parent
- 0.0.0-nightly
+ 1.3.0
diff --git a/jans-auth-server/server/conf/jans-config.json b/jans-auth-server/server/conf/jans-config.json
index 04b5dde5654..1c50a375ba0 100644
--- a/jans-auth-server/server/conf/jans-config.json
+++ b/jans-auth-server/server/conf/jans-config.json
@@ -20,7 +20,8 @@
"par",
"ssa",
"global_token_revocation",
- "status_list"
+ "status_list",
+ "access_evaluation"
],
"issuer":"${config.oxauth.issuer}",
"loginPage":"${config.oxauth.contextPath}/login.htm",
diff --git a/jans-auth-server/server/pom.xml b/jans-auth-server/server/pom.xml
index 6c08b7e9434..a679cd37894 100644
--- a/jans-auth-server/server/pom.xml
+++ b/jans-auth-server/server/pom.xml
@@ -8,7 +8,7 @@
io.jansjans-auth-server-parent
- 0.0.0-nightly
+ 1.3.0
diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeService.java
index 2cc8ece3184..5d71f5aa3dd 100644
--- a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeService.java
+++ b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizationChallengeService.java
@@ -114,6 +114,8 @@ public Response requestAuthorization(AuthzRequest authzRequest) {
public void prepareAuthzRequest(AuthzRequest authzRequest) {
authzRequest.setScope(ServerUtil.urlDecode(authzRequest.getScope()));
+ externalAuthorizationChallengeService.externalPrepareAuthzRequest(authzRequest);
+
if (StringUtils.isNotBlank(authzRequest.getAuthorizationChallengeSession())) {
final AuthorizationChallengeSession session = authorizationChallengeSessionService.getAuthorizationChallengeSession(authzRequest.getAuthorizationChallengeSession());
@@ -158,7 +160,7 @@ public Response authorize(AuthzRequest authzRequest) throws IOException, TokenBi
executionContext.setSessionId(sessionUser);
if (user == null) {
- log.trace("Executing external authentication challenge");
+ log.trace("Executing external authentication challenge ... (requestedScopes: {})", scopes);
final boolean ok = externalAuthorizationChallengeService.externalAuthorize(executionContext);
if (!ok) {
@@ -179,6 +181,8 @@ public Response authorize(AuthzRequest authzRequest) throws IOException, TokenBi
String grantAcr = executionContext.getScript() != null ? executionContext.getScript().getName() : authzRequest.getAcrValues();
+ log.trace("Creating authorization code grant with: scope {}, acr {}", scopes, grantAcr);
+
AuthorizationCodeGrant authorizationGrant = authorizationGrantList.createAuthorizationCodeGrant(user, client, new Date());
authorizationGrant.setNonce(authzRequest.getNonce());
authorizationGrant.setJwtAuthorizationRequest(authzRequest.getJwtRequest());
diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizeAction.java b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizeAction.java
index a5a96ce2de4..9433e274ce3 100644
--- a/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizeAction.java
+++ b/jans-auth-server/server/src/main/java/io/jans/as/server/authorize/ws/rs/AuthorizeAction.java
@@ -86,6 +86,7 @@
@Named
public class AuthorizeAction {
+ public static final String UNKNOWN = "Unknown";
@Inject
private Logger log;
@@ -985,10 +986,28 @@ public String getClientDisplayName() {
log.trace("client {}", clientId);
if (StringUtils.isBlank(clientId)) {
- return "Unknown";
+ return UNKNOWN;
}
final Client client = clientService.getClient(clientId);
+ return getCheckedClientDisplayName(client);
+ }
+
+ public String getClientDisplayName(final Client client) {
+ log.trace("client {}", client);
+
+ if (client == null) {
+ return UNKNOWN;
+ }
+
+ return getCheckedClientDisplayName(client);
+ }
+
+ private String getCheckedClientDisplayName(final Client client) {
+ if (client == null) {
+ return UNKNOWN;
+ }
+
if (StringUtils.isNotBlank(client.getClientName())) {
return client.getClientName();
}
@@ -997,8 +1016,8 @@ public String getClientDisplayName() {
return client.getClientId();
}
- return "Unknown";
- }
+ return UNKNOWN;
+ }
public String getAuthReqId() {
return authReqId;
diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalAuthorizationChallengeService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalAuthorizationChallengeService.java
index 36700006f0e..afe6ecb1505 100644
--- a/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalAuthorizationChallengeService.java
+++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalAuthorizationChallengeService.java
@@ -3,6 +3,7 @@
import io.jans.as.model.authorize.AuthorizeErrorResponseType;
import io.jans.as.model.configuration.AppConfiguration;
import io.jans.as.model.error.ErrorResponseFactory;
+import io.jans.as.server.authorize.ws.rs.AuthzRequest;
import io.jans.as.server.model.common.ExecutionContext;
import io.jans.as.server.service.external.context.ExternalScriptContext;
import io.jans.model.custom.script.CustomScriptType;
@@ -134,4 +135,45 @@ public CustomScriptConfiguration identifyScript(List acrValues) {
log.trace("Unable to find script by acr_values {}", acrValues);
return getCustomScriptConfigurationByName(appConfiguration.getAuthorizationChallengeDefaultAcr());
}
+
+ public void externalPrepareAuthzRequest(AuthzRequest authzRequest) {
+ final List acrValues = authzRequest.getAcrValuesList();
+ final CustomScriptConfiguration script = identifyScript(acrValues);
+ if (script == null) {
+ String msg = String.format("Unable to identify script by acr_values %s.", acrValues);
+ log.debug(msg);
+ throw new WebApplicationException(errorResponseFactory
+ .newErrorResponse(Response.Status.BAD_REQUEST)
+ .entity(errorResponseFactory.getErrorAsJson(AuthorizeErrorResponseType.INVALID_REQUEST, authzRequest.getState(), msg))
+ .build());
+ }
+
+ log.trace("Executing python 'prepareAuthzRequest' method, script name: {}, clientId: {}, scope: {}, authorizationChallengeSession: {}",
+ script.getName(), authzRequest.getClientId(), authzRequest.getScope(), authzRequest.getAuthorizationChallengeSession());
+
+ ExecutionContext executionContext = ExecutionContext.of(authzRequest);
+ executionContext.setScript(script);
+
+ try {
+ AuthorizationChallengeType authorizationChallengeType = (AuthorizationChallengeType) script.getExternalType();
+ final ExternalScriptContext scriptContext = new ExternalScriptContext(executionContext);
+ authorizationChallengeType.prepareAuthzRequest(scriptContext);
+
+ scriptContext.throwWebApplicationExceptionIfSet();
+ } catch (WebApplicationException e) {
+ if (log.isTraceEnabled()) {
+ log.trace("WebApplicationException from script", e);
+ }
+ throw e;
+ } catch (Exception ex) {
+ log.error(ex.getMessage(), ex);
+ saveScriptError(script.getCustomScript(), ex);
+ throw new WebApplicationException(errorResponseFactory
+ .newErrorResponse(Response.Status.INTERNAL_SERVER_ERROR)
+ .entity(errorResponseFactory.getErrorAsJson(AuthorizeErrorResponseType.ACCESS_DENIED, executionContext.getAuthzRequest().getState(), "Unable to run 'prepareAuthzRequest' method authorization challenge script."))
+ .build());
+ }
+
+ log.trace("Finished 'prepareAuthzRequest' method, script name: {}, clientId: {}", script.getName(), executionContext.getAuthzRequest().getClientId());
+ }
}
diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceImpl.java b/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceImpl.java
index b32651ec9f2..e51cf97022c 100644
--- a/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceImpl.java
+++ b/jans-auth-server/server/src/main/java/io/jans/as/server/token/ws/rs/TokenRestWebServiceImpl.java
@@ -442,13 +442,14 @@ private Response processAuthorizationCode(String code, String scope, String code
executionContext.setGrant(authorizationCodeGrant);
log.trace("AuthorizationCodeGrant : '{}'", authorizationCodeGrant);
+ // if authorization code is not found then code was already used or wrong client provided = remove all grants with this auth code
+ tokenRestWebServiceValidator.validateGrant(authorizationCodeGrant, client, code, executionContext.getAuditLog(), grant -> grantService.removeAllByAuthorizationCode(code));
+
// validate redirectUri only for Authorization Code Flow. For First-Party App redirect uri is blank. It is perfectly valid case.
+ // redirect uri must be validated after grant is validated
if (!authorizationCodeGrant.isAuthorizationChallenge()) {
tokenRestWebServiceValidator.validateRedirectUri(redirectUri, executionContext.getAuditLog());
}
-
- // if authorization code is not found then code was already used or wrong client provided = remove all grants with this auth code
- tokenRestWebServiceValidator.validateGrant(authorizationCodeGrant, client, code, executionContext.getAuditLog(), grant -> grantService.removeAllByAuthorizationCode(code));
tokenRestWebServiceValidator.validatePKCE(authorizationCodeGrant, codeVerifier, executionContext.getAuditLog());
dPoPService.validateDpopThumprint(authorizationCodeGrant.getDpopJkt(), executionContext.getDpop());
diff --git a/jans-auth-server/server/src/main/webapp/WEB-INF/incl/layout/authorize-extended-template.xhtml b/jans-auth-server/server/src/main/webapp/WEB-INF/incl/layout/authorize-extended-template.xhtml
index e8b03eafc30..57a4ac12f27 100644
--- a/jans-auth-server/server/src/main/webapp/WEB-INF/incl/layout/authorize-extended-template.xhtml
+++ b/jans-auth-server/server/src/main/webapp/WEB-INF/incl/layout/authorize-extended-template.xhtml
@@ -46,7 +46,7 @@
+ value="#{authorizeAction.getClientDisplayName(client)}" />
@@ -56,8 +56,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/jans-cedarling/bindings/cedarling_wasm/src/lib.rs b/jans-cedarling/bindings/cedarling_wasm/src/lib.rs
new file mode 100644
index 00000000000..5fb8c52eaf7
--- /dev/null
+++ b/jans-cedarling/bindings/cedarling_wasm/src/lib.rs
@@ -0,0 +1,323 @@
+// This software is available under the Apache-2.0 license.
+// See https://www.apache.org/licenses/LICENSE-2.0.txt for full text.
+//
+// Copyright (c) 2024, Gluu, Inc.
+
+use cedarling::bindings::cedar_policy;
+use cedarling::{BootstrapConfig, BootstrapConfigRaw, LogStorage, Request};
+use serde::ser::{Serialize, SerializeStruct, Serializer};
+use serde_json::json;
+use serde_wasm_bindgen::Error;
+use std::rc::Rc;
+use wasm_bindgen::prelude::*;
+use wasm_bindgen_futures::js_sys::{Array, Map, Object, Reflect};
+
+#[cfg(test)]
+mod tests;
+
+/// The instance of the Cedarling application.
+#[wasm_bindgen]
+#[derive(Clone)]
+pub struct Cedarling {
+ instance: cedarling::Cedarling,
+}
+
+/// Create a new instance of the Cedarling application.
+/// This function can take as config parameter the eather `Map` other `Object`
+#[wasm_bindgen]
+pub async fn init(config: JsValue) -> Result {
+ if config.is_instance_of::