diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 59bf47437fe..b6fb98a0c05 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -21,7 +21,7 @@
/jans-fido2/ @yurem
/jans-scim/ @jgomer2001
/jans-config-api/ @pujavs @yuriyz
-/jans-cli/ @devrimyatar
+/jans-cli-tui/ @devrimyatar
/jans-linux-setup/ @devrimyatar @smansoft @yuriyz @yurem
/jans-linux-setup/jans_setup/setup_app/version.py @moabu
/jans-linux-setup/static/scripts/admin_ui_plugin.py @devrimyatar @duttarnab
diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml
index 9affed0a13d..3069b0f1b33 100644
--- a/.github/workflows/build-packages.yml
+++ b/.github/workflows/build-packages.yml
@@ -77,7 +77,7 @@ jobs:
cp -r /opt/dist jans-src/opt/
cp -r /opt/jans jans-src/opt/
touch jans-src/opt/jans/jans-setup/package
- rm -rf install.py install jans-cli
+ rm -rf install.py install jans-cli-tui
rm -rf jans-src/opt/jans/jans-setup/logs/setup.log
rm -rf jans-src/opt/jans/jans-setup/logs/setup_error.log
sed -i "s/%VERSION%/${{ steps.previoustag.outputs.version }}/g" run-build.sh
@@ -134,10 +134,10 @@ jobs:
make zipapp
mv jans-linux-setup.pyz jans-linux-suse-X86-64-setup.pyz
sha256sum jans-linux-suse-X86-64-setup.pyz > jans-linux-suse-X86-64-setup.pyz.sha256sum
- cd ../jans-cli
+ cd ../jans-cli-tui
make zipapp
- mv config-cli.pyz jans-cli-linux-suse-X86-64.pyz
- sha256sum jans-cli-linux-suse-X86-64.pyz > jans-cli-linux-suse-X86-64.pyz.sha256sum
+ mv config-cli.pyz jans-cli-tui-linux-suse-X86-64.pyz
+ sha256sum jans-cli-tui-linux-suse-X86-64.pyz > jans-cli-tui-linux-suse-X86-64.pyz.sha256sum
# To be removed once we get SUSE build working
- uses: addnab/docker-run-action@v3
name: Build with CentOS7
@@ -163,10 +163,10 @@ jobs:
make zipapp
mv jans-linux-setup.pyz jans-linux-X86-64-setup.pyz
sha256sum jans-linux-X86-64-setup.pyz > jans-linux-X86-64-setup.pyz.sha256sum
- cd ../jans-cli
+ cd ../jans-cli-tui
make zipapp
- mv config-cli.pyz jans-cli-linux-X86-64.pyz
- sha256sum jans-cli-linux-X86-64.pyz > jans-cli-linux-X86-64.pyz.sha256sum
+ mv config-cli.pyz jans-cli-tui-linux-X86-64.pyz
+ sha256sum jans-cli-tui-linux-X86-64.pyz > jans-cli-tui-linux-X86-64.pyz.sha256sum
- name: Set up Python 3.6
uses: actions/setup-python@v4
with:
@@ -183,10 +183,10 @@ jobs:
make zipapp
mv jans-linux-setup.pyz jans-linux-ubuntu-X86-64-setup.pyz
sha256sum jans-linux-ubuntu-X86-64-setup.pyz > jans-linux-ubuntu-X86-64-setup.pyz.sha256sum
- cd ../jans-cli
+ cd ../jans-cli-tui
make zipapp
- mv config-cli.pyz jans-cli-linux-ubuntu-X86-64.pyz
- sha256sum jans-cli-linux-ubuntu-X86-64.pyz > jans-cli-linux-ubuntu-X86-64.pyz.sha256sum
+ mv config-cli.pyz jans-cli-tui-linux-ubuntu-X86-64.pyz
+ sha256sum jans-cli-tui-linux-ubuntu-X86-64.pyz > jans-cli-tui-linux-ubuntu-X86-64.pyz.sha256sum
- uses: actions/cache@v3
id: cache-installers
with:
@@ -197,12 +197,12 @@ jobs:
${{github.workspace}}/jans-linux-setup/jans-linux-suse-X86-64-setup.pyz.sha256sum
${{github.workspace}}/jans-linux-setup/jans-linux-ubuntu-X86-64-setup.pyz
${{github.workspace}}/jans-linux-setup/jans-linux-ubuntu-X86-64-setup.pyz.sha256sum
- ${{github.workspace}}/jans-cli/jans-cli-linux-X86-64.pyz
- ${{github.workspace}}/jans-cli/jans-cli-linux-X86-64.pyz.sha256sum
- ${{github.workspace}}/jans-cli/jans-cli-linux-suse-X86-64.pyz
- ${{github.workspace}}/jans-cli/jans-cli-linux-suse-X86-64.pyz.sha256sum
- ${{github.workspace}}/jans-cli/jans-cli-linux-ubuntu-X86-64.pyz
- ${{github.workspace}}/jans-cli/jans-cli-linux-ubuntu-X86-64.pyz.sha256sum
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-X86-64.pyz
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-X86-64.pyz.sha256sum
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-suse-X86-64.pyz
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-suse-X86-64.pyz.sha256sum
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-ubuntu-X86-64.pyz
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-ubuntu-X86-64.pyz.sha256sum
key: ${{ github.sha }}
upload_python_packages:
@@ -224,12 +224,12 @@ jobs:
${{github.workspace}}/jans-linux-setup/jans-linux-suse-X86-64-setup.pyz.sha256sum
${{github.workspace}}/jans-linux-setup/jans-linux-ubuntu-X86-64-setup.pyz
${{github.workspace}}/jans-linux-setup/jans-linux-ubuntu-X86-64-setup.pyz.sha256sum
- ${{github.workspace}}/jans-cli/jans-cli-linux-X86-64.pyz
- ${{github.workspace}}/jans-cli/jans-cli-linux-X86-64.pyz.sha256sum
- ${{github.workspace}}/jans-cli/jans-cli-linux-suse-X86-64.pyz
- ${{github.workspace}}/jans-cli/jans-cli-linux-suse-X86-64.pyz.sha256sum
- ${{github.workspace}}/jans-cli/jans-cli-linux-ubuntu-X86-64.pyz
- ${{github.workspace}}/jans-cli/jans-cli-linux-ubuntu-X86-64.pyz.sha256sum
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-X86-64.pyz
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-X86-64.pyz.sha256sum
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-suse-X86-64.pyz
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-suse-X86-64.pyz.sha256sum
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-ubuntu-X86-64.pyz
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-ubuntu-X86-64.pyz.sha256sum
key: ${{ github.sha }}
- name: Get latest tag
id: previoustag
@@ -262,8 +262,8 @@ jobs:
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.MOAUTO_WORKFLOW_TOKEN }}
- file: ${{github.workspace}}/jans-cli/jans-cli-linux-${{ matrix.name }}-X86-64.pyz
- asset_name: jans-cli-linux-${{ matrix.name }}-X86-64.pyz
+ file: ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-${{ matrix.name }}-X86-64.pyz
+ asset_name: jans-cli-tui-linux-${{ matrix.name }}-X86-64.pyz
tag: ${{ steps.previoustag.outputs.tag }}
- name: Upload checksum to release
id: upload_shas_cli
@@ -271,6 +271,6 @@ jobs:
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.MOAUTO_WORKFLOW_TOKEN }}
- file: ${{github.workspace}}/jans-cli/jans-cli-linux-${{ matrix.name }}-X86-64.pyz.sha256sum
- asset_name: jans-cli-linux-${{ matrix.name }}-X86-64.pyz.sha256sum
+ file: ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-${{ matrix.name }}-X86-64.pyz.sha256sum
+ asset_name: jans-cli-tui-linux-${{ matrix.name }}-X86-64.pyz.sha256sum
tag: ${{ steps.previoustag.outputs.tag }}
diff --git a/.github/workflows/central_code_quality_check.yml b/.github/workflows/central_code_quality_check.yml
index c2f13cac27a..8f0778ae02b 100644
--- a/.github/workflows/central_code_quality_check.yml
+++ b/.github/workflows/central_code_quality_check.yml
@@ -20,7 +20,7 @@ on:
- 'jans-eleven/**'
- 'agama/**'
- 'jans-linux-setup/**'
- - 'jans-cli/**'
+ - 'jans-cli-tui/**'
- 'jans-pycloudlib/**'
- '!**/CHANGELOG.md'
- '!**.txt'
@@ -42,7 +42,7 @@ on:
- 'jans-eleven/**'
- 'agama/**'
- 'jans-linux-setup/**'
- - 'jans-cli/**'
+ - 'jans-cli-tui/**'
- 'jans-pycloudlib/**'
- '!**/CHANGELOG.md'
- '!**.txt'
@@ -56,7 +56,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- module: [jans-auth-server, agama, jans-config-api, jans-core, jans-linux-setup, jans-cli, jans-fido2, jans-notify, jans-orm, jans-scim, jans-eleven, jans-pycloudlib]
+ module: [jans-auth-server, agama, jans-config-api, jans-core, jans-linux-setup, jans-cli-tui, jans-fido2, jans-notify, jans-orm, jans-scim, jans-eleven, jans-pycloudlib]
env:
JVM_PROJECTS: |
jans-auth-server
@@ -70,7 +70,7 @@ jobs:
agama
NON_JVM_PROJECTS: |
jans-linux-setup
- jans-cli
+ jans-cli-tui
jans-pycloudlib
steps:
@@ -112,16 +112,16 @@ jobs:
echo GH sha: $GITHUB_SHA
- name: Set up JDK 11
- # JanssenProject/jans-cli is too similar to JanssenProject/jans-client-api as the contains function is returning it belonging to the JVM_PROJECT
- if: contains(env.CHANGED_DIR, matrix.module) && contains(env.JVM_PROJECTS, matrix.module) && matrix.module != 'jans-cli'
+ # JanssenProject/jans-cli-tui is too similar to JanssenProject/jans-client-api as the contains function is returning it belonging to the JVM_PROJECT
+ if: contains(env.CHANGED_DIR, matrix.module) && contains(env.JVM_PROJECTS, matrix.module) && matrix.module != 'jans-cli-tui'
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'adopt'
- name: Cache SonarCloud packages for JVM based project
- # JanssenProject/jans-cli is too similar to JanssenProject/jans-client-api as the contains function is returning it belonging to the JVM_PROJECT
- if: contains(env.CHANGED_DIR, matrix.module) && contains(env.JVM_PROJECTS, matrix.module) && matrix.module != 'jans-cli'
+ # JanssenProject/jans-cli-tui is too similar to JanssenProject/jans-client-api as the contains function is returning it belonging to the JVM_PROJECT
+ if: contains(env.CHANGED_DIR, matrix.module) && contains(env.JVM_PROJECTS, matrix.module) && matrix.module != 'jans-cli-tui'
uses: actions/cache@v3
with:
path: ~/.sonar/cache
@@ -129,8 +129,8 @@ jobs:
restore-keys: ${{ runner.os }}-sonar
- name: Build and analyze JVM based project
- # JanssenProject/jans-cli is too similar to JanssenProject/jans-client-api as the contains function is returning it belonging to the JVM_PROJECT
- if: contains(env.CHANGED_DIR, matrix.module) && contains(env.JVM_PROJECTS, matrix.module) && matrix.module != 'jans-cli'
+ # JanssenProject/jans-cli-tui is too similar to JanssenProject/jans-client-api as the contains function is returning it belonging to the JVM_PROJECT
+ if: contains(env.CHANGED_DIR, matrix.module) && contains(env.JVM_PROJECTS, matrix.module) && matrix.module != 'jans-cli-tui'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index ee49e5de573..3a365461246 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -143,7 +143,7 @@ jobs:
#max-parallel: 1
fail-fast: false
matrix:
- python-projects: ["jans-pycloudlib", "jans-cli", "jans-linux-setup"]
+ python-projects: ["jans-pycloudlib", "jans-cli-tui", "jans-linux-setup"]
steps:
- name: Checkout
uses: actions/checkout@v3
diff --git a/automation/github-labels/labels-schema.json b/automation/github-labels/labels-schema.json
index e23fb26a1b1..07b44b57a51 100644
--- a/automation/github-labels/labels-schema.json
+++ b/automation/github-labels/labels-schema.json
@@ -152,12 +152,12 @@
"title-prefixes": []
}
},
- "comp-jans-cli": {
+ "comp-jans-cli-tui": {
"color": "0052CC",
- "description": "Touching folder /jans-cli",
+ "description": "Touching folder /jans-cli-tui",
"auto-label": {
"branch": "",
- "paths": ["jans-cli"],
+ "paths": ["jans-cli-tui"],
"title-prefixes": []
}
},
diff --git a/docker-jans-persistence-loader/scripts/utils.py b/docker-jans-persistence-loader/scripts/utils.py
index ef54bd882dc..236dd0a58bb 100644
--- a/docker-jans-persistence-loader/scripts/utils.py
+++ b/docker-jans-persistence-loader/scripts/utils.py
@@ -204,7 +204,7 @@ def merge_jans_cli_ctx(manager, ctx):
# - move the configs and secrets creation to configurator
# - remove them on future release
- # jans-cli client
+ # jans-cli-tui client
ctx["role_based_client_id"] = manager.config.get("role_based_client_id")
if not ctx["role_based_client_id"]:
ctx["role_based_client_id"] = f"2000.{uuid4()}"
diff --git a/jans-cli/CHANGELOG.md b/jans-cli-tui/CHANGELOG.md
similarity index 100%
rename from jans-cli/CHANGELOG.md
rename to jans-cli-tui/CHANGELOG.md
diff --git a/jans-cli-tui/cli_tui/cli/config_cli.py b/jans-cli-tui/cli_tui/cli/config_cli.py
index af8f3fdfb57..94adc996b2f 100755
--- a/jans-cli-tui/cli_tui/cli/config_cli.py
+++ b/jans-cli-tui/cli_tui/cli/config_cli.py
@@ -1,7 +1,13 @@
#!/usr/bin/env python3
-import sys
import os
+import sys
+
+cur_dir = os.path.dirname(os.path.realpath(__file__))
+pylib_dir = os.path.join(cur_dir, 'pylib')
+if os.path.exists(pylib_dir):
+ sys.path.insert(0, pylib_dir)
+
import json
import re
import urllib3
@@ -37,11 +43,8 @@
config_dir = home_dir.joinpath('.config')
config_dir.mkdir(parents=True, exist_ok=True)
config_ini_fn = config_dir.joinpath('jans-cli.ini')
-cur_dir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(cur_dir)
-
-
my_op_mode = 'scim' if 'scim' in os.path.basename(sys.argv[0]) else 'jca'
plugins = []
@@ -204,13 +207,19 @@ def get_plugin_name_from_title(title):
if config['DEFAULT'].get(secret_key_str):
client_secret = config['DEFAULT'][secret_key_str]
elif config['DEFAULT'].get(secret_enc_key_str):
- client_secret_enc = config['DEFAULT'][secret_enc_key_str]
- client_secret = unobscure(client_secret_enc)
+ try:
+ client_secret_enc = config['DEFAULT'][secret_enc_key_str]
+ client_secret = unobscure(client_secret_enc)
+ except Exception:
+ pass
- if 'access_token' in config['DEFAULT']:
+ if 'access_token' in config['DEFAULT'] and config['DEFAULT']['access_token'].strip():
access_token = config['DEFAULT']['access_token']
- elif 'access_token_enc' in config['DEFAULT']:
- access_token = unobscure(config['DEFAULT']['access_token_enc'])
+ elif 'access_token_enc' in config['DEFAULT'] and config['DEFAULT']['access_token_enc'].strip():
+ try:
+ access_token = unobscure(config['DEFAULT']['access_token_enc'])
+ except Exception:
+ pass
debug = config['DEFAULT'].get('debug')
log_dir = config['DEFAULT'].get('log_dir', log_dir)
@@ -358,6 +367,10 @@ def get_request_header(self, headers={}, access_token=None):
if not access_token:
access_token = self.access_token
+ user = self.get_user_info()
+ if 'inum' in user:
+ headers['User-inum'] = user['inum']
+
ret_val = {'Authorization': 'Bearer {}'.format(access_token)}
ret_val.update(headers)
return ret_val
@@ -459,8 +472,9 @@ def revoke_session(self):
def check_access_token(self):
- if not self.access_token :
- print(self.colored_text("Access token was not found.", warning_color))
+ if not self.access_token:
+ if not self.wrapped:
+ print(self.colored_text("Access token was not found.", warning_color))
return
try:
@@ -472,9 +486,9 @@ def check_access_token(self):
}
)
except Exception as e:
- print(self.colored_text("Unable to validate access token: {}".format(e), error_color))
- self.access_token = None
-
+ if not self.wrapped:
+ print(self.colored_text("Unable to validate access token: {}".format(e), error_color))
+ self.access_token = None
def validate_date_time(self, date_str):
try:
@@ -672,7 +686,7 @@ def get_jwt_access_token(self, device_verified=None):
def get_access_token(self, scope):
if self.use_test_client:
self.get_scoped_access_token(scope)
- elif not self.access_token:
+ elif not self.access_token and not self.wrapped:
self.check_access_token()
self.get_jwt_access_token()
return True, ''
diff --git a/jans-cli-tui/cli_tui/jans_cli_tui.py b/jans-cli-tui/cli_tui/jans_cli_tui.py
index 92e2f613447..4bf7e372a68 100755
--- a/jans-cli-tui/cli_tui/jans_cli_tui.py
+++ b/jans-cli-tui/cli_tui/jans_cli_tui.py
@@ -17,6 +17,10 @@
cur_dir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(cur_dir)
+pylib_dir = os.path.join(cur_dir, 'cli', 'pylib')
+if os.path.exists(pylib_dir):
+ sys.path.insert(0, pylib_dir)
+
import prompt_toolkit
from prompt_toolkit.application import Application
from prompt_toolkit.application.current import get_app
@@ -47,8 +51,8 @@
CheckboxList,
Checkbox,
)
-
-from typing import Any, Optional, OrderedDict, Sequence, Union
+from collections import OrderedDict
+from typing import Any, Optional, Sequence, Union
from prompt_toolkit.key_binding.key_processor import KeyPressEvent
from prompt_toolkit.layout.containers import (
AnyContainer,
@@ -97,12 +101,13 @@ def __init__(self):
self.disabled_plugins = []
self.status_bar_text = ''
self.progress_char = ' '
- self.progress_active = False
self.progress_iterator = cycle(['⣾', '⣷', '⣯', '⣟', '⡿', '⢿', '⣻', '⣽'])
self.styles = dict(style.style_rules)
self._plugins = []
self._load_plugins()
self.cli_object_ok = False
+ self.pbar_text = ""
+ self.progressing_text = ""
self.not_implemented = Frame(
body=HSplit([Label(text=_("Not imlemented yet")), Button(text=_("MyButton"))], width=D()),
@@ -112,7 +117,8 @@ def __init__(self):
self.no_button = Button(text=_("No"), handler=accept_no)
self.pbar_window = Window(char=lambda: self.progress_char, style='class:progress', width=1)
self.status_bar = VSplit([
- Window(FormattedTextControl(self.update_status_bar), style='class:status', height=1),
+ Window(FormattedTextControl(lambda: self.pbar_text), style='class:status', height=1),
+ Window(FormattedTextControl(self.update_status_bar), style='class:status', width=1),
self.pbar_window,
], height=1
)
@@ -151,7 +157,6 @@ def __init__(self):
mouse_support=True, ## added
)
self.main_nav_selection_changed(self.nav_bar.navbar_entries[0][0])
- self.create_background_task(self.progress_coroutine())
self.plugins_initialised = False
self.create_background_task(self.check_jans_cli_ini())
@@ -160,12 +165,14 @@ def __init__(self):
async def progress_coroutine(self) -> None:
"""asyncio corotune for progress bar
"""
- while True:
- if self.progress_active:
- self.progress_char = next(self.progress_iterator)
- self.invalidate()
+ self.progress_active = True
+ while self.progress_active:
+ self.progress_char = next(self.progress_iterator)
+ self.pbar_text="Progressing"
+ self.invalidate()
await asyncio.sleep(0.15)
-
+ self.progress_char = ' '
+ self.invalidate()
def cli_requests(self, args: dict) -> Response:
response = self.cli_object.process_command_by_id(
@@ -177,17 +184,17 @@ def cli_requests(self, args: dict) -> Response:
)
return response
- def start_progressing(self):
- self.progress_active = True
+ def start_progressing(self, message: Optional[str]="Progressing") -> None:
+ self.progressing_text = message
+ self.create_background_task(self.progress_coroutine())
- def stop_progressing(self):
+ def stop_progressing(self, message: Optional[str]="") -> None:
+ self.progressing_text = message
self.progress_active = False
- self.progress_char = ' '
- self.invalidate()
def _load_plugins(self) -> None:
# check if admin-ui plugin is available:
-
+
plugin_dir = os.path.join(cur_dir, 'plugins')
for plugin_file in sorted(Path(plugin_dir).glob('*/main.py')):
if plugin_file.parent.joinpath('.enabled').exists():
@@ -215,7 +222,7 @@ def plugin_enabled(self, pid: str) -> bool:
return False
- def remove_plugin(self, pid: str) -> None:
+ def remove_plugin(self, pid: str) -> None:
"""Removes plugin object
Args:
pid (str): PID of plugin
@@ -299,10 +306,11 @@ async def coroutine():
self.stop_progressing()
-
self.cli_object_ok = True
if not self.plugins_initialised:
self.init_plugins()
+ self.runtime_plugins()
+
asyncio.ensure_future(coroutine())
else:
@@ -310,6 +318,12 @@ async def coroutine():
if not self.plugins_initialised:
self.init_plugins()
+ self.runtime_plugins()
+
+
+ def runtime_plugins(self) -> None:
+ """Disables plugins when cli object is ready"""
+
if self.cli_object_ok:
response = self.cli_requests({'operation_id': 'is-license-active'})
if response.status_code == 404:
@@ -568,15 +582,15 @@ def getButton(
return b
def update_status_bar(self) -> None:
- text = ''
- if self.status_bar_text:
- text = self.status_bar_text
- self.status_bar_text = ''
+ cur_text = self.pbar_text
+ if self.progressing_text:
+ self.pbar_text = self.progressing_text
+ elif hasattr(self.layout.current_window, 'jans_help') and self.layout.current_window.jans_help:
+ self.pbar_text = self.layout.current_window.jans_help
else:
- if hasattr(self.layout.current_window, 'jans_help') and self.layout.current_window.jans_help:
- text = self.layout.current_window.jans_help
-
- return text
+ self.pbar_text = ''
+ if cur_text != self.pbar_text:
+ self.invalidate()
def get_plugin_by_id(self, pid: str) -> None:
for plugin in self._plugins:
@@ -646,6 +660,8 @@ def save_creds(self, dialog:Dialog) -> None:
prop_val = child.children[1].content.buffer.text
if prop_name == 'jca_client_secret':
config_cli.config['DEFAULT']['jca_client_secret_enc'] = config_cli.obscure(prop_val)
+ if 'jca_client_secret' in config_cli.config['DEFAULT']:
+ del config_cli.config['DEFAULT']['jca_client_secret']
else:
config_cli.config['DEFAULT'][prop_name] = prop_val
config_cli.write_config()
diff --git a/jans-cli-tui/cli_tui/plugins/010_oxauth/edit_client_dialog.py b/jans-cli-tui/cli_tui/plugins/010_oxauth/edit_client_dialog.py
index df938590aa8..28db4271dda 100755
--- a/jans-cli-tui/cli_tui/plugins/010_oxauth/edit_client_dialog.py
+++ b/jans-cli-tui/cli_tui/plugins/010_oxauth/edit_client_dialog.py
@@ -1,35 +1,20 @@
-from typing import Any, OrderedDict
-from urllib import response
-
+from collections import OrderedDict
+from typing import Any
from prompt_toolkit.layout.dimension import D
from prompt_toolkit.layout.containers import (
HSplit,
VSplit,
DynamicContainer,
- Window
)
from prompt_toolkit.widgets import (
- Box,
Button,
Label,
-)
-from prompt_toolkit.widgets import (
- Button,
- Frame,
- Label,
- RadioList,
TextArea,
- CheckboxList,
- Checkbox,
+ Dialog
)
from prompt_toolkit.lexers import PygmentsLexer, DynamicLexer
-
from prompt_toolkit.application.current import get_app
from asyncio import Future, ensure_future
-
-
-import cli_style
-from cli import config_cli
from utils.static import DialogResult
from utils.multi_lang import _
from wui_components.jans_dialog_with_nav import JansDialogWithNav
@@ -41,18 +26,10 @@
from wui_components.jans_vetrical_nav import JansVerticalNav
from view_uma_dialog import ViewUMADialog
import threading
-from prompt_toolkit.widgets import (
- Button,
- Dialog,
- VerticalLine,
-)
-from prompt_toolkit.layout.containers import AnyContainer
from prompt_toolkit.buffer import Buffer
-
from prompt_toolkit.formatted_text import AnyFormattedText
-from prompt_toolkit.layout.dimension import AnyDimension
-from typing import Optional, Sequence, Union
-from typing import TypeVar, Callable
+from typing import Optional, Sequence
+from typing import Callable
import json
@@ -80,6 +57,7 @@ def __init__(
data (list): selected line data
button_functions (list, optional): Dialog main buttons with their handlers. Defaults to [].
save_handler (method, optional): handler invoked when closing the dialog. Defaults to None.
+ delete_UMAresource (method, optional): handler invoked when deleting UMA-resources
"""
super().__init__(parent, title, buttons)
self.save_handler = save_handler
@@ -91,6 +69,8 @@ def __init__(
self.create_window()
def save(self) -> None:
+ """method to invoked when saving the dialog (Save button is pressed)
+ """
self.data = self.make_data_from_dialog()
self.data['disabled'] = not self.data['disabled']
@@ -164,6 +144,9 @@ def save(self) -> None:
self.future.set_result(DialogResult.ACCEPT)
def cancel(self) -> None:
+ """method to invoked when canceling changes in the dialog (Cancel button is pressed)
+ """
+
self.future.set_result(DialogResult.CANCEL)
def create_window(self) -> None:
@@ -851,6 +834,12 @@ def search_uma_resources(
self,
tbuffer: Buffer,
) -> None:
+ """This method handel the search for UMA resources
+
+ Args:
+ tbuffer (Buffer): Buffer returned from the TextArea widget > GetTitleText
+ """
+
if not len(tbuffer.text) > 2:
self.myparent.show_message(_("Error!"), _("Search string should be at least three characters"))
return
@@ -960,10 +949,18 @@ def client_dialog_nav_selection_changed(
self,
selection: str
) -> None:
+ """This method for client navigation bar when value is changed
+
+ Args:
+ selection (str): the New Value from the nav-bar
+ """
+
self.left_nav = selection
def view_uma_resources(self, **params: Any) -> None:
-
+ """This method view the UMA resources in a dialog
+ """
+
selected_line_data = params['data'] ##self.uma_result
title = _("Edit user Data (Clients)")
@@ -972,5 +969,11 @@ def view_uma_resources(self, **params: Any) -> None:
self.myparent.show_jans_dialog(dialog)
def __pt_container__(self)-> Dialog:
+ """The container for the dialog itself
+
+ Returns:
+ Dialog: The Edit Client Dialog
+ """
+
return self.dialog
diff --git a/jans-cli-tui/cli_tui/plugins/010_oxauth/edit_scope_dialog.py b/jans-cli-tui/cli_tui/plugins/010_oxauth/edit_scope_dialog.py
index 5d101f9aa6b..4abdab70f54 100755
--- a/jans-cli-tui/cli_tui/plugins/010_oxauth/edit_scope_dialog.py
+++ b/jans-cli-tui/cli_tui/plugins/010_oxauth/edit_scope_dialog.py
@@ -1,4 +1,4 @@
-from typing import Any, OrderedDict, Optional, Sequence, Union, TypeVar, Callable
+from typing import Any, Optional, Sequence, Union, TypeVar, Callable
from asyncio import ensure_future
from prompt_toolkit.layout.dimension import D
@@ -58,8 +58,9 @@ def __init__(
"""init for `EditScopeDialog`, inherits from two diffrent classes `JansGDialog` and `DialogUtils`
DialogUtils (methods): Responsable for all `make data from dialog` and `check required fields` in the form for any Edit or Add New
+ JansGDialog (dialog): This is the main dialog Class Widget for all Jans-cli-tui dialogs except custom dialogs like dialogs with navbar
- Args:
+ Args:
parent (widget): This is the parent widget for the dialog, to access `Pageup` and `Pagedown`
title (str): The Main dialog title
data (list): selected line data
@@ -80,6 +81,9 @@ def __init__(
self.sope_type = self.data.get('scopeType') or 'oauth'
def save(self) -> None:
+ """method to invoked when saving the dialog (Save button is pressed)
+ """
+
self.myparent.logger.debug('SAVE SCOPE')
data = {}
@@ -105,6 +109,9 @@ def save(self) -> None:
self.future.set_result(DialogResult.ACCEPT)
def cancel(self) -> None:
+ """method to invoked when canceling changes in the dialog (Cancel button is pressed)
+ """
+
self.future.set_result(DialogResult.CANCEL)
def create_window(self) -> None:
@@ -175,12 +182,27 @@ def scope_selection_changed(
self,
cb: RadioList,
) -> None:
+ """This method for scope type selection set
+
+ Args:
+ cb (RadioList): the New Value from the nav-bar
+ """
+
self.sope_type = cb.current_value
def get_named_claims(
self,
claims_list:list
) -> list:
+ """This method for getting claim name
+
+ Args:
+ claims_list (list): List for Claims
+
+ Returns:
+ list: List with Names retlated to that claims
+ """
+
try :
responce = self.myparent.cli_object.process_command_by_id(
operation_id='get-attributes',
@@ -208,7 +230,6 @@ def get_named_claims(
return calims_names
-
def delete_claim(self, **kwargs: Any) -> None:
"""This method for the deletion of claim
@@ -218,7 +239,7 @@ def delete_claim(self, **kwargs: Any) -> None:
"""
- dialog = self.myparent.get_confirm_dialog(_("Are you sure want to delete claim dn:")+"\n {} ?".format(selected[0]))
+ dialog = self.myparent.get_confirm_dialog(_("Are you sure want to delete claim dn:")+"\n {} ?".format(kwargs['selected'][0]))
async def coroutine():
focused_before = self.myparent.layout.current_window
result = await self.myparent.show_dialog_as_float(dialog)
@@ -411,6 +432,11 @@ def search_claims(
self,
textbuffer: Buffer,
) -> None:
+ """This method handel the search for claims and adding new claims
+
+ Args:
+ tbuffer (Buffer): Buffer returned from the TextArea widget > GetTitleText
+ """
try :
responce = self.myparent.cli_object.process_command_by_id(
@@ -465,5 +491,11 @@ def add_selected_claims(dialog):
self.myparent.show_jans_dialog(dialog)
def __pt_container__(self) -> Dialog:
+ """The container for the dialog itself
+
+ Returns:
+ Dialog: The Edit Scope Dialog
+ """
+
return self.dialog
diff --git a/jans-cli-tui/cli_tui/plugins/010_oxauth/main.py b/jans-cli-tui/cli_tui/plugins/010_oxauth/main.py
index 7c460759bae..59f89370afb 100755
--- a/jans-cli-tui/cli_tui/plugins/010_oxauth/main.py
+++ b/jans-cli-tui/cli_tui/plugins/010_oxauth/main.py
@@ -1,19 +1,10 @@
-import os
-import sys
import time
import json
-
-import threading
import asyncio
from functools import partial
from typing import Any, Optional
-
-
-import prompt_toolkit
from prompt_toolkit.application.current import get_app
from prompt_toolkit.eventloop import get_event_loop
-from prompt_toolkit.key_binding import KeyBindings
-from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
from prompt_toolkit.layout.containers import (
HSplit,
VSplit,
@@ -26,37 +17,22 @@
Box,
Button,
Label,
- Frame,
Dialog,
- CheckboxList,
TextArea
)
from prompt_toolkit.lexers import PygmentsLexer, DynamicLexer
-
-
from utils.static import DialogResult
-from prompt_toolkit.layout import ScrollablePane
-from asyncio import Future
-
-from cli import config_cli
from utils.utils import DialogUtils
from wui_components.jans_nav_bar import JansNavBar
-from wui_components.jans_side_nav_bar import JansSideNavBar
from wui_components.jans_vetrical_nav import JansVerticalNav
-from wui_components.jans_dialog import JansDialog
-from wui_components.jans_dialog_with_nav import JansDialogWithNav
from wui_components.jans_drop_down import DropDownWidget
-from wui_components.jans_data_picker import DateSelectWidget
from wui_components.jans_cli_dialog import JansGDialog
-
from view_property import ViewProperty
from edit_client_dialog import EditClientDialog
from edit_scope_dialog import EditScopeDialog
from prompt_toolkit.buffer import Buffer
from prompt_toolkit.application import Application
-
from utils.multi_lang import _
-import cli_style
class Plugin(DialogUtils):
"""This is a general class for plugins
@@ -68,39 +44,31 @@ def __init__(
"""init for Plugin class "oxauth"
Args:
- app (_type_): _description_
+ app (Generic): The main Application class
"""
self.app = app
self.pid = 'oxauth'
self.name = '[A]uth Server'
self.search_text= None
-
+ self.oauth_update_properties_start_index = 0
+ self.app_configuration = {}
self.oauth_containers = {}
self.oauth_prepare_navbar()
self.oauth_prepare_containers()
self.oauth_nav_selection_changed(self.nav_bar.navbar_entries[0][0])
def init_plugin(self) -> None:
+ """The initialization for this plugin
+ """
self.app.create_background_task(self.get_appconfiguration())
self.schema = self.app.cli_object.get_schema_from_reference('', '#/components/schemas/AppConfiguration')
-
async def get_appconfiguration(self) -> None:
- """Coroutine for getting application configuration.
- """
- try:
- response = self.app.cli_object.process_command_by_id(
- operation_id='get-properties',
- url_suffix='',
- endpoint_args='',
- data_fn=None,
- data={}
- )
-
- except Exception as e:
- self.app.show_message(_("Error getting Jans configuration"), str(e))
- return
+ 'Coroutine for getting application configuration.'
+
+ cli_args = {'operation_id': 'get-properties'}
+ response = await self.app.loop.run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
if response.status_code not in (200, 201):
self.app.show_message(_("Error getting Jans configuration"), str(response.text))
@@ -169,20 +137,19 @@ def oauth_prepare_containers(self) -> None:
self.oauth_containers['properties'] = HSplit([
VSplit([
- self.app.getButton(text=_("Get properties"), name='oauth:scopes:get', jans_help=_("Retreive first {} Scopes").format(self.app.entries_per_page), handler=self.oauth_get_properties),
self.app.getTitledText(
_("Search: "),
name='oauth:properties:search',
jans_help=_("Press enter to perform search"),
accept_handler=self.search_properties,
- style='class:outh_containers_scopes.text') ],
+ style='class:outh_containers_scopes.text')
+ ],
padding=3,
width=D(),
),
DynamicContainer(lambda: self.oauth_data_container['properties'])
],style='class:outh_containers_scopes')
-
self.oauth_containers['logging'] = DynamicContainer(lambda: self.oauth_data_container['logging'])
self.oauth_main_container = HSplit([
@@ -214,6 +181,8 @@ def oauth_nav_selection_changed(
selection (str): the current selected tab
"""
if selection in self.oauth_containers:
+ if selection == 'properties':
+ self.oauth_update_properties(tofocus=False)
self.oauth_main_area = self.oauth_containers[selection]
else:
self.oauth_main_area = self.app.not_implemented
@@ -226,21 +195,16 @@ def oauth_update_clients(
"""update the current clients data to server
Args:
- pattern (str, optional): endpoint arguments for the client data. Defaults to ''.
+ start_index (Optional[int], optional): This is flag for the clients page. Defaults to 0.
+ pattern (Optional[str], optional):endpoint arguments for the client data. Defaults to ''.
"""
- def get_next(
- start_index: int,
- pattern: Optional[str]= '',
- ) -> None:
- self.oauth_update_clients(start_index, pattern='')
-
async def coroutine():
endpoint_args ='limit:{},startIndex:{}'.format(self.app.entries_per_page, start_index)
if pattern:
endpoint_args +=',pattern:'+pattern
cli_args = {'operation_id': 'get-oauth-openid-clients', 'endpoint_args': endpoint_args}
- self.app.start_progressing()
+ self.app.start_progressing(_("Retreiving clients from server..."))
response = await get_event_loop().run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
self.app.stop_progressing()
@@ -256,11 +220,6 @@ async def coroutine():
data =[]
- file1 = open("hopa.log", "w")
- file1.write(str(response.status_code)+'\n')
- file1.write(str(response.json()))
- file1.close()
-
for d in result.get('entries', []):
data.append(
[
@@ -288,17 +247,17 @@ async def coroutine():
)
buttons = []
if start_index > 0:
- handler_partial = partial(get_next, start_index-self.app.entries_per_page, pattern)
+ handler_partial = partial(self.oauth_update_clients, start_index-self.app.entries_per_page, pattern)
prev_button = Button(_("Prev"), handler=handler_partial)
prev_button.window.jans_help = _("Retreives previous %d entries") % self.app.entries_per_page
buttons.append(prev_button)
if result['start'] + self.app.entries_per_page < result['totalEntriesCount']:
- handler_partial = partial(get_next, start_index+self.app.entries_per_page, pattern)
+ handler_partial = partial(self.oauth_update_clients, start_index+self.app.entries_per_page, pattern)
next_button = Button(_("Next"), handler=handler_partial)
next_button.window.jans_help = _("Retreives next %d entries") % self.app.entries_per_page
buttons.append(next_button)
- self.app.layout.focus(clients) # clients.focuse..!? TODO >> DONE
+ self.app.layout.focus(clients)
self.oauth_data_container['clients'] = HSplit([
clients,
VSplit(buttons, padding=5, align=HorizontalAlign.CENTER)
@@ -310,10 +269,8 @@ async def coroutine():
else:
self.app.show_message(_("Oops"), _("No matching result"),tobefocused = self.oauth_containers['clients'])
- self.app.start_progressing()
asyncio.ensure_future(coroutine())
-
def delete_client(self, **kwargs: Any) -> None:
"""This method for the deletion of the clients data
@@ -352,7 +309,6 @@ async def coroutine():
asyncio.ensure_future(coroutine())
-
def oauth_get_scopes(
self,
start_index: Optional[int]= 0,
@@ -361,7 +317,8 @@ def oauth_get_scopes(
"""update the current Scopes data to server
Args:
- start_index (int, optional): add Button("Prev") to the layout. Defaults to 0.
+ start_index (int, optional): add Button("Prev") and Button("Next")to the layout (which page am I in). Defaults to 0.
+ pattern (Optional[str], optional):endpoint arguments for the Scopes data. Defaults to ''.
"""
async def coroutine():
@@ -371,16 +328,16 @@ async def coroutine():
endpoint_args +=',pattern:'+pattern
cli_args = {'operation_id': 'get-oauth-scopes', 'endpoint_args':endpoint_args}
- self.app.start_progressing()
+ self.app.start_progressing(_("Retreiving scopes from server..."))
response = await get_event_loop().run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
- self.app.stop_progressing()
+ self.app.stop_progressing(_("Retreived"))
+
try:
result = response.json()
except Exception as e:
self.app.show_message(_("Error getting response"), str(response))
return
-
data =[]
for d in result.get('entries', []):
@@ -392,7 +349,7 @@ async def coroutine():
d['inum']
]
)
-
+
if data:
scopes = JansVerticalNav(
@@ -436,103 +393,74 @@ async def coroutine():
asyncio.ensure_future(coroutine())
-
def oauth_update_properties(
self,
- start_index: Optional[int]= 0,
+ start_index: Optional[int]= 0,
pattern: Optional[str]= '',
+ tofocus:Optional[bool]=True,
) -> None:
+
"""update the current clients data to server
Args:
+ start_index (int, optional): add Button("Prev") and Button("Next")to the layout (which page am I in). Defaults to 0.
pattern (str, optional): endpoint arguments for the client data. Defaults to ''.
+ tofocus (Optional[bool], optional): To focus the properties or not (used to not focus on navigation). Defaults to True.
"""
- def get_next(
- start_index: int,
- pattern: Optional[str]= '',
- ) -> None:
- self.app.logger.debug("start_index="+str(start_index))
- self.oauth_update_properties(start_index, pattern=pattern)
-
- # ------------------------------------------------------------------------------- #
- # ------------------------------------------------------------------------------- #
- # ------------------------------------------------------------------------------- #
-
- try :
- rsponse = self.app.cli_object.process_command_by_id(
- operation_id='get-properties',
- url_suffix='',
- endpoint_args='',
- data_fn=None,
- data={}
- )
- except Exception as e:
- self.app.stop_progressing()
- self.app.show_message(_("Error getting properties"), str(e))
- return
-
- self.app.stop_progressing()
- if rsponse.status_code not in (200, 201):
- self.app.show_message(_("Error getting properties"), str(rsponse.text))
- return
-
- try:
- result = rsponse.json()
- except Exception:
- self.app.show_message(_("Error getting properties"), str(rsponse.text))
- return
+ self.oauth_update_properties_start_index = start_index
# ------------------------------------------------------------------------------- #
# ----------------------------------- Search ------------------------------------ #
# ------------------------------------------------------------------------------- #
porp_schema = self.app.cli_object.get_schema_from_reference('', '#/components/schemas/AppConfiguration')
data =[]
+
if pattern:
- for k in result:
+ for k in self.app_configuration:
if pattern.lower() in k.lower():
if k in porp_schema.get('properties', {}):
data.append(
[
k,
- result[k],
+ self.app_configuration[k],
]
)
else:
- for d in result:
+ for d in self.app_configuration:
if d in porp_schema.get('properties', {}):
data.append(
[
d,
- result[d],
+ self.app_configuration[d],
]
)
-
# ------------------------------------------------------------------------------- #
# --------------------------------- View Data ----------------------------------- #
- # ------------------------------------------------------------------------------- #
+ # ------------------------------------------------------------------------------- #
+
if data:
buttons = []
- if int(len(data)/ 20) >=1 :
- if start_index< int(len(data)/ 20) :
- handler_partial = partial(get_next, start_index+1, pattern)
- next_button = Button(_("Next"), handler=handler_partial)
- next_button.window.jans_help = _("Retreives next %d entries") % self.app.entries_per_page
- buttons.append(next_button)
+ if len(data)/20 >=1:
if start_index!=0:
- handler_partial = partial(get_next, start_index-1, pattern)
+ handler_partial = partial(self.oauth_update_properties, start_index-1, pattern)
prev_button = Button(_("Prev"), handler=handler_partial)
- prev_button.window.jans_help = _("Retreives previous %d entries") % self.app.entries_per_page
+ prev_button.window.jans_help = _("Displays previous %d entries") % self.app.entries_per_page
buttons.append(prev_button)
+ if start_index< int(len(data)/ 20) :
+ handler_partial = partial(self.oauth_update_properties, start_index+1, pattern)
+ next_button = Button(_("Next"), handler=handler_partial)
+ next_button.window.jans_help = _("Displays next %d entries") % self.app.entries_per_page
+ buttons.append(next_button)
data_now = data[start_index*20:start_index*20+20]
- clients = JansVerticalNav(
+ properties = JansVerticalNav(
myparent=self.app,
headers=['Property Name', 'Property Value'],
preferred_size= [0,0],
@@ -541,22 +469,25 @@ def get_next(
on_display=self.properties_display_dialog,
get_help=(self.get_help,'AppConfiguration'),
# selection_changed=self.data_selection_changed,
- selectes=0,
+ selectes=0,
headerColor='class:outh-verticalnav-headcolor',
entriesColor='class:outh-verticalnav-entriescolor',
- all_data=list(result.values())
+ all_data=list(self.app_configuration.values())
)
- self.app.layout.focus(clients) # clients.focuse..!? TODO >> DONE
+
self.oauth_data_container['properties'] = HSplit([
- clients,
+ properties,
VSplit(buttons, padding=5, align=HorizontalAlign.CENTER)
])
- get_app().invalidate()
- self.app.layout.focus(clients) ### it fix focuse on the last item deletion >> try on UMA-res >> edit_client_dialog >> oauth_update_uma_resources
+ self.app.invalidate()
+ if tofocus:
+ self.app.layout.focus(properties)
else:
self.app.show_message(_("Oops"), _("No matching result"),tobefocused = self.oauth_containers['properties'])
def properties_display_dialog(self, **params: Any) -> None:
+ """Display the properties as Text
+ """
data_property, data_value = params['selected'][0], params['selected'][1]
body = HSplit([
TextArea(
@@ -574,27 +505,24 @@ def properties_display_dialog(self, **params: Any) -> None:
self.app.show_jans_dialog(dialog)
- def oauth_get_properties(self) -> None:
- """Method to get the clients data from server
- """
- self.oauth_data_container['properties'] = HSplit([Label(_("Please wait while getting properties"),style='class:outh-waitclientdata.label')], width=D(),style='class:outh-waitclientdata')
- t = threading.Thread(target=self.oauth_update_properties, daemon=True)
- self.app.start_progressing()
- t.start()
-
def view_property(self, **params: Any) -> None:
- #property, value =params['passed']
-
+ """This method view the properties in Dialog to edit
+ """
selected_line_data = params['passed'] ##self.uma_result
-
+
title = _("Edit property")
- dialog = ViewProperty(self.app, title=title, data=selected_line_data, get_properties= self.oauth_get_properties, search_properties=self.search_properties, search_text=self.search_text)
-
+ dialog = ViewProperty(app=self.app, parent=self, title=title, data=selected_line_data)
+
self.app.show_jans_dialog(dialog)
- def search_properties(self, tbuffer:Buffer,) -> None:
+ def search_properties(self, tbuffer:Buffer) -> None:
+ """This method handel the search for Properties
+
+ Args:
+ tbuffer (Buffer): Buffer returned from the TextArea widget > GetTitleText
+ """
self.app.logger.debug("tbuffer="+str(tbuffer))
self.app.logger.debug("type tbuffer="+str(type(tbuffer)))
self.search_text=tbuffer.text
@@ -602,42 +530,16 @@ def search_properties(self, tbuffer:Buffer,) -> None:
if not len(tbuffer.text) > 2:
self.app.show_message(_("Error!"), _("Search string should be at least three characters"),tobefocused=self.oauth_containers['properties'])
return
- t = threading.Thread(target=self.oauth_update_properties, args=(0,tbuffer.text), daemon=True)
- self.app.start_progressing()
- t.start()
- def oauth_update_keys(self) -> None:
+ self.oauth_update_properties(0, tbuffer.text)
+ def oauth_update_keys(self) -> None:
"""update the current Keys fromserver
"""
- try :
- rsponse = self.app.cli_object.process_command_by_id(
- operation_id='get-config-jwks',
- url_suffix='',
- endpoint_args='',
- data_fn=None,
- data={}
- )
- except Exception as e:
- self.app.stop_progressing()
- self.app.show_message(_("Error getting keys"), str(e))
- return
-
- self.app.stop_progressing()
- if rsponse.status_code not in (200, 201):
- self.app.show_message(_("Error getting keys"), str(rsponse.text))
- return
-
- try:
- result = rsponse.json()
- except Exception:
- self.app.show_message(_("Error getting keys"), str(rsponse.text))
- return
-
data =[]
- for d in result.get('keys', []):
+ for d in self.jwks_keys.get('keys', []):
try:
gmt = time.gmtime(int(d['exp'])/1000)
exps = time.strftime("%d %b %Y %H:%M:%S", gmt)
@@ -661,7 +563,7 @@ def oauth_update_keys(self) -> None:
selectes=0,
headerColor='class:outh-verticalnav-headcolor',
entriesColor='class:outh-verticalnav-entriescolor',
- all_data=result['keys']
+ all_data=self.jwks_keys['keys']
)
self.oauth_data_container['keys'] = HSplit([keys])
@@ -669,30 +571,36 @@ def oauth_update_keys(self) -> None:
self.app.layout.focus(keys)
else:
- self.app.show_message(_("Oops"), _("Nothing to display"), tobefocused=self.oauth_containers['keys'])
+ self.app.show_message(_("Oops"), _("No JWKS keys were found"), tobefocused=self.app.center_frame)
def oauth_get_keys(self) -> None:
"""Method to get the Keys from server
"""
- self.oauth_data_container['keys'] = HSplit([Label(_("Please wait while getting Keys"), style='class:outh-waitscopedata.label')], width=D(), style='class:outh-waitclientdata')
- t = threading.Thread(target=self.oauth_update_keys, daemon=True)
- self.app.start_progressing()
- t.start()
-
+
+ async def coroutine():
+ cli_args = {'operation_id': 'get-config-jwks'}
+ self.app.start_progressing("Retreiving JWKS keys...")
+ response = await get_event_loop().run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
+ self.app.stop_progressing()
+ self.jwks_keys = response.json()
+ self.oauth_update_keys()
+
+ asyncio.ensure_future(coroutine())
+
def edit_scope_dialog(self, **params: Any) -> None:
+ """This Method show the scopes dialog for edit
+ """
selected_line_data = params['data']
dialog = EditScopeDialog(self.app, title=_("Edit Scopes"), data=selected_line_data, save_handler=self.save_scope)
self.app.show_jans_dialog(dialog)
def edit_client_dialog(self, **params: Any) -> None:
+ """This Method show the scopes dialog for edit
+ """
selected_line_data = params['data']
title = _("Edit user Data (Clients)")
- file1= open("hopa.log",'a')
- file1.write("selected_line_data : "+str(selected_line_data)+'\n \n')
- file1.close()
-
self.EditClientDialog = EditClientDialog(self.app, title=title, data=selected_line_data,save_handler=self.save_client,delete_UMAresource=self.delete_UMAresource)
self.app.show_jans_dialog(self.EditClientDialog)
@@ -722,7 +630,6 @@ def save_client(self, dialog: Dialog) -> None:
self.app.show_message(_("Error!"), _("An error ocurred while saving client:\n") + str(response.text))
-
def save_scope(self, dialog: Dialog) -> None:
"""This method to save the client data to server
@@ -748,6 +655,11 @@ async def coroutine():
asyncio.ensure_future(coroutine())
def search_scope(self, tbuffer:Buffer,) -> None:
+ """This method handel the search for Scopes
+
+ Args:
+ tbuffer (Buffer): Buffer returned from the TextArea widget > GetTitleText
+ """
if not len(tbuffer.text) > 2:
self.app.show_message(_("Error!"), _("Search string should be at least three characters"),tobefocused=self.oauth_containers['scopes'])
return
@@ -755,6 +667,11 @@ def search_scope(self, tbuffer:Buffer,) -> None:
self.oauth_get_scopes(pattern=tbuffer.text)
def search_clients(self, tbuffer:Buffer,) -> None:
+ """This method handel the search for Clients
+
+ Args:
+ tbuffer (Buffer): Buffer returned from the TextArea widget > GetTitleText
+ """
if not len(tbuffer.text) > 2:
self.app.show_message(_("Error!"), _("Search string should be at least three characters"),tobefocused=self.oauth_containers['clients'])
return
@@ -762,18 +679,20 @@ def search_clients(self, tbuffer:Buffer,) -> None:
self.oauth_update_clients(pattern=tbuffer.text)
def add_scope(self) -> None:
- """Method to display the dialog of clients
+ """Method to display the dialog of Scopes (Add New)
"""
dialog = EditScopeDialog(self.app, title=_("Add New Scope"), data={}, save_handler=self.save_scope)
result = self.app.show_jans_dialog(dialog)
def add_client(self) -> None:
- """Method to display the dialog of clients
+ """Method to display the dialog of clients (Add New)
"""
dialog = EditClientDialog(self.app, title=_("Add Client"), data={}, save_handler=self.save_client)
result = self.app.show_jans_dialog(dialog)
def get_help(self, **kwargs: Any):
+ """This method get focused field Description to display on statusbar
+ """
self.app.logger.debug("get_help: "+str(kwargs['data']))
self.app.logger.debug("get_help: "+str(kwargs['scheme']))
@@ -790,17 +709,9 @@ def get_help(self, **kwargs: Any):
self.app.status_bar_text= kwargs['data'][1]
self.app.logger.debug("kwargs['data']: "+str(kwargs['data']))
-
- # self.app.status_bar_text= kwargs['data'][1]
-
-
def delete_scope(self, **kwargs: Any):
"""This method for the deletion of the clients data
- Args:
- selected (_type_): The selected Client
- event (_type_): _description_
-
Returns:
str: The server response
"""
@@ -828,6 +739,11 @@ async def coroutine():
asyncio.ensure_future(coroutine())
def delete_UMAresource(self, **kwargs: Any):
+ """This method for the deletion of the UMAresource
+
+ Returns:
+ str: The server response
+ """
dialog = self.app.get_confirm_dialog(_("Are you sure want to delete UMA resoucres with id:")+"\n {} ?".format(kwargs ['selected'][0]))
async def coroutine():
focused_before = self.app.layout.current_window
@@ -852,6 +768,8 @@ async def coroutine():
asyncio.ensure_future(coroutine())
def oauth_logging(self) -> None:
+ """This method for the Auth Login
+ """
self.oauth_data_container['logging'] = HSplit([
self.app.getTitledWidget(
_('Log Level'),
@@ -900,6 +818,8 @@ def oauth_logging(self) -> None:
], style='class:outh_containers_clients', width=D())
def save_logging(self) -> None:
+ """This method to Save the Auth Login to server
+ """
mod_data = self.make_data_from_dialog({'logging':self.oauth_data_container['logging']})
pathches = []
for key_ in mod_data:
diff --git a/jans-cli-tui/cli_tui/plugins/010_oxauth/view_property.py b/jans-cli-tui/cli_tui/plugins/010_oxauth/view_property.py
index 3f786edac9c..a9f35d83d0f 100644
--- a/jans-cli-tui/cli_tui/plugins/010_oxauth/view_property.py
+++ b/jans-cli-tui/cli_tui/plugins/010_oxauth/view_property.py
@@ -1,62 +1,20 @@
-import json
-from asyncio import Future
-from typing import OrderedDict
-
-from prompt_toolkit.widgets import Button, TextArea
-from prompt_toolkit.application.current import get_app
+import asyncio
from prompt_toolkit.layout.dimension import D
-from prompt_toolkit.key_binding import KeyBindings
-from prompt_toolkit.buffer import Buffer
from prompt_toolkit.formatted_text import AnyFormattedText
-from prompt_toolkit.layout.dimension import AnyDimension
-
-from prompt_toolkit.widgets import (
- Button,
- Label,
- TextArea,
-
-)
-from asyncio import ensure_future
-
-from prompt_toolkit.widgets import (
- Button,
- Dialog,
- VerticalLine,
-)
from cli import config_cli
from prompt_toolkit.layout.containers import (
- ConditionalContainer,
- Float,
HSplit,
- VSplit,
- VerticalAlign,
DynamicContainer,
- FloatContainer,
- Window,
- AnyContainer
)
from prompt_toolkit.widgets import (
- Box,
Button,
- Frame,
- Label,
RadioList,
- TextArea,
+ Dialog,
)
-
from utils.static import DialogResult
-from wui_components.jans_dialog import JansDialog
-from wui_components.jans_dialog_with_nav import JansDialogWithNav
-from wui_components.jans_nav_bar import JansNavBar
-from wui_components.jans_side_nav_bar import JansSideNavBar
from utils.utils import DialogUtils
-
from wui_components.jans_cli_dialog import JansGDialog
-
-from wui_components.jans_drop_down import DropDownWidget
-
-from typing import Optional, Sequence, Union
-from typing import TypeVar, Callable
+from typing import Optional, Sequence
from utils.multi_lang import _
import cli_style
@@ -64,34 +22,47 @@ class ViewProperty(JansGDialog, DialogUtils):
"""The Main UMA-resources Dialog to view UMA Resource Details
"""
def __init__(
- self,
- parent,
- data:tuple,
- title: AnyFormattedText= "",
- search_text: AnyFormattedText= "",
- buttons: Optional[Sequence[Button]]= [],
- get_properties: Callable= None,
- search_properties: Callable= None,
- )-> Dialog:
-
- super().__init__(parent, title, buttons)
+ self,
+ app,
+ parent,
+ data:tuple,
+ title: AnyFormattedText= "",
+ buttons: Optional[Sequence[Button]]= []
+ )-> None:
+ """init for `ViewProperty`, inherits from two diffrent classes `JansGDialog` and `DialogUtils`
+
+ JansGDialog (dialog): This is the main dialog Class Widget for all Jans-cli-tui dialogs except custom dialogs like dialogs with navbar
+ DialogUtils (methods): Responsable for all `make data from dialog` and `check required fields` in the form for any Edit or Add New
+
+ Args:
+ app (Generic): The main Application class
+ parent (widget): This is the parent widget for the dialog
+ data (tuple): selected line data
+ title (AnyFormattedText, optional): The Main dialog title. Defaults to "".
+ button_functions (list, optional): Dialog main buttons with their handlers. Defaults to [].
+ """
+ super().__init__(app, title, buttons)
self.property, self.value = data[0],data[1]
- self.myparent= parent
- self.get_properties = get_properties
- self.search_properties= search_properties
- self.search_text=search_text
+ self.app = app
+ self.myparent = parent
self.value_content = HSplit([],width=D())
self.tabs = {}
self.selected_tab = 'tab0'
- self.schema = self.myparent.cli_object.get_schema_from_reference('', '#/components/schemas/AppConfiguration')
+ self.schema = self.app.cli_object.get_schema_from_reference('', '#/components/schemas/AppConfiguration')
self.prepare_properties()
self.create_window()
def cancel(self) -> None:
+ """method to invoked when canceling changes in the dialog (Cancel button is pressed)
+ """
+
self.future.set_result(DialogResult.CANCEL)
def save(self) -> None:
+ """method to invoked when saving the dialog (Save button is pressed)
+ """
+
data_dict = {}
list_data =[]
@@ -100,10 +71,15 @@ def save(self) -> None:
prop_type = self.get_item_data(wid)
data = prop_type['value']
- elif (type(self.value)==list and type(self.value[0]) not in [dict,list]):
+ elif (type(self.value)==list and (type(self.value[0]) not in [dict,list])):
+
for wid in self.value_content.children:
prop_type = self.get_item_data(wid)
- data = prop_type['value'].split('\n')
+
+ if self.get_type(prop_type['key']) != 'checkboxlist':
+ data = prop_type['value'].split('\n')
+ else:
+ data = prop_type['value']
elif type(self.value) == dict :
for wid in self.value_content.children:
@@ -121,58 +97,58 @@ def save(self) -> None:
list_data.append(data_dict)
data = list_data
else :
- self.myparent.logger.debug("self.value: "+str(self.value))
- self.myparent.logger.debug("type self.value: "+str(type(self.value)))
+ self.app.logger.debug("self.value: "+str(self.value))
+ self.app.logger.debug("type self.value: "+str(type(self.value)))
data = []
# ------------------------------------------------------------#
# --------------------- Patch to server ----------------------#
# ------------------------------------------------------------#
if data :
- response = self.myparent.cli_object.process_command_by_id(
- operation_id='patch-properties' ,
- url_suffix='',
- endpoint_args='',
- data_fn='',
- data=[ {'op':'replace', 'path': self.property, 'value': data } ]
- )
- else:
- return
- # ------------------------------------------------------------#
- # -- get_properties or serach again to see Momentary change --#
- # ------------------------------------------------------------#
- if response:
- if self.search_text:
- tbuff = Buffer(name='', )
- tbuff.text=self.search_text
- self.search_properties(tbuff)
- else:
- self.get_properties()
- self.future.set_result(DialogResult.ACCEPT)
- return True
- self.myparent.show_message(_("Error!"), _("An error ocurred while saving property:\n") + str(response.text))
+ cli_args = {'operation_id': 'patch-properties', 'data': [ {'op':'replace', 'path': self.property, 'value': data } ]}
+
+ async def coroutine():
+ self.app.start_progressing()
+ response = await self.app.loop.run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
+ self.app.stop_progressing()
+ self.myparent.app_configuration = response
+ self.future.set_result(DialogResult.ACCEPT)
+ self.myparent.oauth_update_properties(start_index=self.myparent.oauth_update_properties_start_index)
+ asyncio.ensure_future(coroutine())
def get_type(self,prop):
+ """This Method get a property and get its type from schema to return the widget type to implement
+
+ Args:
+ prop (str): The property name
+
+ Returns:
+ str: the widget type to implement
+ """
try :
- if self.schema.get('properties', {})[prop]['type'] == 'string':
+ proper = self.schema.get('properties', {})[prop]
+
+ if proper['type'] == 'string':
prop_type= 'TitledText'
- elif self.schema.get('properties', {})[prop]['type'] == 'integer':
+ elif proper['type'] == 'integer':
prop_type= 'int-TitledText'
- elif self.schema.get('properties', {})[prop]['type'] == 'boolean':
+ elif proper['type'] == 'boolean':
prop_type= 'TitledCheckBox'
- elif self.schema.get('properties', {})[prop]['type'] == 'object':
+ elif proper['type'] == 'object':
prop_type= 'dict'
- elif self.schema.get('properties', {})[prop]['type'] == 'array':
- if 'enum' in self.schema.get('properties', {})[prop]:
+ elif proper['type'] == 'array':
+ if 'enum' in proper or ('enum' in proper['items']):
prop_type= 'checkboxlist'
else:
if type(self.value[0]) == dict:
prop_type= 'list-dict'
+ elif type(self.value[0]) == list:
+ prop_type= 'list-list'
else:
prop_type= 'long-TitledText'
except:
@@ -180,20 +156,35 @@ def get_type(self,prop):
return prop_type
- def get_listValues(self,prop):
+ def get_listValues(self,prop,type=None):
+ """This method get list values for properties own Enum values
+
+ Args:
+ prop (str): The property name
+ type (_type_, optional): If the Items in Property properties had a nasted Enum. Defaults to None.
+
+ Returns:
+ list: List of the properties enum to choose from
+ """
try :
- list_values= self.schema.get('properties', {})[prop]['enum']
+ if type !='nasted':
+ list_values= self.schema.get('properties', {})[prop]['items']['enum']
+ else:
+ list_values= self.schema.get('properties', {})[prop]['items']['items']['enum']
+
except:
list_values = []
return list_values
def prepare_properties(self):
+ """This method build the main value_content to edit the properties
+ """
prop_type = self.get_type(self.property)
if prop_type == 'TitledText':
- self.value_content= HSplit([self.myparent.getTitledText(
+ self.value_content= HSplit([self.app.getTitledText(
self.property,
name=self.property,
value=self.value,
@@ -202,7 +193,7 @@ def prepare_properties(self):
],width=D())
elif prop_type == 'int-TitledText':
- self.value_content= HSplit([self.myparent.getTitledText(
+ self.value_content= HSplit([self.app.getTitledText(
self.property,
name=self.property,
value=self.value,
@@ -212,7 +203,7 @@ def prepare_properties(self):
],width=D())
elif prop_type == 'long-TitledText':
- self.value_content= HSplit([self.myparent.getTitledText(
+ self.value_content= HSplit([self.app.getTitledText(
self.property,
name=self.property,
height=3,
@@ -221,9 +212,18 @@ def prepare_properties(self):
),
],width=D())
+ elif prop_type == 'list-list':
+ self.value_content= HSplit([
+ self.app.getTitledCheckBoxList(
+ self.property,
+ name=self.property,
+ values=self.get_listValues(self.property,'nasted'),
+ style='class:outh-client-checkboxlist'),
+ ],width=D())
+
elif prop_type == 'checkboxlist':
self.value_content= HSplit([
- self.myparent.getTitledCheckBoxList(
+ self.app.getTitledCheckBoxList(
self.property,
name=self.property,
values=self.get_listValues(self.property),
@@ -241,7 +241,7 @@ def prepare_properties(self):
tab_list=[]
for item in tab:
if type(tab[item]) == str:
- tab_list.append(HSplit([self.myparent.getTitledText(
+ tab_list.append(HSplit([self.app.getTitledText(
item ,
name=item,
value=tab[item],
@@ -250,7 +250,7 @@ def prepare_properties(self):
],width=D()))
if type(tab[item]) == int :
- tab_list.append(HSplit([self.myparent.getTitledText(
+ tab_list.append(HSplit([self.app.getTitledText(
item ,
name=item,
value=tab[item],
@@ -260,7 +260,7 @@ def prepare_properties(self):
],width=D()))
elif type(tab[item]) == list:
- tab_list.append(HSplit([self.myparent.getTitledText(
+ tab_list.append(HSplit([self.app.getTitledText(
item,
name=item,
height=3,
@@ -271,7 +271,7 @@ def prepare_properties(self):
elif type(tab[item]) == bool:
tab_list.append(HSplit([
- self.myparent.getTitledCheckBox(
+ self.app.getTitledCheckBox(
item,
name=item,
checked= tab[item],
@@ -281,7 +281,7 @@ def prepare_properties(self):
self.tabs['tab{}'.format(self.value.index(tab))] = HSplit(tab_list,width=D())
self.value_content=HSplit([
- self.myparent.getTitledRadioButton(
+ self.app.getTitledRadioButton(
_("Tab Num"),
name='tabNum',
current_value=self.selected_tab,
@@ -295,7 +295,7 @@ def prepare_properties(self):
elif prop_type == 'TitledCheckBox':
self.value_content= HSplit([
- self.myparent.getTitledCheckBox(
+ self.app.getTitledCheckBox(
self.property,
name=self.property,
checked= self.value,
@@ -306,7 +306,7 @@ def prepare_properties(self):
dict_list=[]
for item in self.value:
if type(self.value[item]) == str:
- dict_list.append(HSplit([self.myparent.getTitledText(
+ dict_list.append(HSplit([self.app.getTitledText(
item ,
name=item,
value=self.value[item],
@@ -315,7 +315,7 @@ def prepare_properties(self):
],width=D()))
elif type(self.value[item]) == int :
- dict_list.append(HSplit([self.myparent.getTitledText(
+ dict_list.append(HSplit([self.app.getTitledText(
item ,
name=item,
value=self.value[item],
@@ -325,7 +325,7 @@ def prepare_properties(self):
],width=D()))
elif type(self.value[item]) == list:
- dict_list.append(HSplit([self.myparent.getTitledText(
+ dict_list.append(HSplit([self.app.getTitledText(
item,
name=item,
height=3,
@@ -336,7 +336,7 @@ def prepare_properties(self):
elif type(self.value[item]) == bool:
dict_list.append(HSplit([
- self.myparent.getTitledCheckBox(
+ self.app.getTitledCheckBox(
item,
name=item,
checked= self.value[item],
@@ -344,7 +344,7 @@ def prepare_properties(self):
],width=D()))
else :
- dict_list.append(HSplit([self.myparent.getTitledText(
+ dict_list.append(HSplit([self.app.getTitledText(
item,
name=item,
value="No Items Here",
@@ -378,8 +378,19 @@ def tab_selection_changed(
self,
cb: RadioList,
) -> None:
+ """This method for properties that implemented in multi tab
+
+ Args:
+ cb (RadioList): the New Value from the nasted tab
+ """
self.selected_tab = cb.current_value
def __pt_container__(self)-> Dialog:
+ """The container for the dialog itself
+
+ Returns:
+ Dialog: View Property
+ """
+
return self.dialog
diff --git a/jans-cli-tui/cli_tui/plugins/010_oxauth/view_uma_dialog.py b/jans-cli-tui/cli_tui/plugins/010_oxauth/view_uma_dialog.py
index 99d801f76d6..d6c551df023 100644
--- a/jans-cli-tui/cli_tui/plugins/010_oxauth/view_uma_dialog.py
+++ b/jans-cli-tui/cli_tui/plugins/010_oxauth/view_uma_dialog.py
@@ -1,65 +1,29 @@
-import json
-from asyncio import Future
-from typing import OrderedDict
-
-from prompt_toolkit.widgets import Button, TextArea
-from prompt_toolkit.application.current import get_app
from prompt_toolkit.layout.dimension import D
from prompt_toolkit.layout.containers import (
VSplit,
DynamicContainer,
)
-from prompt_toolkit.key_binding import KeyBindings
-
-from prompt_toolkit.widgets import (
- Button,
- Label,
- TextArea,
-
-)
-from asyncio import ensure_future
-
-from prompt_toolkit.widgets import (
- Button,
- Dialog,
- VerticalLine,
-)
-from cli import config_cli
from prompt_toolkit.layout.containers import (
- ConditionalContainer,
- Float,
HSplit,
VSplit,
- VerticalAlign,
DynamicContainer,
- FloatContainer,
- Window,
- AnyContainer
)
from prompt_toolkit.widgets import (
Box,
Button,
- Frame,
Label,
- RadioList,
- TextArea,
+ Dialog,
)
-
import cli_style
from utils.multi_lang import _
from utils.utils import DialogUtils
from utils.static import DialogResult
-from wui_components.jans_dialog_with_nav import JansDialogWithNav
from wui_components.jans_nav_bar import JansNavBar
-from wui_components.jans_side_nav_bar import JansSideNavBar
-from wui_components.jans_dialog import JansDialog
from wui_components.jans_cli_dialog import JansGDialog
-from wui_components.jans_drop_down import DropDownWidget
from prompt_toolkit.formatted_text import AnyFormattedText
-from prompt_toolkit.layout.dimension import AnyDimension
-from typing import Optional, Sequence, Union
-from typing import TypeVar, Callable
+from typing import Optional, Sequence
+from typing import Callable
class ViewUMADialog(JansGDialog, DialogUtils):
"""The Main UMA-resources Dialog to view UMA Resource Details
@@ -81,8 +45,8 @@ def __init__(
parent (widget): This is the parent widget for the dialog
title (str): The Main dialog title
data (list): selected line data
- button_functions (list, optional): Dialog main buttons with their handlers. Defaults to [].
- save_handler (method, optional): handler invoked when closing the dialog. Defaults to None.
+ buttons (list, optional): Dialog main buttons with their handlers. Defaults to [].
+ deleted_uma (method, optional): handler invoked when Deleting UMA-res Defaults to None.
"""
super().__init__(parent, title, buttons)
self.data = data
@@ -212,5 +176,11 @@ def oauth_nav_selection_changed(
self.oauth_main_area = self.UMA_containers[selection]
def __pt_container__(self)-> Dialog:
+ """The container for the dialog itself
+
+ Returns:
+ Dialog: View Property
+ """
+
return self.dialog
diff --git a/jans-cli-tui/cli_tui/plugins/020_fido/main.py b/jans-cli-tui/cli_tui/plugins/020_fido/main.py
index a78504df3a6..7029fb94971 100755
--- a/jans-cli-tui/cli_tui/plugins/020_fido/main.py
+++ b/jans-cli-tui/cli_tui/plugins/020_fido/main.py
@@ -1,29 +1,18 @@
-import os
-import sys
import asyncio
-import time
-
from collections import OrderedDict
from functools import partial
from typing import Any
-
-from prompt_toolkit.application.current import get_app
from prompt_toolkit.layout.containers import HSplit, DynamicContainer, VSplit, Window
from prompt_toolkit.layout.dimension import D
-from prompt_toolkit.widgets import Button, Label, Frame, Box, Dialog
+from prompt_toolkit.widgets import Button, Label, Box, Dialog
from prompt_toolkit.application import Application
-
from wui_components.jans_nav_bar import JansNavBar
from wui_components.jans_drop_down import DropDownWidget
from wui_components.jans_vetrical_nav import JansVerticalNav
from wui_components.jans_cli_dialog import JansGDialog
-
-
from utils.multi_lang import _
from utils.utils import DialogUtils
-import cli_style
-
class Plugin(DialogUtils):
"""This is a general class for plugins
"""
@@ -34,7 +23,7 @@ def __init__(
"""init for Plugin class "fido"
Args:
- app (_type_): _description_
+ app (Generic): The main Application class
"""
self.app = app
self.pid = 'fido'
@@ -48,11 +37,14 @@ def process(self) -> None:
pass
def init_plugin(self) -> None:
+ """The initialization for this plugin
+ """
self.app.create_background_task(self.get_fido_configuration())
-
def edit_requested_party(self, **kwargs: Any) -> None:
+ """This method for editing the requested party
+ """
title = _("Enter Request Party Properties")
schema = self.app.cli_object.get_schema_from_reference('Fido2', '#/components/schemas/RequestedParty')
cur_data = kwargs.get('passed', ['', ''])
@@ -74,9 +66,28 @@ def add_request_party(dialog: Dialog) -> None:
dialog = JansGDialog(self.app, title=title, body=body, buttons=buttons, width=self.app.dialog_width-20)
self.app.show_jans_dialog(dialog)
-
def delete_requested_party(self, **kwargs: Any) -> None:
- self.requested_parties_container.remove_item(kwargs['selected'])
+ """This method for deleting the requested party
+ """
+
+ dialog = self.app.get_confirm_dialog(_("Are you sure want to delete requested patry:")+"\n {} ?".format(kwargs['selected'][1]))
+
+ async def coroutine():
+ focused_before = self.app.layout.current_window
+ result = await self.app.show_dialog_as_float(dialog)
+ try:
+ self.app.layout.focus(focused_before)
+ except:
+ self.app.stop_progressing()
+ self.app.layout.focus(self.app.center_frame)
+
+ if result.lower() == 'yes':
+ self.requested_parties_container.remove_item(kwargs['selected'])
+ self.app.stop_progressing()
+
+ return result
+
+ asyncio.ensure_future(coroutine())
def create_widgets(self):
self.schema = self.app.cli_object.get_schema_from_reference('Fido2', '#/components/schemas/AppConfiguration')
@@ -188,7 +199,6 @@ def create_widgets(self):
self.nav_selection_changed(list(self.tabs)[0])
-
async def get_fido_configuration(self) -> None:
'Coroutine for getting fido2 configuration.'
try:
@@ -253,8 +263,9 @@ def nav_selection_changed(
else:
self.main_area = self.app.not_implemented
-
def save_config(self) -> None:
+ """This method for saving the configuration
+ """
fido2_config = self.make_data_from_dialog(tabs={'configuration': self.tabs['configuration']})
fido2_static = self.make_data_from_dialog(tabs={'static': self.tabs['static']})
@@ -276,7 +287,6 @@ async def coroutine():
asyncio.ensure_future(coroutine())
-
def set_center_frame(self) -> None:
"""center frame content
"""
diff --git a/jans-cli-tui/cli_tui/plugins/030_scim/main.py b/jans-cli-tui/cli_tui/plugins/030_scim/main.py
index 650d8bcc096..0e01fd02636 100755
--- a/jans-cli-tui/cli_tui/plugins/030_scim/main.py
+++ b/jans-cli-tui/cli_tui/plugins/030_scim/main.py
@@ -1,21 +1,12 @@
-import os
-import sys
import asyncio
-
-from typing import Sequence
-
-
from prompt_toolkit.application import Application
from prompt_toolkit.layout.containers import HSplit, VSplit, Window
from prompt_toolkit.layout.dimension import D
-from prompt_toolkit.widgets import Button, Label, Frame
-from prompt_toolkit.formatted_text import HTML
+from prompt_toolkit.widgets import Button, Frame
from wui_components.jans_drop_down import DropDownWidget
-
from utils.utils import DialogUtils
from utils.multi_lang import _
-
class Plugin(DialogUtils):
"""This is a general class for plugins
"""
@@ -26,7 +17,7 @@ def __init__(
"""init for Plugin class "scim"
Args:
- app (_type_): _description_
+ app (Generic): The main Application class
"""
self.app = app
self.pid = 'scim'
@@ -37,7 +28,6 @@ def __init__(
body=HSplit([Button(text=_("Get Scim Configuration"), handler=self.get_app_config)], width=D()),
height=D())
-
def process(self) -> None:
pass
@@ -46,7 +36,6 @@ def set_center_frame(self) -> None:
"""
self.app.center_container = self.container
-
def create_widgets(self) -> None:
"""SCIM Application configuration widgets are created in this fonction
"""
@@ -87,7 +76,6 @@ def create_widgets(self) -> None:
self.app.center_container = self.container
-
def get_app_config(self) -> None:
"""Gets SCIM application configurations from server.
"""
@@ -104,7 +92,6 @@ async def coroutine():
asyncio.ensure_future(coroutine())
-
def save_app_config(self) -> None:
"""Save button handler for saving SCIM application configurations.
Once configuration data was obtained from form, patch operations are prepared and saved to server.
@@ -119,9 +106,8 @@ def save_app_config(self) -> None:
if data[key] and key not in self.app_config:
patche_list.append({'op':'add', 'path': key, 'value': data[key]})
-
if not patche_list:
- self.app.show_message(_("Warning"), _("No changes was done on Scim appilication configuration. Nothing to save."))
+ self.app.show_message(_("Warning"), _("No changes was done on Scim appilication configuration. Nothing to save."), tobefocused=self.app.center_container)
return
async def coroutine():
diff --git a/jans-cli-tui/cli_tui/plugins/040_config_api/main.py b/jans-cli-tui/cli_tui/plugins/040_config_api/main.py
index c493a292745..238ae2a4c27 100755
--- a/jans-cli-tui/cli_tui/plugins/040_config_api/main.py
+++ b/jans-cli-tui/cli_tui/plugins/040_config_api/main.py
@@ -1,35 +1,22 @@
-import os
-import sys
from prompt_toolkit.application import Application
-import threading
-import prompt_toolkit
-
from prompt_toolkit.layout.containers import (
HSplit,
VSplit,
HorizontalAlign,
DynamicContainer,
- Window,
)
from prompt_toolkit.application.current import get_app
from prompt_toolkit.buffer import Buffer
-
from prompt_toolkit.layout.dimension import D
-from prompt_toolkit.widgets import Button, Label, Frame
from wui_components.jans_nav_bar import JansNavBar
-from prompt_toolkit.layout.containers import HSplit, DynamicContainer, VSplit, Window
-from prompt_toolkit.widgets import Button, Label, Frame, Box, Dialog
+from prompt_toolkit.widgets import Button, Label, Box, Dialog
from wui_components.jans_cli_dialog import JansGDialog
from collections import OrderedDict
from functools import partial
-from typing import Any
from wui_components.jans_vetrical_nav import JansVerticalNav
from utils.multi_lang import _
from typing import Any, Optional
-from utils.utils import DialogUtils
-from utils.static import DialogResult
import asyncio
-from prompt_toolkit.widgets.base import RadioList
class Plugin():
"""This is a general class for plugins
@@ -41,7 +28,7 @@ def __init__(
"""init for Plugin class "config_api"
Args:
- app (_type_): _description_
+ app (Generic): The main Application class
"""
self.app = app
self.pid = 'config_api'
@@ -120,7 +107,7 @@ def create_widgets(self):
handler=self.get_adminui_permissions),
self.app.getTitledText(
- _("Search: "),
+ _("Search"),
name='oauth:scopes:search',
jans_help=_("Press enter to perform search"),
accept_handler=self.search_adminui_permissions,
@@ -147,7 +134,7 @@ def create_widgets(self):
handler=self.get_adminui_mapping),
self.app.getTitledText(
- _("Search: "),
+ _("Search"),
name='oauth:scopes:search',
jans_help=_("Press enter to perform search"),
accept_handler=self.search_adminui_mapping,
@@ -167,10 +154,6 @@ def create_widgets(self):
self.nav_selection_changed(list(self.containers)[0])
- #--------------------------------------------------------------------------------#
- #----------------------------------- accessroles --------------------------------#
- #--------------------------------------------------------------------------------#
-
def get_adminui_roles(self) -> None:
"""Method to get the admin ui roles from server
"""
@@ -191,13 +174,9 @@ async def coroutine():
asyncio.ensure_future(coroutine())
-
def adminui_update_roles(self,
) -> None:
- """update the current clients data to server
-
- Args:
- pattern (str, optional): endpoint arguments for the client data. Defaults to ''.
+ """update the current adminui-roles to server
"""
data =[]
@@ -210,10 +189,6 @@ def adminui_update_roles(self,
]
)
- # ------------------------------------------------------------------------------- #
- # --------------------------------- View Data ----------------------------------- #
- # ------------------------------------------------------------------------------- #
-
if data:
clients = JansVerticalNav(
myparent=self.app,
@@ -239,9 +214,7 @@ def adminui_update_roles(self,
self.app.show_message(_("Oops"), _("No matching result"), tobefocused=self.app.center_container)
def add_adminui_roles(self) -> None:
- """Method to display the dialog of clients
- """
- """Method to display the dialog of clients
+ """Method to display the dialog of adminui-roles
"""
self.adminui_role = self.app.getTitledText(
@@ -260,9 +233,6 @@ def save(dialog: Dialog) -> None:
role = self.adminui_role.me.text
desc = self.adminui_role_description.me.text
- # ------------------------------------------------------------#
- # --------------------- Patch to server ----------------------#
- # ------------------------------------------------------------#
if desc :
response = self.app.cli_object.process_command_by_id(
operation_id='add-adminui-role' ,
@@ -273,9 +243,7 @@ def save(dialog: Dialog) -> None:
)
else:
return
- # ------------------------------------------------------------#
- # -- get_properties or serach again to see Momentary change --#
- # ------------------------------------------------------------#
+
if response:
self.get_adminui_roles()
# self.future.set_result(DialogResult.ACCEPT)
@@ -289,55 +257,43 @@ def save(dialog: Dialog) -> None:
dialog = JansGDialog(self.app, title=_('Add New Role'), body=body, buttons=buttons, width=self.app.dialog_width-20)
self.app.show_jans_dialog(dialog)
-
def edit_adminui_roles(self, **params: Any) -> None:
- """Method to display the dialog of clients
+ """Method to display the dialog of admin-ui roles for editing
"""
role_data = params.get('data', {})
title = role_data.get('role','')
self.adminui_role_description = self.app.getTitledText(
- _("Domains"),
- name='domains',
+ _("Description"),
+ name='description',
value=role_data.get('description',''),
- height=3,
style='class:dialog-titled-widget')
self.adminui_role_deletable = self.app.getTitledCheckBox(
"Deletable",
name='deletable',
- checked= False,
+ checked= role_data.get('deletable', False),
jans_help= "Default to False",
- style='class:outh-client-checkbox')
+ style='class:outh-client-checkbox')
def save(dialog: Dialog) -> None:
desc = self.adminui_role_description.me.text
deletable = self.adminui_role_deletable.me.checked
- # ------------------------------------------------------------#
- # --------------------- Patch to server ----------------------#
- # ------------------------------------------------------------#
- if desc :
- response = self.app.cli_object.process_command_by_id(
- operation_id='edit-adminui-role' ,
- url_suffix='',
- endpoint_args='',
- data_fn='',
- data={'role': '{}'.format(title), 'description': '{}'.format(desc), 'deletable':'{}'.format(deletable)},
- )
- else:
- return
- # ------------------------------------------------------------#
- # -- get_properties or serach again to see Momentary change --#
- # ------------------------------------------------------------#
- if response:
+ async def coroutine():
+ cli_args = {
+ 'operation_id': 'edit-adminui-role',
+ 'data': {'role': '{}'.format(title), 'description': '{}'.format(desc), 'deletable':'{}'.format(deletable)}
+ }
+ self.app.start_progressing()
+ response = await self.app.loop.run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
+ self.app.stop_progressing()
self.get_adminui_roles()
- # self.future.set_result(DialogResult.ACCEPT)
- return True
-
- self.app.show_message(_("Error!"), _("An error ocurred while saving role adminui:\n") + str(response.text))
+ if response.status_code != 200:
+ self.app.show_message(_("Error!"), _("An error ocurred while saving role adminui:\n") + str(response.text), tobefocused=self.app.center_container)
+ asyncio.ensure_future(coroutine())
body = HSplit([self.adminui_role_description,self.adminui_role_deletable])
buttons = [Button(_("Cancel")), Button(_("OK"), handler=save)]
@@ -345,6 +301,8 @@ def save(dialog: Dialog) -> None:
self.app.show_jans_dialog(dialog)
def delete_adminui_roles(self, **kwargs: Any) -> None:
+ """Method to delete admin-ui roles
+ """
dialog = self.app.get_confirm_dialog(_("Are you sure want to delete adminui_roles :")+"\n {} ?".format(kwargs['selected'][0]))
@@ -368,66 +326,45 @@ async def coroutine(): ## Need to add editable
asyncio.ensure_future(coroutine())
- #--------------------------------------------------------------------------------#
- #------------------------------------- permissions ------------------------------#
- #--------------------------------------------------------------------------------#
-
- def get_adminui_permissions(self) -> None:
- """Method to get the adminui_permissions data from server
- """
- self.config_data_container['permissions'] = HSplit([Label(_("Please wait while getting adminui_permissions"),style='class:outh-waitclientdata.label')], width=D(),style='class:outh-waitclientdata')
- t = threading.Thread(target=self.adminui_update_permissions, daemon=True)
- self.app.start_progressing()
- t.start()
-
- def adminui_update_permissions(
- self,
- start_index: Optional[int]= 0,
- pattern: Optional[str]= '',
+ def get_adminui_permissions(self,
+ start_index: Optional[int]=0,
+ pattern: Optional[str]= ''
) -> None:
- """update the current adminui_permissions data to server
+ """Method to get the adminui_permissions data from server
Args:
- pattern (str, optional): endpoint arguments for the client data. Defaults to ''.
+ start_index (Optional[int], optional): This is flag for the adminui-roles pages. Defaults to 0.
+ pattern (str, optional): endpoint arguments for the client data. Defaults to ''.
"""
- def get_next(
- start_index: int,
- pattern: Optional[str]= '',
- ) -> None:
- self.adminui_update_permissions(start_index, pattern='')
-
- endpoint_args ='limit:{},startIndex:{}'.format(self.app.entries_per_page, start_index)
-
- try :
- rsponse = self.app.cli_object.process_command_by_id(
- operation_id='get-all-adminui-permissions',
- url_suffix='',
- endpoint_args='',
- data_fn=None,
- data={}
- )
-
- except Exception as e:
+ async def coroutine():
+ cli_args = {'operation_id': 'get-all-adminui-permissions'}
+ self.app.start_progressing()
+ response = await self.app.loop.run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
self.app.stop_progressing()
- self.app.show_message(_("Error getting adminui_permissions"), str(e))
- return
+ self.adminui_permissions_data = response.json()
+ self.adminui_update_permissions(start_index, pattern)
- self.app.stop_progressing()
+ asyncio.ensure_future(coroutine())
+
+ def adminui_update_permissions(self,
+ start_index: Optional[int]=0,
+ pattern: Optional[str]= ''
+ ) -> None:
+ """update the current adminui_permissions data to server
- if rsponse.status_code not in (200, 201):
- self.app.show_message(_("Error getting adminui_permissions"), str(rsponse.text))
- return
+ Args:.
+ start_index (Optional[int], optional): This is flag for the adminui-roles pages. Defaults to 0.
+ pattern (str, optional): endpoint arguments for the client data. Defaults to ''.
+ """
- try:
- result = rsponse.json()
- except Exception:
- self.app.show_message(_("Error getting adminui_permissions"), str(rsponse.text))
+ if not hasattr(self, 'adminui_permissions_data'):
+ self.get_adminui_permissions(start_index, pattern)
return
data =[]
if pattern:
- for k in result:
+ for k in self.adminui_permissions_data:
if pattern.lower() in k.get('permission').lower():
data.append(
[
@@ -436,7 +373,7 @@ def get_next(
]
)
else:
- for d in result:
+ for d in self.adminui_permissions_data:
data.append(
[
d.get('permission'),
@@ -446,21 +383,21 @@ def get_next(
if data:
buttons = []
- if int(len(data)/ 20) >=1 :
+ if int(len(data)/self.app.entries_per_page) >=1:
+ if start_index!=0:
+ handler_partial = partial(self.adminui_update_permissions, start_index-1, pattern)
+ prev_button = Button(_("Prev"), handler=handler_partial)
+ prev_button.window.jans_help = _("Displays previous %d entries") % self.app.entries_per_page
+ buttons.append(prev_button)
if start_index< int(len(data)/ 20) :
- handler_partial = partial(get_next, start_index+1, pattern)
+ handler_partial = partial(self.adminui_update_permissions, start_index+1, pattern)
next_button = Button(_("Next"), handler=handler_partial)
- next_button.window.jans_help = _("Retreives next %d entries") % self.app.entries_per_page
+ next_button.window.jans_help = _("Displays next %d entries") % self.app.entries_per_page
buttons.append(next_button)
- if start_index!=0:
- handler_partial = partial(get_next, start_index-1, pattern)
- prev_button = Button(_("Prev"), handler=handler_partial)
- prev_button.window.jans_help = _("Retreives previous %d entries") % self.app.entries_per_page
- buttons.append(prev_button)
data_now = data[start_index*20:start_index*20+20]
-
+
adminui_permissions = JansVerticalNav(
myparent=self.app,
headers=['permission', 'defaultPermissionInToken',],
@@ -473,20 +410,21 @@ def get_next(
selectes=0,
headerColor='class:outh-verticalnav-headcolor',
entriesColor='class:outh-verticalnav-entriescolor',
- all_data=result
+ all_data=self.adminui_permissions_data
)
- self.app.layout.focus(adminui_permissions)
+
+ self.app.layout.focus(adminui_permissions)
self.config_data_container['permissions'] = HSplit([
adminui_permissions,
VSplit(buttons, padding=5, align=HorizontalAlign.CENTER)
])
- get_app().invalidate()
- self.app.layout.focus(adminui_permissions)
+ self.app.invalidate()
+ self.app.layout.focus(adminui_permissions)
else:
- self.app.show_message(_("Oops"), _("No matching result"),tobefocused = self.config_data_container['permissions'])
+ self.app.show_message(_("Oops"), _("No matching result"), tobefocused=self.app.center_container)
def add_adminui_permissions(self) -> None:
- """Method to display the dialog of clients
+ """Method to display the dialog of adminui-roles
"""
self.adminui_permission = self.app.getTitledText(
@@ -532,22 +470,24 @@ async def coroutine():
dialog = JansGDialog(self.app, title=_('Add New Role'), body=body, buttons=buttons, width=self.app.dialog_width-20)
self.app.show_jans_dialog(dialog)
- def search_adminui_permissions(self, tbuffer:Buffer,) -> None:
+ def search_adminui_permissions(self, tbuffer:Buffer) -> None:
+ """This method handel the search for adminui_permissions
+
+ Args:
+ tbuffer (Buffer): Buffer returned from the TextArea widget > GetTitleText
+ """
if not len(tbuffer.text) > 2:
- self.app.show_message(_("Error!"), _("Search string should be at least three characters"),tobefocused=self.containers['permissions'])
+ self.app.show_message(_("Error!"), _("Search string should be at least three characters"), tobefocused=self.app.center_container)
return
- t = threading.Thread(target=self.adminui_update_permissions, args=(0,tbuffer.text), daemon=True)
- self.app.start_progressing()
- t.start()
+ self.adminui_update_permissions(0, tbuffer.text)
def edit_adminui_permissions(self, **params: Any) -> None:
- """Method to display the dialog of clients
+ """Method to display the dialog of adminui_permissions for editing
"""
role_data = params.get('passed', [])
permission = role_data[0]
-
defaultPermissionInToken = role_data[1]
@@ -587,9 +527,12 @@ def save(dialog: Dialog) -> None:
dialog = JansGDialog(self.app, title='admin ui permissions', body=body, buttons=buttons, width=self.app.dialog_width-20)
self.app.show_jans_dialog(dialog)
-
def delete_adminui_permissions(self, **kwargs: Any) -> None:
+ """This method for the deletion of the adminui_permissions
+ Returns:
+ str: The server response
+ """
dialog = self.app.get_confirm_dialog(_("Are you sure want to delete adminui_permissions :")+"\n {} ?".format(kwargs['selected'][0]))
@@ -617,76 +560,34 @@ async def coroutine():
asyncio.ensure_future(coroutine())
- #--------------------------------------------------------------------------------#
- #------------------------------------- mapping ----------------------------------#
- #--------------------------------------------------------------------------------#
-
- def get_adminui_mapping(self) -> None:
+ def get_adminui_mapping(self, pattern: Optional[str]= '') -> None:
"""Method to get the adminui_permissions data from server
- """
- self.config_data_container['mapping'] = HSplit([Label(_("Please wait while getting adminui_permissions"),style='class:outh-waitclientdata.label')], width=D(),style='class:outh-waitclientdata')
- t = threading.Thread(target=self.adminui_update_mapping, daemon=True)
- self.app.start_progressing()
- t.start()
-
- def adminui_update_mapping(
- self,
- start_index: Optional[int]= 0,
- pattern: Optional[str]= '',
- ) -> None:
+ """
+ async def coroutine():
+ cli_args = {'operation_id': 'get-all-adminui-role-permissions'}
+ self.app.start_progressing()
+ response = await self.app.loop.run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
+ self.app.stop_progressing()
+ self.adminui_role_permissions_data = response.json()
+ self.adminui_update_mapping(pattern)
+
+ asyncio.ensure_future(coroutine())
+
+ def adminui_update_mapping(self, pattern: Optional[str]= '') -> None:
"""update the current adminui_permissions data to server
Args:
pattern (str, optional): endpoint arguments for the client data. Defaults to ''.
"""
- def get_next(
- start_index: int,
- pattern: Optional[str]= '',
- ) -> None:
- self.adminui_update_mapping(start_index, pattern='')
-
- endpoint_args ='limit:{},startIndex:{}'.format(self.app.entries_per_page, start_index)
- if pattern:
- endpoint_args +=',pattern:'+pattern
- try :
- rsponse = self.app.cli_object.process_command_by_id(
- operation_id='get-all-adminui-role-permissions',
- url_suffix='',
- endpoint_args='',
- data_fn=None,
- data={}
- )
-
- except Exception as e:
- self.app.stop_progressing()
- self.app.show_message(_("Error getting adminui_permissions"), str(e))
- return
-
- self.app.stop_progressing()
-
- if rsponse.status_code not in (200, 201):
- self.app.show_message(_("Error getting adminui_permissions"), str(rsponse.text))
- return
-
- try:
- result = rsponse.json()
- except Exception:
- self.app.show_message(_("Error getting adminui_permissions"), str(rsponse.text))
+ if not hasattr(self, 'adminui_role_permissions_data'):
+ self.get_adminui_mapping(pattern)
return
data =[]
- # for d in result:
- # data.append(
- # [
- # d.get('role'),
- # len(d.get('permissions')),
- # ]
- # )
-
if pattern:
- for k in result:
+ for k in self.adminui_role_permissions_data:
if pattern.lower() in k.get('role').lower():
data.append(
[
@@ -695,7 +596,7 @@ def get_next(
]
)
else:
- for d in result:
+ for d in self.adminui_role_permissions_data:
data.append(
[
d.get('role'),
@@ -703,138 +604,42 @@ def get_next(
]
)
-
if data:
- buttons = []
- if int(len(data)/ 20) >=1 :
-
- if start_index< int(len(data)/ 20) :
- handler_partial = partial(get_next, start_index+1, pattern)
- next_button = Button(_("Next"), handler=handler_partial)
- next_button.window.jans_help = _("Retreives next %d entries") % self.app.entries_per_page
- buttons.append(next_button)
-
- if start_index!=0:
- handler_partial = partial(get_next, start_index-1, pattern)
- prev_button = Button(_("Prev"), handler=handler_partial)
- prev_button.window.jans_help = _("Retreives previous %d entries") % self.app.entries_per_page
- buttons.append(prev_button)
- data_now = data[start_index*20:start_index*20+20]
-
adminui_permissions = JansVerticalNav(
myparent=self.app,
headers=['role', 'permissions',],
preferred_size= [0,0],
- data=data_now,
+ data=data,
on_enter=self.edit_adminui_mapping,
on_display=self.app.data_display_dialog,
# get_help=(self.get_help,'AdminRole'),
selectes=0,
headerColor='class:outh-verticalnav-headcolor',
entriesColor='class:outh-verticalnav-entriescolor',
- all_data=result
+ all_data=self.adminui_role_permissions_data
)
self.app.layout.focus(adminui_permissions) # clients.focuse..!? TODO >> DONE
- self.config_data_container['mapping'] = HSplit([
- adminui_permissions,
- VSplit(buttons, padding=5, align=HorizontalAlign.CENTER)
- ])
- get_app().invalidate()
- self.app.layout.focus(adminui_permissions) ### it fix focuse on the last item deletion >> try on UMA-res >> edit_client_dialog >> oauth_update_uma_resources
-
+ self.config_data_container['mapping'] = adminui_permissions
+ self.app.invalidate()
+ self.app.layout.focus(adminui_permissions)
+
else:
- self.app.show_message(_("Oops"), _("No matching result"),tobefocused = self.config_data_container['mapping'])
-
- # def add_adminui_mapping(self) -> None:
- # try :
- # rsponse = self.app.cli_object.process_command_by_id(
- # operation_id='get-all-adminui-roles',
- # url_suffix='',
- # endpoint_args='',
- # data_fn=None,
- # data={}
- # )
-
- # except Exception as e:
- # self.app.stop_progressing()
- # self.app.show_message(_("Error getting clients"), str(e))
- # return
-
- # values=[]
- # for i in rsponse.json():
- # values.append((i['role'],i['role']))
-
- # #------------------------------------------------------------------------#
- # #- values = [(api-manager,api-manager),(api-admin,api-admin),(api-editor,api-editor),(api-viewer,api-viewer)]#
- # #------------------------------------------------------------------------#
-
- # self.alt_tabs = {}
- # self.alt_tabs['api-manager'] = Label(text=_("api-manager"),style='red')
- # self.alt_tabs['api-admin'] = Label(text=_("api-admin"),style='red')
- # self.alt_tabs['api-editor'] = Label(text=_("api-editor"),style='red')
- # self.alt_tabs['api-viewer'] = Label(text=_("api-viewer"),style='red')
- # self.alt_tabs['api-editor2'] = Label(text=_("api-editor2"),style='red')
- # self.alt_tabs['api-hopa'] = Label(text=_("api-hopa"),style='red')
-
- # def role_selection_changed(
- # cb: RadioList,
- # ) -> None:
- # self.role_type = cb.current_value
-
- # self.adminui_mapping= self.app.getTitledRadioButton(
- # _("role"),
- # name='role',
- # values=values,
- # on_selection_changed=role_selection_changed,
- # style='class:outh-scope-radiobutton')
-
-
- # def save(dialog: Dialog) -> None:
-
- # permission = self.adminui_permission.me.text
- # defaultPermissionInToken = self.adminui_role_permissions.me.checked
-
- # self.app.logger.debug("defaultPermissionInToken: "+str(defaultPermissionInToken))
- # # ------------------------------------------------------------#
- # # --------------------- Patch to server ----------------------#
- # # ------------------------------------------------------------#
- # if permission :
- # response = self.app.cli_object.process_command_by_id(
- # operation_id='add-adminui-permission',
- # url_suffix='',
- # endpoint_args='',
- # data_fn='',
- # data={'permission': '{}'.format(permission), 'defaultPermissionInToken': '{}'.format(defaultPermissionInToken)},
- # )
- # else:
- # return
- # # ------------------------------------------------------------#
- # # -- get_properties or serach again to see Momentary change --#
- # # ------------------------------------------------------------#
- # if response:
- # self.get_adminui_permissions()
- # # self.future.set_result(DialogResult.ACCEPT)
- # return True
-
- # self.app.show_message(_("Error!"), _("An error ocurred while Addin role adminui permission:\n") + str(response.text))
-
-
- # body = HSplit([self.adminui_mapping,DynamicContainer(lambda: self.alt_tabs[self.role_type])])
- # buttons = [Button(_("Cancel")), Button(_("OK"), handler=save)]
- # dialog = JansGDialog(self.app, title=_('Add New Role'), body=body, buttons=buttons, width=self.app.dialog_width-20)
- # self.app.show_jans_dialog(dialog)
-
+ self.app.show_message(_("Oops"), _("No matching result"), tobefocused=self.app.center_container)
+
def search_adminui_mapping(self, tbuffer:Buffer,) -> None:
+ """This method handel the search for adminui_mapping
+
+ Args:
+ tbuffer (Buffer): Buffer returned from the TextArea widget > GetTitleText
+ """
if not len(tbuffer.text) > 2:
self.app.show_message(_("Error!"), _("Search string should be at least three characters"),tobefocused=self.containers['mapping'])
return
- t = threading.Thread(target=self.adminui_update_mapping, args=(0,tbuffer.text), daemon=True)
- self.app.start_progressing()
- t.start()
+ self.adminui_update_mapping(tbuffer.text)
def edit_adminui_mapping(self, **params: Any) -> None:
- """Method to display the dialog of clients
+ """Method to display the dialog of adminui_mapping
"""
role_data = params.get('data', [])
permission = role_data.get('role')
@@ -888,10 +693,6 @@ def save(dialog: Dialog) -> None:
dialog = JansGDialog(self.app, title='admin ui permissions', body=body, buttons=buttons, width=self.app.dialog_width-20)
self.app.show_jans_dialog(dialog)
- #--------------------------------------------------------------------------------#
- #--------------------------------------------------------------------------------#
- #--------------------------------------------------------------------------------#
-
def nav_selection_changed(
self,
selection: str
diff --git a/jans-cli-tui/cli_tui/plugins/060_scripts/edit_script_dialog.py b/jans-cli-tui/cli_tui/plugins/060_scripts/edit_script_dialog.py
index 9263b71af24..e55b9a35e5b 100755
--- a/jans-cli-tui/cli_tui/plugins/060_scripts/edit_script_dialog.py
+++ b/jans-cli-tui/cli_tui/plugins/060_scripts/edit_script_dialog.py
@@ -1,19 +1,11 @@
-import re
-import threading
-
-from typing import OrderedDict
-from asyncio import ensure_future
from functools import partial
-
from prompt_toolkit.layout.dimension import D
from prompt_toolkit.layout.containers import (
HSplit,
VSplit,
- DynamicContainer,
Window
)
from prompt_toolkit.widgets import (
- Box,
Button,
Label,
TextArea,
@@ -21,38 +13,23 @@
Button,
Dialog,
)
-from prompt_toolkit.application.current import get_app
-
+import asyncio
from prompt_toolkit.lexers import PygmentsLexer
from pygments.lexers.python import PythonLexer
from pygments.lexers.jvm import JavaLexer
-
-from cli import config_cli
from utils.static import DialogResult
from wui_components.jans_dialog_with_nav import JansDialogWithNav
-from wui_components.jans_side_nav_bar import JansSideNavBar
from wui_components.jans_cli_dialog import JansGDialog
from wui_components.jans_drop_down import DropDownWidget
-from wui_components.jans_data_picker import DateSelectWidget
from utils.utils import DialogUtils
from wui_components.jans_vetrical_nav import JansVerticalNav
from wui_components.jans_spinner import Spinner
-from prompt_toolkit.layout.containers import (
- AnyContainer,
-)
from prompt_toolkit.formatted_text import AnyFormattedText
-from prompt_toolkit.layout.dimension import AnyDimension
-from typing import Optional, Sequence, Union
-from typing import TypeVar, Callable
-
+from typing import Optional, Sequence
+from typing import Callable
from typing import Any, Optional
-
-
-from view_uma_dialog import ViewUMADialog
-
from utils.multi_lang import _
-
class EditScriptDialog(JansGDialog, DialogUtils):
"""This Script editing dialog
"""
@@ -66,8 +43,9 @@ def __init__(
)-> Dialog:
"""init for `EditScriptDialog`, inherits from two diffrent classes `JansGDialog` and `DialogUtils`
+ JansGDialog (dialog): This is the main dialog Class Widget for all Jans-cli-tui dialogs except custom dialogs like dialogs with navbar
DialogUtils (methods): Responsable for all `make data from dialog` and `check required fields` in the form for any Edit or Add New
-
+
Args:
parent (widget): This is the parent widget for the dialog, to access `Pageup` and `Pagedown`
title (str): The Main dialog title
@@ -85,6 +63,8 @@ def __init__(
self.script = self.data.get('script','')
def save(self) -> None:
+ """method to invoked when saving the dialog (Save button is pressed)
+ """
data = {}
@@ -133,6 +113,9 @@ def save(self) -> None:
self.future.set_result(DialogResult.ACCEPT)
def cancel(self) -> None:
+ """method to invoked when canceling changes in the dialog (Cancel button is pressed)
+ """
+
self.future.set_result(DialogResult.CANCEL)
def create_window(self) -> None:
@@ -319,27 +302,35 @@ def create_window(self) -> None:
width=self.myparent.dialog_width,
)
-
def get_help(self, **kwargs: Any):
+ """This method get focused field Description to display on statusbar
+ """
# schema = self.app.cli_object.get_schema_from_reference('#/components/schemas/{}'.format(str(kwargs['scheme'])))
if kwargs['scheme'] == 'Properties':
self.myparent.status_bar_text= kwargs['data'][0]
-
-
-
def script_lang_changed(
self,
value: str,
) -> None:
+ """Change the script lang
+
+ Args:
+ value (str): lang to change to (python, java)
+ """
self.cur_lang = value
def set_location_widget_state(
self,
state: bool,
) -> None:
+ """This method check the state of the location to save script
+
+ Args:
+ state (bool): state is changed or not
+ """
self.location_widget.me.read_only = not state
self.location_widget.me.focusable = state
if not state:
@@ -349,10 +340,17 @@ def script_location_changed(
self,
redio_button: RadioList,
) -> None:
+ """Location to save Script
+
+ Args:
+ redio_button (RadioList): Where to save the scripts (Database, Filesystem)
+ """
state = redio_button.current_value == 'file'
self.set_location_widget_state(state)
def edit_property(self, **kwargs: Any) -> None:
+ """This method for editing the properties
+ """
if kwargs['jans_name'] == 'moduleProperties':
key, val = kwargs.get('data', ('',''))
@@ -391,12 +389,35 @@ def add_property(dialog: Dialog) -> None:
self.myparent.show_jans_dialog(dialog)
def delete_config_property(self, **kwargs: Any) -> None:
- if kwargs['jans_name'] == 'configurationProperties':
- self.config_properties_container.remove_item(kwargs['selected'])
- else:
- self.module_properties_container.remove_item(kwargs['selected'])
+ """This method for deleting the coniguration of properties
+ """
+ dialog = self.myparent.get_confirm_dialog(_("Are you sure want to delete property with Key:")+"\n {} ?".format(kwargs['selected'][0]))
+
+ async def coroutine():
+ focused_before = self.myparent.layout.current_window
+ result = await self.myparent.show_dialog_as_float(dialog)
+ try:
+ self.myparent.layout.focus(focused_before)
+ except:
+ self.myparent.stop_progressing()
+ self.myparent.layout.focus(self.myparent.center_frame)
+
+ if result.lower() == 'yes':
+ if kwargs['jans_name'] == 'configurationProperties':
+ self.config_properties_container.remove_item(kwargs['selected'])
+ else:
+ self.module_properties_container.remove_item(kwargs['selected'])
+ self.myparent.stop_progressing()
+
+ return result
+
+ asyncio.ensure_future(coroutine())
+
+
def edit_script_dialog(self) -> None:
+ """This method shows the script itself and let the user view or edit it
+ """
text_editor = TextArea(
text=self.script,
@@ -417,5 +438,10 @@ def modify_script(arg) -> None:
self.myparent.show_jans_dialog(dialog)
def __pt_container__(self)-> Dialog:
+ """The container for the dialog itself
+
+ Returns:
+ Dialog: The Edit Script Dialog
+ """
return self.dialog
diff --git a/jans-cli-tui/cli_tui/plugins/060_scripts/main.py b/jans-cli-tui/cli_tui/plugins/060_scripts/main.py
index dcc44894f33..a3c8e19d789 100755
--- a/jans-cli-tui/cli_tui/plugins/060_scripts/main.py
+++ b/jans-cli-tui/cli_tui/plugins/060_scripts/main.py
@@ -1,13 +1,6 @@
-import os
-import sys
-
from functools import partial
import asyncio
-
-import prompt_toolkit
from prompt_toolkit.application.current import get_app
-from prompt_toolkit.key_binding import KeyBindings
-from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
from prompt_toolkit.layout.containers import (
HSplit,
VSplit,
@@ -17,32 +10,16 @@
)
from prompt_toolkit.layout.dimension import D
from prompt_toolkit.widgets import (
- Box,
Button,
- Label,
- Frame,
Dialog
)
from typing import Any, Optional
from prompt_toolkit.buffer import Buffer
-
from utils.static import DialogResult
-
-from cli import config_cli
-from wui_components.jans_nav_bar import JansNavBar
-from wui_components.jans_side_nav_bar import JansSideNavBar
from wui_components.jans_vetrical_nav import JansVerticalNav
-from wui_components.jans_dialog import JansDialog
-from wui_components.jans_dialog_with_nav import JansDialogWithNav
-from wui_components.jans_drop_down import DropDownWidget
-from wui_components.jans_data_picker import DateSelectWidget
-
from edit_script_dialog import EditScriptDialog
from prompt_toolkit.application import Application
-
from utils.multi_lang import _
-import cli_style
-
class Plugin():
"""This is a general class for plugins
@@ -51,10 +28,10 @@ def __init__(
self,
app: Application
) -> None:
- """init for Plugin class "oxauth"
+ """init for Plugin class "scripts"
Args:
- app (_type_): _description_
+ app (Generic): The main Application class
"""
self.app = app
self.pid = 'scripts'
@@ -87,12 +64,17 @@ def scripts_prepare_containers(self) -> None:
DynamicContainer(lambda: self.scripts_list_container)
],style='class:outh_containers_scopes')
-
def get_scripts(
self,
start_index: Optional[int]= 1,
pattern: Optional[str]= '',
) -> None:
+ """Get the current Scripts from server
+
+ Args:
+ start_index (Optional[int], optional): This is flag for the Scripts pages. Defaults to 0.
+ pattern (Optional[str], optional):endpoint arguments for the Scripts. Defaults to ''.
+ """
endpoint_args ='limit:{},startIndex:{}'.format(self.app.entries_per_page, start_index)
if pattern:
@@ -116,7 +98,6 @@ async def coroutine():
asyncio.ensure_future(coroutine())
-
def scripts_update_list(
self,
pattern: Optional[str]= '',
@@ -124,7 +105,7 @@ def scripts_update_list(
"""Updates Scripts data from server
Args:
- start_index (int, optional): add Button("Prev") to the layout. Defaults to 0.
+ pattern (Optional[str], optional):endpoint arguments for the Scripts. Defaults to ''.
"""
data =[]
@@ -175,17 +156,20 @@ def scripts_update_list(
self.app.layout.focus(self.scripts_listbox)
get_app().invalidate()
-
def get_help(self, **kwargs: Any):
+ """This method get focused field Description to display on statusbar
+ """
# schema = self.app.cli_object.get_schema_from_reference('#/components/schemas/{}'.format(str(kwargs['scheme'])))
-
if kwargs['scheme'] == 'Scripts':
self.app.status_bar_text= kwargs['data'][2]
-
-
def search_scripts(self, tbuffer:Buffer) -> None:
+ """This method handel the search for scripts
+
+ Args:
+ tbuffer (Buffer): Buffer returned from the TextArea widget > GetTitleText
+ """
if not len(tbuffer.text) > 2:
self.app.show_message(_("Error!"), _("Search string should be at least three characters"), tobefocused=self.scripts_main_area)
return
@@ -212,7 +196,7 @@ def save_script(self, dialog: Dialog) -> None:
dialog (_type_): the main dialog to save data in
Returns:
- _type_: bool value to check the status code response
+ bool : value to check the status code response
"""
async def coroutine():
@@ -231,6 +215,8 @@ async def coroutine():
asyncio.ensure_future(coroutine())
def delete_script(self, **kwargs: Any) -> None:
+ """This method for the deletion of the Scripts
+ """
def do_delete_script():
diff --git a/jans-cli-tui/cli_tui/plugins/070_users/edit_user_dialog.py b/jans-cli-tui/cli_tui/plugins/070_users/edit_user_dialog.py
index 90b31613c96..1c50760bac4 100644
--- a/jans-cli-tui/cli_tui/plugins/070_users/edit_user_dialog.py
+++ b/jans-cli-tui/cli_tui/plugins/070_users/edit_user_dialog.py
@@ -1,50 +1,30 @@
-import re
-import threading
-
-from typing import OrderedDict, Optional, Sequence, Union, TypeVar, Callable
+from typing import Optional, Sequence, Callable
import asyncio
from functools import partial
-
-
from prompt_toolkit.layout.dimension import D
from prompt_toolkit.layout.containers import (
HSplit,
VSplit,
DynamicContainer,
Window,
- AnyContainer
)
from prompt_toolkit.widgets import (
- Box,
Button,
Label,
- TextArea,
- RadioList,
CheckboxList,
- Button,
Dialog,
)
from prompt_toolkit.eventloop import get_event_loop
-
-from cli import config_cli
from utils.static import DialogResult
from wui_components.jans_dialog_with_nav import JansDialogWithNav
-from wui_components.jans_side_nav_bar import JansSideNavBar
from wui_components.jans_cli_dialog import JansGDialog
-from wui_components.jans_drop_down import DropDownWidget
-from wui_components.jans_data_picker import DateSelectWidget
from utils.utils import DialogUtils, common_data
from wui_components.jans_vetrical_nav import JansVerticalNav
from prompt_toolkit.formatted_text import AnyFormattedText
-
from typing import Any, Optional
-
-
-from view_uma_dialog import ViewUMADialog
-
+from prompt_toolkit.layout import ScrollablePane
from utils.multi_lang import _
-
class EditUserDialog(JansGDialog, DialogUtils):
"""This user editing dialog
"""
@@ -58,13 +38,14 @@ def __init__(
)-> Dialog:
"""init for `EditUserDialog`, inherits from two diffrent classes `JansGDialog` and `DialogUtils`
+ JansGDialog (dialog): This is the main dialog Class Widget for all Jans-cli-tui dialogs except custom dialogs like dialogs with navbar
DialogUtils (methods): Responsable for all `make data from dialog` and `check required fields` in the form for any Edit or Add New
-
+
Args:
parent (widget): This is the parent widget for the dialog, to access `Pageup` and `Pagedown`
title (str): The Main dialog title
data (list): selected line data
- button_functions (list, optional): Dialog main buttons with their handlers. Defaults to [].
+ buttons (list, optional): Dialog main buttons with their handlers. Defaults to [].
save_handler (method, optional): handler invoked when closing the dialog. Defaults to None.
"""
super().__init__(parent, title, buttons)
@@ -77,10 +58,21 @@ def __init__(
self.create_window()
def cancel(self) -> None:
- self.future.set_result(DialogResult.CANCEL)
+ """method to invoked when canceling changes in the dialog (Cancel button is pressed)
+ """
+ self.future.set_result(DialogResult.CANCEL)
def get_claim_properties(self, claim):
+ """This method for getting claims properties
+
+ Args:
+ claim (str): Claim
+
+ Returns:
+ _type_: properties
+ """
+
ret_val = {}
for tmp in common_data.users.claims:
if tmp['name'] == claim:
@@ -171,7 +163,10 @@ def get_custom_attribute(attribute, multi=False):
self.app.getTitledCheckBox(_(claim_prop['displayName']), name=ca['name'], checked=checked, style='class:script-checkbox', jans_help=self.app.get_help_from_schema(self.schema, ca['name']))
)
- self.edit_user_container = HSplit(self.edit_user_content, height=D(), width=D())
+ self.edit_user_container = ScrollablePane(content=HSplit(self.edit_user_content, width=D()),show_scrollbar=False)
+
+
+
self.dialog = JansDialogWithNav(
title=self.title,
@@ -181,11 +176,12 @@ def get_custom_attribute(attribute, multi=False):
width=self.app.dialog_width,
)
-
def get_admin_ui_roles(self) -> None:
+ """This method for getting admin ui roles
+ """
async def coroutine():
cli_args = {'operation_id': 'get-all-adminui-roles'}
- self.app.start_progressing()
+ self.app.start_progressing(_("Retreiving admin UI roles from server..."))
response = await get_event_loop().run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
self.app.stop_progressing()
self.admin_ui_roles = response.json()
@@ -193,6 +189,8 @@ async def coroutine():
asyncio.ensure_future(coroutine())
def add_admin_ui_role(self) -> None:
+ """This method for adding new admin ui roles
+ """
if not self.admin_ui_roles:
self.get_admin_ui_roles()
return
@@ -216,13 +214,14 @@ def add_role(dialog) -> None:
dialog = JansGDialog(self.app, title=_("Select Admin-UI"), body=body, buttons=buttons, width=self.app.dialog_width-20)
self.app.show_jans_dialog(dialog)
-
def delete_admin_ui_role(self, **kwargs: Any) -> None:
+ """This method for deleting admin ui roles
+ """
self.admin_ui_roles_container.remove_item(kwargs['selected'])
-
-
def add_claim(self) -> None:
+ """This method for adding new claim
+ """
cur_claims = []
for w in self.edit_user_content:
if hasattr(w, 'me'):
@@ -249,7 +248,8 @@ def add_claim(dialog) -> None:
else:
widget = self.app.getTitledText(_(display_name), name=claim_, value='', style='class:script-titledtext', jans_help=self.app.get_help_from_schema(self.schema, claim_))
self.edit_user_content.insert(-1, widget)
- self.edit_user_container = HSplit(self.edit_user_content, height=D(), width=D())
+ self.edit_user_container = ScrollablePane(content=HSplit(self.edit_user_content, width=D()),show_scrollbar=False)
+
body = HSplit([Label(_("Select claim to be added to current user.")), claims_checkbox])
buttons = [Button(_("Cancel")), Button(_("OK"), handler=add_claim)]
@@ -257,5 +257,11 @@ def add_claim(dialog) -> None:
self.app.show_jans_dialog(dialog)
def __pt_container__(self)-> Dialog:
+ """The container for the dialog itself
+
+ Returns:
+ Dialog: The Edit User Dialog
+ """
+
return self.dialog
diff --git a/jans-cli-tui/cli_tui/plugins/070_users/main.py b/jans-cli-tui/cli_tui/plugins/070_users/main.py
index 796c1c16ff2..2eefaa6434b 100755
--- a/jans-cli-tui/cli_tui/plugins/070_users/main.py
+++ b/jans-cli-tui/cli_tui/plugins/070_users/main.py
@@ -1,22 +1,14 @@
-import os
-import sys
import asyncio
-
from functools import partial
from types import SimpleNamespace
-from typing import Sequence, Any, Optional
-
+from typing import Any, Optional
from prompt_toolkit.buffer import Buffer
from prompt_toolkit.application import Application
-from prompt_toolkit.layout.containers import HSplit, VSplit, Window, DynamicContainer, HorizontalAlign
+from prompt_toolkit.layout.containers import HSplit, VSplit, DynamicContainer, HorizontalAlign
from prompt_toolkit.layout.dimension import D
-from prompt_toolkit.widgets import Button, Label, Frame, Dialog
-from prompt_toolkit.formatted_text import HTML
-from wui_components.jans_drop_down import DropDownWidget
-from wui_components.jans_cli_dialog import JansGDialog
+from prompt_toolkit.widgets import Button, Dialog
from wui_components.jans_vetrical_nav import JansVerticalNav
from edit_user_dialog import EditUserDialog
-
from utils.utils import DialogUtils, common_data
from utils.static import DialogResult
from utils.multi_lang import _
@@ -33,7 +25,7 @@ def __init__(
"""init for Plugin class "users"
Args:
- app (_type_): _description_
+ app (Generic): The main Application class
"""
self.app = app
self.pid = 'users'
@@ -41,8 +33,6 @@ def __init__(
self.users = {}
self.widgets_ready = False
-
-
def process(self) -> None:
pass
@@ -61,7 +51,7 @@ def set_center_frame(self) -> None:
self.app.center_container = HSplit([
VSplit([
self.app.getButton(text=_("Get Users"), name='oauth:scopes:get', jans_help=_("Retreive first {} users").format(self.app.entries_per_page), handler=self.get_users),
- self.app.getTitledText(_("Search: "), name='oauth:scopes:search', jans_help=_("Press enter to perform search"), accept_handler=self.search_user, style='class:outh_containers_scopes.text'),
+ self.app.getTitledText(_("Search"), name='oauth:scopes:search', jans_help=_("Press enter to perform search"), accept_handler=self.search_user, style='class:outh_containers_scopes.text'),
self.app.getButton(text=_("Add Users"), name='oauth:scopes:add', jans_help=_("To add a new user press this button"), handler=self.edit_user_dialog),
],
padding=3,
@@ -71,7 +61,6 @@ def set_center_frame(self) -> None:
DynamicContainer(lambda: self.nav_buttons),
],style='class:outh_containers_scopes')
-
def update_user_list_container(self, pattern: Optional[str]='') -> None:
"""User management list
"""
@@ -114,7 +103,6 @@ def update_user_list_container(self, pattern: Optional[str]='') -> None:
self.app.invalidate()
-
def get_users(self, start_index: int=1, pattern: Optional[str]='') -> None:
"""Gets Users from server.
"""
@@ -125,7 +113,7 @@ def get_users(self, start_index: int=1, pattern: Optional[str]='') -> None:
cli_args = {'operation_id': 'get-user', 'endpoint_args': endpoint_args}
async def coroutine():
- self.app.start_progressing()
+ self.app.start_progressing(_("Retreiving users from server..."))
response = await self.app.loop.run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
self.app.stop_progressing()
self.users = response.json()
@@ -141,7 +129,6 @@ async def coroutine():
asyncio.ensure_future(coroutine())
-
def edit_user_dialog(self, **kwargs: Any) -> None:
"""Method to display the edit user dialog
"""
@@ -155,15 +142,16 @@ def edit_user_dialog(self, **kwargs: Any) -> None:
edit_user_dialog = EditUserDialog(self.app, title=title, data=data, save_handler=self.save_user)
self.app.show_jans_dialog(edit_user_dialog)
-
def delete_user(self, **kwargs: Any) -> None:
+ """This method for the deletion of the User
+ """
def do_delete_user():
for user in self.users['entries']:
if user.get('userId') == kwargs['selected'][1]:
async def coroutine():
cli_args = {'operation_id': 'delete-user', 'url_suffix':'inum:{}'.format(user['inum'])}
- self.app.start_progressing()
+ self.app.start_progressing(_("Deleting user {}").format(user['userId']))
response = await self.app.loop.run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
self.app.stop_progressing()
if response:
@@ -182,7 +170,6 @@ async def coroutine():
tobefocused=self.user_list_container
)
-
def save_user(self, dialog: Dialog) -> None:
"""This method to save user data to server
@@ -244,7 +231,7 @@ def save_user(self, dialog: Dialog) -> None:
async def coroutine():
operation_id = 'put-user' if dialog.data.get('baseDn') else 'post-user'
cli_args = {'operation_id': operation_id, 'data': user_info}
- self.app.start_progressing()
+ self.app.start_progressing(_("Saving user ..."))
response = await self.app.loop.run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
self.app.stop_progressing()
if response.status_code == 500:
@@ -255,13 +242,14 @@ async def coroutine():
asyncio.ensure_future(coroutine())
-
def get_claims(self) -> None:
+ """This method for getting claims
+ """
if hasattr(common_data.users, 'claims'):
return
async def coroutine():
cli_args = {'operation_id': 'get-attributes', 'endpoint_args':'limit:200,status:active'}
- self.app.start_progressing()
+ self.app.start_progressing(_("Retreiving claims"))
response = await self.app.loop.run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
self.app.stop_progressing()
result = response.json()
@@ -270,6 +258,11 @@ async def coroutine():
asyncio.ensure_future(coroutine())
def search_user(self, tbuffer:Buffer) -> None:
+ """This method handel the search for Users
+
+ Args:
+ tbuffer (Buffer): Buffer returned from the TextArea widget > GetTitleText
+ """
if not len(tbuffer.text) > 2:
self.app.show_message(_("Error!"), _("Search string should be at least three characters"), tobefocused=self.app.center_container)
return
diff --git a/jans-cli-tui/cli_tui/plugins/999_jans/main.py b/jans-cli-tui/cli_tui/plugins/999_jans/main.py
index fd74ef8009c..5b877812fae 100755
--- a/jans-cli-tui/cli_tui/plugins/999_jans/main.py
+++ b/jans-cli-tui/cli_tui/plugins/999_jans/main.py
@@ -1,23 +1,11 @@
-import os
-import sys
import asyncio
-
-from typing import Sequence
-
-
from prompt_toolkit.application import Application
-from prompt_toolkit.layout.containers import HSplit, VSplit, Window, Float
+from prompt_toolkit.layout.containers import HSplit
from prompt_toolkit.layout.dimension import D
-from prompt_toolkit.widgets import Button, Label, Frame
-from prompt_toolkit.formatted_text import HTML
-from prompt_toolkit.widgets import Shadow
-from prompt_toolkit.layout.controls import FormattedTextControl
-
-
+from prompt_toolkit.widgets import Button, Frame
from utils.multi_lang import _
from cli import config_cli
-
class Plugin:
"""This is a general class for plugins
"""
@@ -28,7 +16,7 @@ def __init__(
"""init for Plugin class "Jans CLI Menu"
Args:
- app (_type_): _description_
+ app (Generic): The main Application class
"""
self.app = app
self.pid = 'jans-menu'
@@ -45,7 +33,6 @@ def __init__(
height=D()
)
-
def process(self) -> None:
pass
@@ -55,13 +42,11 @@ def set_center_frame(self) -> None:
self.app.center_container = self.menu_container
-
def exit_cli(self) -> None:
"""Exits
"""
self.app.exit(result=False)
-
def logout_exit_cli(self) -> None:
"""Removes auth token and exits
"""
@@ -73,8 +58,8 @@ async def coroutine():
asyncio.ensure_future(coroutine())
- del config_cli.config['DEFAULT']['access_token_enc']
- del config_cli.config['DEFAULT']['user_data']
+ config_cli.config['DEFAULT'].pop('access_token_enc', None)
+ config_cli.config['DEFAULT'].pop('user_data', None)
config_cli.write_config()
self.exit_cli()
diff --git a/jans-cli-tui/cli_tui/version.py b/jans-cli-tui/cli_tui/version.py
index 80381fb7ede..9fbd42fb085 100644
--- a/jans-cli-tui/cli_tui/version.py
+++ b/jans-cli-tui/cli_tui/version.py
@@ -3,4 +3,4 @@
https://www.apache.org/licenses/LICENSE-2.0
"""
-__version__ = "1.0.1-dev"
+__version__ = "1.0.5-dev"
diff --git a/jans-cli-tui/setup.py b/jans-cli-tui/setup.py
index a4a97efce48..75cbe389539 100644
--- a/jans-cli-tui/setup.py
+++ b/jans-cli-tui/setup.py
@@ -62,7 +62,7 @@ def find_version(*file_paths):
"ruamel.yaml>=0.16.5",
"PyJWT==2.3.0",
"pygments",
- "prompt_toolkit",
+ "prompt_toolkit==3.0.33",
"requests",
"urllib3",
"pyDes",
diff --git a/jans-cli/.gitignore b/jans-cli/.gitignore
deleted file mode 100644
index 14c925f58d3..00000000000
--- a/jans-cli/.gitignore
+++ /dev/null
@@ -1,134 +0,0 @@
-swagger_yaml.json
-config.ini
-
-# 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/
-pip-wheel-metadata/
-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/
-
-# 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
-target/
-
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# IPython
-profile_default/
-ipython_config.py
-
-# pyenv
-.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/
-.idea/
-.idea
diff --git a/jans-cli/LICENSE b/jans-cli/LICENSE
deleted file mode 100644
index 261eeb9e9f8..00000000000
--- a/jans-cli/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 [yyyy] [name of copyright owner]
-
- 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.
diff --git a/jans-cli/Makefile b/jans-cli/Makefile
deleted file mode 100644
index 5a916486caa..00000000000
--- a/jans-cli/Makefile
+++ /dev/null
@@ -1,13 +0,0 @@
-.DEFAULT_GOAL := develop
-
-develop:
- pip3 install -e .
-
-install:
- pip3 install .
-
-uninstall:
- pip3 uninstall jans-cli -y
-
-zipapp:
- shiv --compressed -o config-cli.pyz -p '/usr/bin/env python3' -e cli.config_cli:main . --no-cache
diff --git a/jans-cli/README.md b/jans-cli/README.md
deleted file mode 100644
index fb917ef7a23..00000000000
--- a/jans-cli/README.md
+++ /dev/null
@@ -1,173 +0,0 @@
-# _Janssen Command Line Interface_
-`jans-cli` is a **Command Line Interface** for Janssen Configuration. It also has `menu-driven` interface that makes it easier to understand how to use [Janssen Server](https://github.com/JanssenProject/home) through the Interactive Mode.
-
-Table of Contents
-=================
-
- * [Janssen Command Line Interface](#_janssen-command-line-interface_)
- * [Installation](#_installation_)
- * [Quick Start](#_quick-start_)
-
-# _Installation_
-
-You can directly download the `jans-cli` package file as below:
-
-### For macOs:
-
-```
-wget https://github.com/JanssenProject/jans-cli/releases/latest/download/jans-cli-macos-amd64.pyz
-```
-
-### for linux:
-
-```
-wget https://github.com/JanssenProject/jans-cli/releases/latest/download/jans-cli-linux-amd64.pyz
-```
-
-## Build `jans-cli.pyz` manually
-
-If you would like to build `jans-cli` manually, you can go through the following steps noted here:
-
-## Prerequisites
-1. wget
-2. unzip
-3. Python 3.6+.
-4. Python `pip3` package.
-
-### Building
-
-1. Install dependencies
-
- * On Ubuntu 20
- ```sh
- apt install -y wget unzip python3-pip python3-dev
- ```
- * On CentOS Stream 8
- ```sh
- yum install -y wget unzip python3-pip python3-devel make
- pip3 install --upgrade pip
- ```
-
- Install Shiv
-
- ```sh
- pip3 install shiv
- ```
-
-2. Download the repository:
-
- ```sh
- wget https://github.com/JanssenProject/jans/archive/refs/heads/main.zip
- ```
-
-3. Unzip package, and change to directory
-
- ```sh
- unzip main.zip
- cd jans-main/jans-cli
- ```
-
-4. Build
-
- ```sh
- make zipapp
- ```
-
- You can verify with the following command line if everything is done successfully.
-
- ```
- python3 config-cli.pyz -h
- ```
-
-5. Executing config-cli.pyz Remotely
- Login your Jans Server. Execute the following command to find **client-id** and **client-secret**:
- ```sh
- cat /opt/jans/jans-setup/setup.properties.last | grep "role_based_client"
- ```
- It will output like this:
- ```sh
- role_based_client_encoded_pw=+U3XiW2uM/rnidqZ2mv9sw\=\=
- role_based_client_id=2000.09b47f56-1b9e-4443-bebd-bdf970406a15
- role_based_client_pw=T68kLUz4YXnR
- ```
- **client-id** is the value of **role_based_client_id** and **client-secret** is the value of **role_based_client_pw**
- Thus we can execute CLI as:
-
- ```sh
- python3 config-cli.pyz --host demoexmple.gluu.org --client-id 2000.09b47f56-1b9e-4443-bebd-bdf970406a15 --client-secret T68kLUz4YXnR
- ```
-
-
-### Standard Python package
-1. Install venv module
- ```sh
- pip3 install virtualenv
- ```
-
-2. Create virtual environment and activate:
-
- ```sh
- python3 -m virtualenv .venv
- source .venv/bin/activate
- ```
-
-3. Download and install the package:
-
- ```
- wget https://github.com/JanssenProject/jans/archive/refs/heads/main.zip
- unzip main.zip
- cd jans-main/jans-cli
- make install
- ```
-
- This command will install executable called `jans-cli` available in virtual environment `PATH`.
-
-
-![](../docs/assets/image-build-jans-cli-pyz-manually-03042021.png)
-
-
-## Virtual Machine Setup
-
-**jans-cli** is automatically installed if you choose `jans-config-api` during [Janssen Server](https://github.com/JanssenProject/home/blob/main/development.md#install-janssen-into-vm) Installation on Virtual Machine.
-
-![](../docs/assets/image-jans-config-api-03042021.png)
-
-After successfully installed Janssen Server, you will get two command-line arguments as below:
-
-![](../docs/assets/image-installed-03042021.png)
-
-# _Quick Start_
-
-As you have seen, CLI supports both of the `config-cli` and `scim-cli`. For a quick start, let's run the following command.
-
-```
-/opt/jans/jans-cli/config-cli.py
-```
-If you get an error, you can try in this way:
-
-```
-python3 /opt/jans/jans-cli/config-cli.py
-```
-
-Alternatively, you can make python3 to default version:
-```
-sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 10
-/opt/jans/jans-cli/config-cli.py
-```
-
-You will get a menu as below image:
-
-![main-menu.png](../docs/assets/image-im-main-03042021.png)
-
-From the following list, you can choose any options by selecting its number. For example, let's say number 2,
-to get **Default Authentication Method**.
-
-That returns another two options as below:
-
-![option-2-option.png](../docs/assets/image-im-default-auth-02-03042021.png)
-
-Now by selecting 1 it returns our desired result as below image:
-
-![default-authentication-method.png](../docs/assets/image-im-cur-default-auth-03042021.png)
-
-So, That was a quick start to view how this _jans-cli_ Interactive Mode works. Please, follow this [link](https://github.com/JanssenProject/jans/blob/main/docs/admin/config-guide/jans-cli/index.md) to read the _jans-cli_ docs for a better understanding of the Janssen Command-Line.
diff --git a/jans-cli/cli/__init__.py b/jans-cli/cli/__init__.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/jans-cli/cli/config_cli.py b/jans-cli/cli/config_cli.py
deleted file mode 100755
index 33da70bd508..00000000000
--- a/jans-cli/cli/config_cli.py
+++ /dev/null
@@ -1,2401 +0,0 @@
-#!/usr/bin/env python3
-
-import sys
-import os
-import json
-import re
-import urllib3
-import configparser
-import readline
-import argparse
-import inspect
-import random
-import datetime
-import ruamel.yaml
-import importlib
-import code
-import traceback
-import ast
-import base64
-import pprint
-import copy
-
-from pathlib import Path
-from types import SimpleNamespace
-from urllib.parse import urlencode
-from collections import OrderedDict
-
-home_dir = Path.home()
-config_dir = home_dir.joinpath('.config')
-config_dir.mkdir(parents=True, exist_ok=True)
-config_ini_fn = config_dir.joinpath('jans-cli.ini')
-cur_dir = os.path.dirname(os.path.realpath(__file__))
-log_dir = os.environ.get('cli_log_dir', cur_dir)
-sys.path.append(cur_dir)
-
-from pylib.tabulate.tabulate import tabulate
-try:
- import jwt
-except ModuleNotFoundError:
- from pylib import jwt
-
-tabulate_endpoints = {
- 'jca.get-config-scripts': ['scriptType', 'name', 'enabled', 'inum'],
- 'jca.get-user': ['inum', 'userId', 'mail','sn', 'givenName', 'jansStatus'],
- 'jca.get-all-attribute': ['inum', 'name', 'displayName', 'status', 'dataType', 'claimName'],
- 'jca.get-oauth-openid-clients': ['inum', 'displayName', 'clientName', 'applicationType'],
- 'jca.get-oauth-scopes': ['dn', 'id', 'scopeType'],
- 'jca.get-oauth-uma-resources': ['dn', 'name', 'expirationDate'],
- 'scim.get-users': ['id', 'userName', 'displayName', 'active']
-}
-
-tabular_dataset = {'scim.get-users': 'Resources'}
-excluded_operations = {'scim': ['search-user'], 'jca':[]}
-
-my_op_mode = 'scim' if 'scim' in os.path.basename(sys.argv[0]) else 'jca'
-sys.path.append(os.path.join(cur_dir, my_op_mode))
-swagger_client = importlib.import_module(my_op_mode + '.swagger_client')
-swagger_client.models = importlib.import_module(my_op_mode + '.swagger_client.models')
-swagger_client.api = importlib.import_module(my_op_mode + '.swagger_client.api')
-swagger_client.rest = importlib.import_module(my_op_mode + '.swagger_client.rest')
-plugins = []
-
-warning_color = 214
-error_color = 196
-success_color = 10
-bold_color = 15
-grey_color = 242
-
-clear = lambda: os.system('clear')
-
-urllib3.disable_warnings()
-config = configparser.ConfigParser()
-
-host = os.environ.get('jans_host')
-client_id = os.environ.get(my_op_mode + 'jca_client_id')
-client_secret = os.environ.get(my_op_mode + 'jca_client_secret')
-access_token = None
-debug = os.environ.get('jans_client_debug')
-
-def encode_decode(s, decode=False):
- cmd = '/opt/jans/bin/encode.py '
- if decode:
- cmd += '-D '
- result = os.popen(cmd + s + ' 2>/dev/null').read()
- return result.strip()
-
-
-# dummy api class to reach private ApiClient methods
-class MyApiClient(swagger_client.ApiClient):
- pass
-
-
-class DummyPool:
- def close(self):
- pass
-
- def join(self):
- pass
-
-
-myapi = MyApiClient()
-myapi.pool = DummyPool()
-
-##################### arguments #####################
-op_list = []
-
-for api_name in dir(swagger_client.api):
- if api_name.endswith('Api') and inspect.isclass(getattr(swagger_client.api, api_name)):
- op_list.append(api_name[:-3])
-
-parser = argparse.ArgumentParser()
-parser.add_argument("--host", help="Hostname of server")
-parser.add_argument("--client-id", help="Jans Config Api Client ID")
-parser.add_argument("--client-secret", "--client_secret", help="Jans Config Api Client ID secret")
-parser.add_argument("--access-token", help="JWT access token or path to file containing JWT access token")
-parser.add_argument("--plugins", help="Available plugins separated by comma")
-parser.add_argument("-debug", help="Run in debug mode", action='store_true')
-parser.add_argument("--operation-id", help="Operation ID to be done")
-parser.add_argument("--url-suffix", help="Argument to be added api endpoint url. For example inum:2B29")
-parser.add_argument("--info", choices=op_list, help="Help for operation")
-parser.add_argument("--op-mode", choices=['get', 'post', 'put', 'patch', 'delete'], default='get',
- help="Operation mode to be done")
-parser.add_argument("--endpoint-args",
- help="Arguments to pass endpoint separated by comma. For example limit:5,status:INACTIVE")
-parser.add_argument("--schema", help="Get sample json schema")
-
-parser.add_argument("-CC", "--config-api-mtls-client-cert", help="Path to SSL Certificate file")
-parser.add_argument("-CK", "--config-api-mtls-client-key", help="Path to SSL Key file")
-parser.add_argument("--key-password", help="Password for SSL Key file")
-parser.add_argument("-noverify", help="Ignore verifying the SSL certificate", action='store_true', default=True)
-
-parser.add_argument("-use-test-client", help="Use test client without device authorization", action='store_true')
-
-
-parser.add_argument("--patch-add", help="Colon delimited key:value pair for add patch operation. For example loggingLevel:DEBUG")
-parser.add_argument("--patch-replace", help="Colon delimited key:value pair for replace patch operation. For example loggingLevel:DEBUG")
-parser.add_argument("--patch-remove", help="Key for remove patch operation. For example imgLocation")
-parser.add_argument("--no-suggestion", help="Do not use prompt toolkit to display word completer", action='store_true')
-parser.add_argument("-log-dir", help="Do not use prompt toolkit to display word completer", default=log_dir)
-
-
-# parser.add_argument("-show-data-type", help="Show data type in schema query", action='store_true')
-parser.add_argument("--data", help="Path to json data file")
-args = parser.parse_args()
-
-if args.config_api_mtls_client_cert and args.config_api_mtls_client_key:
- excluded_operations['jca'] += [
- 'get-user', 'post-user', 'put-user', 'get-user-by-inum', 'delete-user', 'patch-user-by-inum',
- 'get-properties-fido2', 'put-properties-fido2', 'get-registration-entries-fido2',
- ]
-
-if not args.no_suggestion:
- from prompt_toolkit import prompt, HTML
- from prompt_toolkit.completion import WordCompleter
-
-
-################## end of arguments #################
-
-test_client = args.use_test_client
-
-
-if args.plugins:
- for plugin in args.plugins.split(','):
- plugins.append(plugin.strip())
-
-def write_config():
- with open(config_ini_fn, 'w') as w:
- config.write(w)
-
-if not(host and (client_id and client_secret or access_token)):
- host = args.host
- client_id = args.client_id
- client_secret = args.client_secret
- debug = args.debug
-
- access_token = args.access_token
- if access_token and os.path.isfile(access_token):
- with open(access_token) as f:
- access_token = f.read()
-
-
-if not(host and (client_id and client_secret or access_token)):
-
- if config_ini_fn.exists():
- config.read_string(config_ini_fn.read_text())
- host = config['DEFAULT']['jans_host']
-
- if 'jca_test_client_id' in config['DEFAULT'] and test_client:
- client_id = config['DEFAULT']['jca_test_client_id']
- secret_key_str = 'jca_test_client_secret'
- else:
- client_id = config['DEFAULT']['jca_client_id']
- secret_key_str = 'jca_client_secret'
-
- secret_enc_key_str = secret_key_str + '_enc'
- if config['DEFAULT'].get(secret_key_str):
- client_secret = config['DEFAULT'][secret_key_str]
- elif config['DEFAULT'].get(secret_enc_key_str):
- client_secret_enc = config['DEFAULT'][secret_enc_key_str]
- client_secret = encode_decode(client_secret_enc, decode=True)
-
- debug = config['DEFAULT'].get('debug')
- log_dir = config['DEFAULT'].get('log_dir', log_dir)
-
- else:
- config['DEFAULT'] = {'jans_host': 'jans server hostname,e.g, jans.foo.net',
- 'jca_client_id': 'your jans config api client id',
- 'jca_client_secret': 'client secret for your jans config api client',
- 'scim_client_id': 'your jans scim client id',
- 'scim_client_secret': 'client secret for your jans scim client'}
-
- write_config()
-
- print(
- "Pelase fill {} or set environmental variables jans_host, jans_client_id ,and jans_client_secret and re-run".format(config_ini_fn)
- )
- sys.exit()
-
-
-def get_bool(val):
- if str(val).lower() in ('yes', 'true', '1', 'on'):
- return True
- return False
-
-
-debug = get_bool(debug)
-
-
-class Menu(object):
-
- def __init__(self, name, method='', info={}, path=''):
- self.name = name
- self.display_name = name
- self.method = method
- self.info = info
- self.path = path
- self.children = []
- self.parent = None
- self.ignore = False
-
- def __iter__(self):
- self.current_index = 0
- return self
-
- def __repr__(self):
- return self.display_name
- self.__print_child(self)
-
- def tree(self):
- print(self.name)
- self.__print_child(self)
-
- def __get_parent_number(self, child):
- n = 0
- while True:
- if not child.parent:
- break
- n += 1
- child = child.parent
-
- return n
-
- def __print_child(self, menu):
- if menu.children:
- for child in menu.children:
- print(' ' * self.__get_parent_number(child) * 2, child)
- self.__print_child(child)
-
- def add_child(self, node):
- assert isinstance(node, Menu)
- node.parent = self
- self.children.append(node)
-
- def get_child(self, n):
- if len(self.children) > n:
- return self.children[n]
-
- def __next__(self):
- if self.current_index < len(self.children):
- retVal = self.children[self.current_index]
- self.current_index += 1
- return retVal
-
- else:
- raise StopIteration
-
- def __contains__(self, child):
- for child_ in self.children:
- if child_.name == child:
- return True
- return False
-
-
-class JCA_CLI:
-
- def __init__(self, host, client_id, client_secret, access_token, test_client=False):
- self.host = host
- self.client_id = client_id
- self.client_secret = client_secret
- self.use_test_client = test_client
-
- self.swagger_configuration = swagger_client.Configuration()
- self.swagger_configuration.host = 'https://{}'.format(self.host)
- self.access_token = access_token or config['DEFAULT'].get('access_token')
-
- self.set_user()
- self.plugins()
-
- if not self.access_token and config['DEFAULT'].get('access_token_enc'):
- self.access_token = encode_decode(config['DEFAULT']['access_token_enc'], decode=True)
-
- if my_op_mode == 'scim':
- self.swagger_configuration.host += '/jans-scim/restv1/v2'
-
- self.ssl_settings()
-
- self.swagger_configuration.debug = debug
- if self.swagger_configuration.debug:
- self.swagger_configuration.logger_file = os.path.join(log_dir, 'swagger.log')
-
- self.swagger_yaml_fn = os.path.join(cur_dir, my_op_mode + '.yaml')
-
- self.cfg_yml = self.get_yaml()
- self.make_menu()
- self.current_menu = self.menu
- self.enums()
-
- def enums(self):
- self.enum_dict = {
- "CustomAttribute": {
- "properties.name": {
- "f": "get_attrib_list"
- }
- }
- }
-
- def set_user(self):
- self.auth_username = None
- self.auth_password = None
- self.askuser = get_bool(config['DEFAULT'].get('askuser'))
-
- if self.askuser:
- if args.username:
- self.auth_username = args.username
- if args.password:
- self.auth_password = args.password
- elif args.j:
- if os.path.isfile(args.j):
- with open(args.j) as reader:
- self.auth_password = reader.read()
- else:
- print(args.j, "does not exist. Exiting ...")
- sys.exit()
- if not (self.auth_username and self.auth_password):
- print("I need username and password. Exiting ...")
- sys.exit()
-
- def plugins(self):
- for plugin_s in config['DEFAULT'].get(my_op_mode + '_plugins', '').split(','):
- plugin = plugin_s.strip()
- if plugin:
- plugins.append(plugin)
-
- def ssl_settings(self):
- if args.noverify:
- self.swagger_configuration.verify_ssl = False
- else:
- self.swagger_configuration.verify_ssl = True
-
- if args.config_api_mtls_client_cert:
- self.swagger_configuration.cert_file = args.config_api_mtls_client_cert
-
- if args.config_api_mtls_client_key:
- self.swagger_configuration.key_file = args.config_api_mtls_client_key
-
- def drop_to_shell(self, mylocals):
- locals_ = locals()
- locals_.update(mylocals)
- code.interact(local=locals_)
- sys.exit()
-
- def get_yaml(self):
- debug_json = 'swagger_yaml.json'
- if os.path.exists(debug_json):
- with open(debug_json) as f:
- return json.load(f, object_pairs_hook=OrderedDict)
-
- with open(self.swagger_yaml_fn) as f:
- self.cfg_yml = ruamel.yaml.load(f.read().replace('\t', ''), ruamel.yaml.RoundTripLoader)
- if os.environ.get('dump_yaml'):
- with open(debug_json, 'w') as w:
- json.dump(self.cfg_yml, w, indent=2)
- return self.cfg_yml
-
- def get_rest_client(self):
- rest = swagger_client.rest.RESTClientObject(self.swagger_configuration)
- if args.key_password:
- rest.pool_manager.connection_pool_kw['key_password'] = args.key_password
- return rest
-
- def check_connection(self):
- rest = self.get_rest_client()
- headers = urllib3.make_headers(basic_auth='{}:{}'.format(self.client_id, self.client_secret))
- url = 'https://{}/jans-auth/restv1/token'.format(self.host)
- headers['Content-Type'] = 'application/x-www-form-urlencoded'
-
- response = rest.POST(
- url,
- headers=headers,
- post_params={"grant_type": "client_credentials"}
- )
-
- if response.status != 200:
- raise ValueError(
- self.colored_text("Unable to connect jans-auth server: {}".format(response.reason), error_color))
-
-
- def check_access_token(self):
- if not self.access_token :
- return False
-
- try:
- jwt.decode(self.access_token,
- options={
- 'verify_signature': False,
- 'verify_exp': True,
- 'verify_aud': False
- }
- )
- return True
- except Exception as e:
- print(self.colored_text("Unable to validate access token: {}".format(e), error_color))
- self.access_token = None
-
- return False
-
-
- def guess_param_mapping(self, param_s):
- word_list = re.sub( r"([A-Z])", r" \1", param_s).split()
- word_list = [w.lower() for w in word_list]
- param_name = '_'.join(word_list)
- return param_name
-
-
- def make_menu(self):
-
- menu_groups = []
-
- def get_sep_pos(s):
- for i, c in enumerate(s):
- if c in ('-', '–'):
- return i
- return -1
-
- def get_group_obj(mname):
- for grp in menu_groups:
- if grp.mname == mname:
- return grp
-
-
- for tag in self.cfg_yml['tags']:
- tname = tag['name'].strip()
- if tname == 'developers':
- continue
- n = get_sep_pos(tname)
- mname = tname[:n].strip() if n > -1 else tname
- grp = get_group_obj(mname)
- if not grp:
- grp = SimpleNamespace()
- grp.tag = None if n > -1 else tname
- grp.mname = mname
- grp.submenu = []
- menu_groups.append(grp)
-
- if n > -1:
- sname = tname[n+1:].strip()
- sub = SimpleNamespace()
- sub.tag = tname
- sub.mname = sname
- grp.submenu.append(sub)
-
-
- def get_methods_of_tag(tag):
- methods = []
- if tag:
- for path_name in self.cfg_yml['paths']:
- path = self.cfg_yml['paths'][path_name]
- path_parameters = []
- if 'parameters' in path:
- for pparam in path['parameters']:
- if pparam.get('in') == 'path':
- path_parameters.append(dict(pparam))
-
- for method_name in path:
- method = path[method_name]
-
-
-
- if hasattr(method, 'get') and method.get('operationId') in excluded_operations[my_op_mode]:
- continue
- if 'tags' in method and tag in method['tags'] and 'operationId' in method:
- if method.get('x-cli-plugin') and method['x-cli-plugin'] not in plugins:
- continue
- if path_parameters:
- method['__path_parameters__'] = path_parameters
- method['__method_name__'] = method_name
- method['__path_name__'] = path_name
- methods.append(method)
-
- return methods
-
- menu = Menu('Main Menu')
-
- for grp in menu_groups:
- methods = get_methods_of_tag(grp.tag)
- m = Menu(name=grp.mname)
- m.display_name = m.name + ' ˅'
- menu.add_child(m)
-
- for method in methods:
- for tag in method['tags']:
- menu_name = method.get('summary') or method.get('description')
- sm = Menu(
- name=menu_name.strip('.'),
- method=method['__method_name__'],
- info=method,
- path=method['__path_name__'],
- )
- m.add_child(sm)
-
- if grp.submenu:
- m.display_name = m.name + ' ˅'
- for sub in grp.submenu:
- methods = get_methods_of_tag(sub.tag)
- if not methods:
- continue
- smenu = Menu(name=sub.mname)
- smenu.display_name = smenu.name + ' ˅'
- m.add_child(smenu)
-
- for method in methods:
- for tag in method['tags']:
-
- sub_menu_name = method.get('summary') or method.get('description')
- ssm = Menu(
- name=sub_menu_name.strip('.'),
- method=method['__method_name__'],
- info=method,
- path=method['__path_name__'],
- )
- smenu.add_child(ssm)
-
- self.menu = menu
-
-
- def get_json_from_response(self, response):
- js_data = {}
- data = response.data
- if data:
- try:
- js_data = json.loads(data.decode())
- except:
- pass
- return js_data
-
- def get_scoped_access_token(self, scope):
- sys.stderr.write("Getting access token for scope {}\n".format(scope))
- rest = self.get_rest_client()
- headers = urllib3.make_headers(basic_auth='{}:{}'.format(self.client_id, self.client_secret))
- url = 'https://{}/jans-auth/restv1/token'.format(self.host)
- headers['Content-Type'] = 'application/x-www-form-urlencoded'
- if self.askuser:
- post_params = {"grant_type": "password", "scope": scope, "username": self.auth_username,
- "password": self.auth_password}
- else:
- post_params = {"grant_type": "client_credentials", "scope": scope}
-
- response = rest.POST(
- url,
- headers=headers,
- post_params=post_params
- )
-
- try:
- data = json.loads(response.data)
- if 'access_token' in data:
- self.swagger_configuration.access_token = data['access_token']
- else:
- sys.stderr.write("Error while getting access token")
- sys.stderr.write(data)
- sys.stderr.write('\n')
- except Exception as e:
- print("Error while getting access token")
- sys.stderr.write(response.data)
- sys.stderr.write(e)
- sys.stderr.write('\n')
-
-
- def get_jwt_access_token(self):
-
- rest = self.get_rest_client()
-
- """
- STEP 1: Get device verification code
- This fucntion requests user code from jans-auth, print result and
- waits untill verification done.
- """
-
- headers_basic_auth = urllib3.make_headers(basic_auth='{}:{}'.format(self.client_id, self.client_secret))
- headers_basic_auth['Content-Type'] = 'application/x-www-form-urlencoded'
- response = rest.POST(
- 'https://{}/jans-auth/restv1/device_authorization'.format(host),
- headers=headers_basic_auth,
- post_params={
- 'client_id': self.client_id,
- 'scope': 'openid+profile+email+offline_access'
- }
- )
-
- if response.status != 200:
- raise ValueError(
- self.colored_text("Unable to get device authorization user code: {}".format(response.reason), error_color))
-
- result = self.get_json_from_response(response.urllib3_response)
-
- if 'verification_uri' in result and 'user_code' in result:
-
- print("Please visit verification url {} and enter user code {} in {} secods".format(
- self.colored_text(result['verification_uri'], success_color),
- self.colored_text(result['user_code'], bold_color),
- result['expires_in']
- )
- )
-
- input(self.colored_text("Please press «Enter» when ready", warning_color))
-
- else:
- raise ValueError(self.colored_text("Unable to get device authorization user code"))
-
- """
- STEP 2: Get access token for retreiving user info
- After device code was verified, we use it to retreive refresh token
- """
- response = rest.POST(
- 'https://{}/jans-auth/restv1/token'.format(host),
- headers=headers_basic_auth,
- post_params=[
- ('client_id',self.client_id),
- ('scope','openid+profile+email+offline_access'),
- ('grant_type', 'urn:ietf:params:oauth:grant-type:device_code'),
- ('grant_type', 'refresh_token'),
- ('device_code',result['device_code'])
- ]
- )
-
- if response.status != 200:
- raise ValueError(
- self.colored_text("Unable to get access token"))
-
- result = self.get_json_from_response(response.urllib3_response)
-
-
- """
- STEP 3: Get user info
- refresh token is used for retreiving user information to identify user roles
- """
- headers_bearer = urllib3.make_headers()
- headers_bearer['Content-Type'] = 'application/x-www-form-urlencoded'
- headers_bearer['Authorization'] = 'Bearer {}'.format(result['access_token'])
- response = rest.POST(
- 'https://{}/jans-auth/restv1/userinfo'.format(host),
- headers=headers_bearer,
- post_params={
- 'access_token': result['access_token'],
- },
- )
-
- if response.status != 200:
- raise ValueError(
- self.colored_text("Unable to get access token"))
-
- result = response.urllib3_response.data.decode()
-
- """
- STEP 4: Get access token for config-api endpoints
- Use client creditentials to retreive access token for client endpoints.
- Since introception script will be executed, access token will have permissions with all scopes
- """
- response = rest.POST(
- 'https://{}/jans-auth/restv1/token'.format(host),
- headers=headers_basic_auth,
- post_params={
- 'grant_type': 'client_credentials',
- 'scope': 'openid',
- 'ujwt': result,
- },
- )
-
- if response.status != 200:
- raise ValueError(
- self.colored_text("Unable to get access token"))
-
- result = self.get_json_from_response(response.urllib3_response)
-
- self.access_token = result['access_token']
- access_token_enc = encode_decode(self.access_token)
- config['DEFAULT']['access_token_enc'] = access_token_enc
- write_config()
-
-
- def get_access_token(self, scope):
- if self.use_test_client:
- self.get_scoped_access_token(scope)
- else:
- if not self.check_access_token():
- self.get_jwt_access_token()
-
- if not self.use_test_client:
- self.swagger_configuration.access_token = self.access_token
-
- def print_exception(self, e):
- error_printed = False
- if hasattr(e, 'body'):
- try:
- jsdata = json.loads(e.body.decode())
- print(self.colored_text(e.body.decode(), error_color))
- error_printed = True
- except:
- pass
- if not error_printed:
- print(self.colored_text("Error retreiving data", warning_color))
- print('\u001b[38;5;196m')
- if hasattr(e, 'reason'):
- print(e.reason)
- if hasattr(e, 'body'):
- print(e.body)
- if hasattr(e, 'args'):
- print(', '.join(e.args))
- print('\u001b[0m')
-
- def colored_text(self, text, color=255):
- return u"\u001b[38;5;{}m{}\u001b[0m".format(color, text)
-
-
- def guess_bool(self, val):
- if val == '_false':
- return False
- if val == '_true':
- return True
-
-
- def check_type(self, val, vtype):
- if vtype == 'string' and val:
- return str(val)
- elif vtype == 'integer':
- if isinstance(val, int):
- return val
- if val.isnumeric():
- return int(val)
- elif vtype == 'object':
- try:
- retVal = json.loads(val)
- if isinstance(retVal, dict):
- return retVal
- except:
- pass
- elif vtype == 'boolean':
- guessed_val = self.guess_bool(val)
- if not guessed_val is None:
- return guessed_val
-
- error_text = "Please enter a(n) {} value".format(vtype)
- if vtype == 'boolean':
- error_text += ': _true, _false'
-
- raise TypeError(self.colored_text(error_text, warning_color))
-
- def get_input(self, values=[], text='Selection', default=None, itype=None,
- help_text=None, sitype=None, enforce='__true__',
- example=None, spacing=0
- ):
- if 'b' in values and 'q' in values and 'x' in values:
- greyed_help_list = [ ('b', 'back'), ('q', 'quit'), ('x', 'logout and quit') ]
- for k,v in (('w', 'write result'), ('y', 'yes'), ('n', 'no')):
- if k in values:
- greyed_help_list.insert(1, (k, v))
- grey_help_text = ', '.join(['{}: {}'.format(k,v) for k,v in greyed_help_list])
- print(self.colored_text(grey_help_text, grey_color))
- print()
- type_text = ''
- if itype:
- if itype == 'array':
- type_text = "Type: array of {} separated by _,".format(sitype)
- if values:
- type_text += ' Valid values: {}'.format(', '.join(values))
- elif itype == 'boolean':
- type_text = "Type: " + itype
- if default is None:
- default = False
- else:
- type_text = "Type: " + itype
- if values:
- type_text += ', Valid values: {}'.format(self.colored_text(', '.join(values), bold_color))
-
- if help_text:
- help_text = help_text.strip('.') + '. ' + type_text
- else:
- help_text = type_text
-
- if help_text:
- print(' ' * spacing, self.colored_text('«{}»'.format(help_text), 244), sep='')
-
- if example:
- join_char = '_,' if itype == 'array' else ', '
- if isinstance(example, list):
- example_str = join_char.join(example)
- else:
- example_str = str(example)
- if join_char == '_,':
- example_str = example_str.replace(' ', join_char)
-
- print(' ' * spacing, self.colored_text('Example: {}'.format(example_str), 244), sep='')
-
- if not default is None:
- default_text = str(default).lower() if itype == 'boolean' else str(default)
- text += ' [' + default_text + ']'
- if itype == 'integer':
- default = int(default)
-
- if not text.endswith('?'):
- text += ':'
-
- if itype == 'boolean' and not values:
- values = ['_true', '_false']
-
- while True:
-
- if args.no_suggestion:
- selection = input(' ' * spacing + self.colored_text(text, 20) + ' ')
- else:
- html_completer = WordCompleter(values)
- selection = prompt(HTML(' ' * spacing + text + ' '), completer=html_completer)
-
- selection = selection.strip()
-
- if selection == '_b':
- self.display_menu(self.current_menu)
- break
-
- if selection.startswith('_file '):
- fname = selection.split()[1]
- if os.path.isfile(fname):
- with open(fname) as f:
- selection = f.read().strip()
- else:
- print(self.colored_text("File {} does not exist".format(fname), warning_color))
- continue
-
- if itype == 'boolean' and not selection:
- return False
-
- if not selection and default:
- return default
-
- if enforce and not selection:
- continue
-
- if not enforce and not selection:
- if itype == 'array':
- return []
- return None
-
- if 'q' in values and selection == 'q':
- print("Quiting...")
- sys.exit()
-
- if 'x' in values and selection == 'x':
- print("Logging out...")
- if 'access_token_enc' in config['DEFAULT']:
- config['DEFAULT'].pop('access_token_enc')
- write_config()
- print("Quiting...")
- sys.exit()
- break
-
-
- if itype == 'object' and sitype:
- try:
- object_ = self.check_type(selection, itype)
- except Exception as e:
- print(' ' * spacing, e, sep='')
- continue
-
- data_ok = True
- for items in object_:
- try:
- self.check_type(object_[items], sitype)
- except Exception as e:
- print(' ' * spacing, e, sep='')
- data_ok = False
- if data_ok:
- return object_
- else:
- continue
-
- if itype == 'array' and default and not selection:
- return default
-
- if itype == 'array' and sitype:
- if selection == '_null':
- selection = []
- data_ok = True
- else:
- selection = selection.split('_,')
- for i, item in enumerate(selection):
- data_ok = True
- try:
- selection[i] = self.check_type(item.strip(), sitype)
- if selection[i] == '_null':
- selection[i] = None
- if values:
- if not selection[i] in values:
- data_ok = False
- print(' ' * spacing, self.colored_text(
- "Please enter array of {} separated by _,".format(', '.join(values)),
- warning_color), sep='')
- break
- except TypeError as e:
- print(' ' * spacing, e, sep='')
- data_ok = False
- if data_ok:
- break
- else:
- if not itype is None:
- try:
- selection = self.check_type(selection, itype)
- except TypeError as e:
- if enforce:
- print(' ' * spacing, e, sep='')
- continue
-
- if values:
- if selection in values:
- break
- elif itype == 'boolean':
- if isinstance(selection, bool):
- break
- else:
- continue
- else:
- print(' ' * spacing,
- self.colored_text('Please enter one of {}'.format(', '.join(values)), warning_color),
- sep='')
-
- if not values and not selection and not enforce:
- break
-
- if not values and selection:
- break
-
- if selection == '_null':
- selection = None
- elif selection == '_q':
- selection = 'q'
-
- return selection
-
- def print_underlined(self, text):
- print()
- print(text)
- print('-' * len(text.splitlines()[-1]))
-
- def print_colored_output(self, data):
- try:
- data_json = json.dumps(data, indent=2)
- except:
- data = ast.literal_eval(str(data))
- data_json = json.dumps(data, indent=2)
-
- print(self.colored_text(data_json, success_color))
-
- def pretty_print(self, data):
- pp = pprint.PrettyPrinter(indent=2)
- pp_string = pp.pformat(data)
- print(self.colored_text(pp_string, success_color))
-
- def get_url_param(self, url):
- if url.endswith('}'):
- pname = re.findall('/\{(.*?)\}$', url)[0]
- return pname
-
- def get_endpiont_url_param(self, endpoint):
- param = {}
- pname = self.get_url_param(endpoint.path)
- if pname:
- param = {'name': pname, 'description': pname, 'schema': {'type': 'string'}}
-
- return param
-
- def make_swagger_var(self, varname):
- word_list = re.sub(r'([A-Z])', r' \1', varname).lower().split()
- return '_'.join(word_list)
-
- def obtain_parameters(self, endpoint, single=False):
- parameters = {}
- end_point_param = {}
- endpoint_parameters = []
- if 'parameters' in endpoint.info:
- endpoint_parameters = endpoint.info['parameters']
-
-
- if '__path_parameters__' in endpoint.info:
- end_point_param = endpoint.info['__path_parameters__'][0]
-
- if end_point_param and not end_point_param in endpoint_parameters:
- endpoint_parameters.insert(0, end_point_param)
-
- n = 1 if single else len(endpoint_parameters)
-
- for param in endpoint_parameters[0:n]:
- param_name = self.make_swagger_var(param['name'])
- if not param_name in parameters:
- text_ = param['name']
- help_text = param.get('description') or param.get('summary')
- enforce = True if param['schema']['type'] == 'integer' or (end_point_param and end_point_param['name'] == param['name']) else False
-
- parameters[param_name] = self.get_input(
- text=text_.strip('.'),
- itype=param['schema']['type'],
- default=param['schema'].get('default'),
- enforce=enforce,
- help_text=help_text,
- example=param.get('example'),
- values=param['schema'].get('enum', [])
- )
-
- return parameters
-
- def get_name_from_string(self, txt):
- return re.sub(r'[^0-9a-zA-Z\s]+', '', txt)
-
- def get_api_class_name(self, name):
- namle_list = self.get_name_from_string(name).split()
- for i, w in enumerate(namle_list[:]):
- if len(w) > 1:
- w = w[0].upper() + w[1:]
- else:
- w = w.upper()
-
- namle_list[i] = w
-
- return ''.join(namle_list) + 'Api'
-
- def get_path_by_id(self, operation_id):
- retVal = {}
- for path in self.cfg_yml['paths']:
- for method in self.cfg_yml['paths'][path]:
- if 'operationId' in self.cfg_yml['paths'][path][method] and self.cfg_yml['paths'][path][method]['operationId'] == operation_id:
- retVal = self.cfg_yml['paths'][path][method].copy()
- retVal['__path__'] = path
- retVal['__method__'] = method
- retVal['__urlsuffix__'] = self.get_url_param(path)
-
- return retVal
-
- def get_tag_from_api_name(self, api_name, qmethod=None):
-
- for tag in self.cfg_yml['tags']:
- api_class_name = self.get_api_class_name(tag['name'])
- if api_class_name == api_name:
- break
-
- paths = []
-
- for path in self.cfg_yml['paths']:
-
- for method in self.cfg_yml['paths'][path]:
-
- if 'tags' in self.cfg_yml['paths'][path][method] and tag['name'] in self.cfg_yml['paths'][path][method]['tags'] and 'operationId' in self.cfg_yml['paths'][path][method]:
- retVal = self.cfg_yml['paths'][path][method].copy()
- retVal['__path__'] = path
- retVal['__method__'] = method
- retVal['__urlsuffix__'] = self.get_url_param(path)
- if qmethod:
- if method == qmethod:
- paths.append(retVal)
- else:
- paths.append(retVal)
-
- return paths
-
- def get_scope_for_endpoint(self, endpoint):
- scope = []
- for security in endpoint.info.get('security', []):
- for stype in security:
- scope += security[stype]
-
- return ' '.join(scope)
-
- def unmap_model(self, model, data_dict=None):
- if data_dict is None:
- data_dict = {}
-
- for key_ in model.attribute_map:
-
- val = getattr(model, key_)
- if isinstance(val, datetime.date):
- val = str(val)
-
- if isinstance(val, list):
- sub_list = []
- for entry in val:
- if hasattr(entry, 'swagger_types'):
- sub_list_dict = {}
- self.unmap_model(entry, sub_list_dict)
- sub_list.append(sub_list_dict)
- else:
- sub_list.append(entry)
- data_dict[model.attribute_map[key_]] = sub_list
- elif hasattr(val, 'swagger_types'):
- sub_data_dict = {}
- self.unmap_model(val, sub_data_dict)
- data_dict[model.attribute_map[key_]] = sub_data_dict
- else:
- data_dict[model.attribute_map[key_]] = val
-
- return data_dict
-
- def get_model_key_map(self, model, key):
- key_underscore = key.replace('-', '_')
- for key_ in model.attribute_map:
- if model.attribute_map[key_] == key or model.attribute_map[key_] == key_underscore:
- return key_
-
- def tabular_data(self, data, ome):
- tab_data = []
- headers = tabulate_endpoints[ome]
- for i, entry in enumerate(data):
- row_ = [i + 1]
- for header in headers:
- row_.append(str(entry.get(header, '')))
- tab_data.append(row_)
-
- print(tabulate(tab_data, headers, tablefmt="grid"))
-
- def process_get(self, endpoint, return_value=False, parameters=None):
- clear()
- if not return_value:
- title = endpoint.name
- if endpoint.name != endpoint.info['description'].strip('.'):
- title += '\n' + endpoint.info['description']
-
- self.print_underlined(title)
-
- if not parameters:
- parameters = self.obtain_parameters(endpoint, single=return_value)
-
- for param in parameters.copy():
- if not parameters[param]:
- del parameters[param]
-
- if parameters and not return_value:
- print("Calling Api with parameters:", parameters)
-
- print("Please wait while retreiving data ...\n")
-
- api_caller = self.get_api_caller(endpoint)
-
- api_response = None
- raise_error = False
- try:
- api_response = api_caller(**parameters)
- except Exception as e:
- if return_value:
- raise_error = True
- else:
- self.print_exception(e)
-
- if raise_error:
- raise ValueError('Not found')
-
- if return_value:
- if api_response:
- return api_response
- return False
-
- selections = ['q', 'x', 'b']
- item_counters = []
- tabulated = False
-
- if api_response:
- try:
- if 'response' in api_response:
- api_response = api_response['response']
- except:
- pass
-
- selections.append('w')
- api_response_unmapped = []
- if isinstance(api_response, list):
- for model in api_response:
- data_dict = self.unmap_model(model)
- api_response_unmapped.append(data_dict)
- elif isinstance(api_response, dict):
- api_response_unmapped = api_response
- else:
- data_dict = self.unmap_model(api_response)
- api_response_unmapped = data_dict
-
- op_mode_endpoint = my_op_mode + '.' + endpoint.info['operationId']
-
- if op_mode_endpoint in tabulate_endpoints:
- api_response_unmapped_ext = copy.deepcopy(api_response_unmapped)
- if 'entries' in api_response_unmapped_ext:
- api_response_unmapped_ext = api_response_unmapped_ext['entries']
-
- if endpoint.info['operationId'] == 'get-user':
- for entry in api_response_unmapped_ext:
- if entry.get('customAttributes'):
- for attrib in entry['customAttributes']:
- if attrib['name'] == 'mail':
- entry['mail'] = ', '.join(attrib['values'])
- elif attrib['name'] in tabulate_endpoints[op_mode_endpoint]:
- entry[attrib['name']] = attrib['values'][0]
-
- if endpoint.info['operationId'] == 'get-oauth-openid-clients':
- for entry in api_response_unmapped_ext:
- for custom_attrib in entry.get('customAttributes', []):
- if custom_attrib.get('name') == 'displayName':
- entry['displayName'] = custom_attrib.get('value') or custom_attrib.get('values',['?'])[0]
- break
- if isinstance(entry['clientName'], dict) and 'value' in entry['clientName']:
- entry['clientName'] = entry['clientName']['value']
-
- tab_data = api_response_unmapped_ext
- if op_mode_endpoint in tabular_dataset:
- tab_data = api_response_unmapped_ext[tabular_dataset[op_mode_endpoint]]
- self.tabular_data(tab_data, op_mode_endpoint)
- item_counters = [str(i + 1) for i in range(len(tab_data))]
- tabulated = True
- else:
- self.print_colored_output(api_response_unmapped)
-
- selections += item_counters
- while True:
- selection = self.get_input(selections)
- if selection == 'b':
- self.display_menu(endpoint.parent)
- break
- elif selection == 'w':
- fn = input('File name: ')
- try:
- with open(fn, 'w') as w:
- json.dump(api_response_unmapped, w, indent=2)
- print("Output was written to", fn)
- except Exception as e:
- print("An error ocurred while saving data")
- self.print_exception(e)
- elif selection in item_counters:
- if my_op_mode == 'scim' and 'Resources' in api_response_unmapped:
- items = api_response_unmapped['Resources']
- elif my_op_mode == 'jca' and 'entries' in api_response_unmapped:
- items = api_response_unmapped['entries']
- self.pretty_print(items[int(selection) - 1])
-
- def get_schema_from_reference(self, ref):
- schema_path_list = ref.strip('/#').split('/')
- schema = self.cfg_yml[schema_path_list[0]]
-
- schema_ = schema.copy()
-
- for p in schema_path_list[1:]:
- schema_ = schema_[p]
-
- if 'allOf' in schema_:
- all_schema = OrderedDict()
- all_schema['required'] = []
-
- all_schema['properties'] = OrderedDict()
- for sch in schema_['allOf']:
- if '$ref' in sch:
- all_schema.update(self.get_schema_from_reference(sch['$ref']))
- elif 'properties' in sch:
- for sprop in sch['properties']:
- all_schema['properties'][sprop] = sch['properties'][sprop]
- all_schema['required'] += sch.get('required', [])
-
- schema_ = all_schema
-
- for key_ in schema_.get('properties', []):
- if '$ref' in schema_['properties'][key_]:
- schema_['properties'][key_] = self.get_schema_from_reference(schema_['properties'][key_]['$ref'])
- elif schema_['properties'][key_].get('type') == 'array' and '$ref' in schema_['properties'][key_]['items']:
- ref_path = schema_['properties'][key_]['items'].pop('$ref')
- ref_schema = self.get_schema_from_reference(ref_path)
- schema_['properties'][key_]['properties'] = ref_schema['properties']
- schema_['properties'][key_]['title'] = ref_schema['title']
- schema_['properties'][key_]['description'] = ref_schema.get('description', '')
- schema_['properties'][key_]['__schema_name__'] = ref_schema['__schema_name__']
-
- if not 'title' in schema_:
- schema_['title'] = p
-
- schema_['__schema_name__'] = p
-
- return schema_
-
- def get_scheme_for_endpoint(self, endpoint):
- schema_ = {}
- for content_type in endpoint.info.get('requestBody', {}).get('content', {}):
- if 'schema' in endpoint.info['requestBody']['content'][content_type]:
- schema = endpoint.info['requestBody']['content'][content_type]['schema']
- break
- else:
- return schema_
-
- schema_ = schema.copy()
-
- if schema_.get('type') == 'array':
- schma_ref = schema_.get('items', {}).pop('$ref')
- else:
- schma_ref = schema_.pop('$ref')
-
- if schma_ref:
- schema_ref_ = self.get_schema_from_reference(schma_ref)
- schema_.update(schema_ref_)
-
- return schema_
-
- def get_swagger_types(self, model, name):
- for attribute in model.swagger_types:
- if model.swagger_types[attribute] == name:
- return attribute
-
- def get_attrib_list(self):
- for parent in self.menu:
- for children in parent:
- if children.info.get('operationId') == 'get-attributes':
- attributes = self.process_get(children, return_value=True, parameters={'limit': 1000} )
- attrib_names = []
- for a in attributes:
- attrib_names.append(a.name)
- attrib_names.sort()
- return attrib_names
-
- def get_enum(self, schema):
- if schema['__schema_name__'] in self.enum_dict:
- enum_obj = schema
-
- for path in self.enum_dict[schema['__schema_name__']].copy():
- for p in path.split('.'):
- enum_obj = enum_obj[p]
-
- if not 'enum' in self.enum_dict[schema['__schema_name__']][path]:
- self.enum_dict[schema['__schema_name__']][path]['enum'] = getattr(self, self.enum_dict[schema['__schema_name__']][path]['f'])()
-
- enum_obj['enum'] = self.enum_dict[schema['__schema_name__']][path]['enum']
-
-
- def get_input_for_schema_(self, schema, model, spacing=0, initialised=False, getitem=None, required_only=False):
-
- self.get_enum(schema)
- data = {}
- for prop in schema['properties']:
- item = schema['properties'][prop]
- if getitem and prop != getitem['__name__'] or prop in ('dn', 'inum'):
- continue
-
- if required_only and not prop in schema.get('required', []):
- continue
-
- prop_ = self.get_model_key_map(model, prop)
- if item['type'] == 'object' and 'properties' in item:
- print()
- print("Data for object {}. {}".format(prop, item.get('description', '')))
-
- model_name_str = item.get('__schema_name__') or item.get('title') or item.get('description')
- model_name = self.get_name_from_string(model_name_str)
-
- if initialised and getattr(model, prop_):
- sub_model = getattr(model, prop_)
- self.get_input_for_schema_(item, sub_model, spacing=3, initialised=initialised)
- elif isinstance(model, type) and hasattr(swagger_client.models, model_name):
- sub_model_class = getattr(swagger_client.models, model_name)
- result = self.get_input_for_schema_(item, sub_model_class, spacing=3, initialised=initialised)
- setattr(model, prop_, result)
- elif hasattr(swagger_client.models, model.swagger_types[prop_]):
- sub_model = getattr(swagger_client.models, model.swagger_types[prop_])
- result = self.get_input_for_schema_(item, sub_model, spacing=3, initialised=initialised)
- setattr(model, prop_, result)
- else:
- sub_model = getattr(model, prop_)
- self.get_input_for_schema_(item, sub_model, spacing=3, initialised=initialised)
- # print(self.colored_text("Fix me: can't find model", error_color))
-
- elif item['type'] == 'array' and '__schema_name__' in item:
- model_name = item['__schema_name__']
- sub_model_class = getattr(swagger_client.models, model_name)
- sub_model_list = []
- sub_model_list_help_text = ''
- sub_model_list_title_text = item.get('title')
- if sub_model_list_title_text:
- sub_model_list_help_text = item.get('description')
- else:
- sub_model_list_title_text = item.get('description')
-
- cur_model_data = getattr(model, prop_)
-
- if cur_model_data and initialised:
- for cur_data in cur_model_data:
- print("\nUpdate {}".format(sub_model_list_title_text))
- cur_model_data = self.get_input_for_schema_(item, cur_data, spacing=spacing + 3)
- sub_model_list.append(cur_model_data)
-
- sub_model_list_selection = self.get_input(text="Add {}?".format(sub_model_list_title_text),
- values=['y', 'n'], help_text=sub_model_list_help_text)
-
- if sub_model_list_selection == 'y':
- while True:
- sub_model_list_data = self.get_input_for_schema_(item, sub_model_class, spacing=spacing + 3)
- sub_model_list.append(sub_model_list_data)
- sub_model_list_selection = self.get_input(
- text="Add another {}?".format(sub_model_list_title_text), values=['y', 'n'])
- if sub_model_list_selection == 'n':
- break
-
- data[prop_] = sub_model_list
-
- else:
- default = getattr(model, prop_)
- if isinstance(default, property):
- default = None
- enforce = True if item['type'] == 'boolean' else False
-
- if prop in schema.get('required', []):
- enforce = True
-
- if not default:
- default = item.get('default')
-
- values_ = item.get('enum', [])
- if not values_ and item['type'] == 'array' and 'enum' in item['items']:
- values_ = item['items']['enum']
- if item['type'] == 'object' and not default:
- default = {}
-
- if not values_:
- values_ = []
-
- val = self.get_input(
- values=values_,
- text=prop,
- default=default,
- itype=item['type'],
- help_text=item.get('description'),
- sitype=item.get('items', {}).get('type'),
- enforce=enforce,
- example=item.get('example'),
- spacing=spacing
- )
- data[prop_] = val
-
- if model.__class__.__name__ == 'type':
- modelObject = model(**data)
- for key_ in data:
- if data[key_] and not getattr(modelObject, key_, None):
- setattr(modelObject, key_, data[key_])
- return modelObject
- else:
- for key_ in data:
- setattr(model, key_, data[key_])
-
- return model
-
- def get_api_caller(self, endpoint):
- security = self.get_scope_for_endpoint(endpoint)
- if security.strip():
- self.get_access_token(security)
-
- client = getattr(swagger_client, self.get_api_class_name(endpoint.info['tags'][0]))
- api_instance = self.get_api_instance(client)
- api_caller = getattr(api_instance, endpoint.info['operationId'].replace('-', '_'))
-
- return api_caller
-
- def process_post(self, endpoint):
- schema = self.get_scheme_for_endpoint(endpoint)
-
- if schema:
-
- title = schema.get('description') or schema['title']
- data_dict = {}
-
- model_class = getattr(swagger_client.models, schema['__schema_name__'])
-
- if my_op_mode == 'scim':
- if endpoint.path == '/jans-scim/restv1/v2/Groups':
- schema['properties']['schemas']['default'] = ['urn:ietf:params:scim:schemas:core:2.0:Group']
- elif endpoint.path == '/jans-scim/restv1/v2/Users':
- schema['properties']['schemas']['default'] = ['urn:ietf:params:scim:schemas:core:2.0:User']
- if endpoint.info['operationId'] == 'create-user':
- schema['required'] = ['userName', 'name', 'displayName', 'emails', 'password']
-
- model = self.get_input_for_schema_(schema, model_class, required_only=True)
-
- optional_fields = []
- required_fields = schema.get('required', []) + ['dn', 'inum']
- for field in schema['properties']:
- if not field in required_fields:
- optional_fields.append(field)
-
- optional_fields.sort()
- if optional_fields:
- fill_optional = self.get_input(values=['y', 'n'], text='Populate optional fields?')
- fields_numbers = []
- if fill_optional == 'y':
- print("Optional Fields:")
- for i, field in enumerate(optional_fields):
- print(i + 1, field)
- fields_numbers.append(str(i + 1))
-
- while True:
- optional_selection = self.get_input(values=['q', 'x', 'c'] + fields_numbers,
- help_text="c: continue, #: populate field")
- if optional_selection == 'c':
- break
- if optional_selection in fields_numbers:
- item_name = optional_fields[int(optional_selection) - 1]
- schema_item = schema['properties'][item_name].copy()
- schema_item['__name__'] = item_name
- self.get_input_for_schema_(schema, model, initialised=True, getitem=schema_item)
-
- print("Obtained Data:\n")
- model_unmapped = self.unmap_model(model)
- self.print_colored_output(model_unmapped)
-
- selection = self.get_input(values=['q', 'x', 'b', 'y', 'n'], text='Continue?')
-
- else:
- selection = 'y'
- model = None
-
-
- path_vals = {}
- if '__path_parameters__' in endpoint.info:
- for pparam in endpoint.info['__path_parameters__']:
- swagger_var = self.make_swagger_var(pparam['name'])
- path_vals[swagger_var] = self.get_input(
- values=pparam['schema'].get('enum', []),
- text=pparam['name'],
- itype=pparam['schema']['type'],
- help_text= pparam.get('description'),
- enforce='__true__',
- )
-
-
- if selection == 'y':
- api_caller = self.get_api_caller(endpoint)
- print("Please wait while posting data ...\n")
-
- try:
- if model:
- if path_vals:
- api_response = api_caller(**path_vals, body=model)
- else:
- api_response = api_caller(body=model)
-
- else:
- api_response = api_caller(**path_vals)
-
- except Exception as e:
- api_response = None
- self.print_exception(e)
-
- if api_response:
- try:
- api_response_unmapped = self.unmap_model(api_response)
- self.print_colored_output(api_response_unmapped)
- except:
- print(self.colored_text(str(api_response), success_color))
-
- selection = self.get_input(values=['q', 'x', 'b'])
- if selection in ('b', 'n'):
- self.display_menu(endpoint.parent)
-
- def process_delete(self, endpoint):
- url_param = self.get_endpiont_url_param(endpoint)
- if url_param:
- url_param_val = self.get_input(text=url_param['name'], help_text='Entry to be deleted')
- else:
- url_param_val = ''
- selection = self.get_input(text="Are you sure want to delete {} ?".format(url_param_val),
- values=['b', 'y', 'n', 'q', 'x'])
- if selection in ('b', 'n'):
- self.display_menu(endpoint.parent)
- elif selection == 'y':
- api_caller = self.get_api_caller(endpoint)
- print("Please wait while deleting {} ...\n".format(url_param_val))
- api_response = '__result__'
-
- try:
- api_response = api_caller(url_param_val) if url_param_val else api_caller()
- except Exception as e:
- self.print_exception(e)
-
- if api_response is None:
- print(self.colored_text("\nEntry {} was deleted successfully\n".format(url_param_val), success_color))
-
- selection = self.get_input(['b', 'q', 'x'])
- if selection == 'b':
- self.display_menu(endpoint.parent)
-
- def process_patch(self, endpoint):
- if endpoint.info['operationId'] == 'patch-user-by-inum':
- schema = self.cfg_yml['components']['schemas']['CustomAttribute'].copy()
- schema['__schema_name__'] = 'CustomAttribute'
- model = getattr(swagger_client.models, 'CustomAttribute')
- elif 'PatchOperation' in self.cfg_yml['components']['schemas']:
- schema = self.cfg_yml['components']['schemas']['PatchOperation'].copy()
- model = getattr(swagger_client.models, 'PatchOperation')
- for item in schema['properties']:
- if not 'type' in schema['properties'][item]:
- schema['properties'][item]['type'] = 'string'
- schema['__schema_name__'] = 'PatchOperation'
- else:
- schema = self.cfg_yml['components']['schemas']['PatchRequest'].copy()
- schema['__schema_name__'] = 'PatchRequest'
- model = getattr(swagger_client.models, 'PatchRequest')
-
- parent_schema = {}
-
- schema_ref = endpoint.info.get('responses', {}).get('200', {}).get('content', {}).get('application/json', {}).get('schema', {}).get('$ref')
- if schema_ref:
- parent_schema = self.get_schema_from_reference(schema_ref)
-
- url_param_val = None
- url_param = self.get_endpiont_url_param(endpoint)
- if 'name' in url_param:
- url_param_val = self.get_input(text=url_param['name'], help_text='Entry to be patched')
- body = []
-
- if endpoint.info['operationId'] == 'patch-user-by-inum':
- patch_op = self.get_input(text="Patch operation", values=['add', 'remove', 'replace'], help_text='The operation to be performed')
-
- while True:
- data = self.get_input_for_schema_(schema, model)
- if endpoint.info['operationId'] != 'patch-user-by-inum':
- guessed_val = self.guess_bool(data.value)
- if not guessed_val is None:
- data.value = guessed_val
- if my_op_mode != 'scim' and not data.path.startswith('/'):
- data.path = '/' + data.path
-
- if my_op_mode == 'scim':
- data.path = data.path.replace('/', '.')
-
- if parent_schema and 'properties' in parent_schema:
- for prop_ in parent_schema['properties']:
- if data.path.lstrip('/') == prop_:
- if parent_schema['properties'][prop_]['type'] == 'array':
- data.value = data.value.split('_,')
- body.append(data)
- selection = self.get_input(text='Another patch operation?', values=['y', 'n'])
- if selection == 'n':
- break
-
- unmapped_body = []
- for item in body:
- unmapped_body.append(self.unmap_model(item))
-
- self.print_colored_output(unmapped_body)
-
- selection = self.get_input(values=['y', 'n'], text='Continue?')
-
- if selection == 'y':
-
- api_caller = self.get_api_caller(endpoint)
-
- print("Please wait patching...\n")
-
- if my_op_mode == 'scim':
- body = {'schemas': ['urn:ietf:params:scim:api:messages:2.0:PatchOp'], 'Operations': body}
- elif endpoint.info['operationId'] == 'patch-user-by-inum':
- patch_data = {'jsonPatchString': json.dumps([{'op': patch_op, 'path': '/dn', 'value': 'inum={},ou=people,o=jans'.format(url_param_val)}]), 'customAttributes':unmapped_body}
- body = patch_data
- try:
- if url_param_val:
- param_mapping = self.guess_param_mapping(url_param['name'])
- payload = {param_mapping: url_param_val, 'body': body}
- api_response = api_caller(**payload)
- else:
- api_response = api_caller(body=body)
- except Exception as e:
- api_response = None
- self.print_exception(e)
-
- if api_response:
- api_response_unmapped = self.unmap_model(api_response)
- self.print_colored_output(api_response_unmapped)
-
- selection = self.get_input(['b'])
- if selection == 'b':
- self.display_menu(endpoint.parent)
-
- def process_put(self, endpoint):
-
- schema = self.get_scheme_for_endpoint(endpoint)
-
- initialised = False
- cur_model = None
- go_back = False
- key_name = None
- parent_model = None
-
- if endpoint.info.get('x-cli-getdata') != '_file':
- if 'x-cli-getdata' in endpoint.info and endpoint.info['x-cli-getdata'] != None:
- for m in endpoint.parent:
- if m.info['operationId'] == endpoint.info['x-cli-getdata']:
- while True:
- try:
- cur_model = self.process_get(m, return_value=True)
- break
- except ValueError as e:
- print(self.colored_text("Server returned no data", error_color))
- retry = self.get_input(values=['y', 'n'], text='Retry?')
- if retry == 'n':
- self.display_menu(endpoint.parent)
- break
- initialised = True
- get_endpoint = m
- break
-
- else:
- if endpoint.info['operationId'] == 'put-properties-fido2':
- for m in endpoint.parent:
- if m.method == 'get':
- break
- cur_model = self.process_get(m, return_value=True)
- initialised = True
- get_endpoint = m
-
- else:
- for m in endpoint.parent:
- if m.method == 'get' and m.path.endswith('}'):
- while True:
- while True:
- try:
- key_name_desc = self.get_endpiont_url_param(m)
- if key_name_desc and 'name' in key_name_desc:
- key_name = key_name_desc['name']
- cur_model = self.process_get(m, return_value=True)
- break
- except ValueError as e:
- print(self.colored_text("Server returned no data", error_color))
- retry = self.get_input(values=['y', 'n'], text='Retry?')
- if retry == 'n':
- self.display_menu(endpoint.parent)
- break
-
- if not cur_model is False:
- break
-
- initialised = True
- get_endpoint = m
- break
-
- if not cur_model:
- for m in endpoint.parent:
- if m.method == 'get' and not m.path.endswith('}'):
- cur_model = self.process_get(m, return_value=True)
- get_endpoint = m
-
-
- if not cur_model:
- cur_model = getattr(swagger_client.models, schema['__schema_name__'])
-
- end_point_param = self.get_endpiont_url_param(endpoint)
-
-
- if cur_model:
-
- if endpoint.info.get('x-cli-getdata') == '_file':
-
- schema_desc = schema.get('description') or schema['__schema_name__']
- text = 'Enter filename to load data for «{}»: '.format(schema_desc)
- data_fn = input(self.colored_text(text, 244))
- if data_fn == 'b':
- go_back = True
- elif data_fn == 'q':
- sys.exit()
- else:
- data_org = self.get_json_from_file(data_fn)
-
- data = {}
- for k in data_org:
- if k in cur_model.attribute_map:
- mapped_key = cur_model.attribute_map[k]
- data[mapped_key] = data_org[k]
- else:
- data[k] = data_org[k]
-
- api_caller = self.get_api_caller(endpoint)
-
- print("Please wait while posting data ...\n")
-
- try:
- api_response = api_caller(body=data)
- except Exception as e:
- api_response = None
- self.print_exception(e)
-
- if api_response:
- api_response_unmapped = self.unmap_model(api_response)
- self.print_colored_output(api_response_unmapped)
-
- selection = self.get_input(values=['q', 'x', 'b'])
- if selection == 'b':
- self.display_menu(endpoint.parent)
-
-
- else:
-
- end_point_param_val = None
- if end_point_param:
- end_point_param_val = getattr(cur_model, end_point_param['name'], None) or self.get_model_key_map(cur_model, end_point_param['name'])
-
- attr_name_list = []
- for attr_name in cur_model.attribute_map:
- if attr_name != 'dn':
- attr_name_list.append(cur_model.attribute_map[attr_name])
-
- attr_name_list.sort()
- item_numbers = []
-
- def print_fields():
- print("Fields:")
- for i, attr_name in enumerate(attr_name_list):
- print(str(i + 1).rjust(2), attr_name)
- item_numbers.append(str(i + 1))
-
- print_fields()
- changed_items = []
- selection_list = ['q', 'x', 'b', 'v', 's', 'l'] + item_numbers
- help_text = 'q: quit, v: view, s: save, l: list fields #: update field'
-
- while True:
- selection = self.get_input(values=selection_list, help_text=help_text)
- if selection == 'v':
- self.pretty_print(self.unmap_model(cur_model))
- elif selection == 'l':
- print_fields()
- elif selection in item_numbers:
- item = attr_name_list[int(selection) - 1]
- item_unmapped = self.get_model_key_map(cur_model, item)
- if schema['properties'].get('keys', {}).get('properties'):
- schema = schema['properties']['keys']
-
- schema_item = schema['properties'][item]
- schema_item['__name__'] = item
- self.get_input_for_schema_(schema, cur_model, initialised=initialised, getitem=schema_item)
- changed_items.append(item)
-
- if selection == 'b':
- self.display_menu(endpoint.parent)
- break
- elif selection == 's':
- print('Changes:')
- for ci in changed_items:
- model_key = self.get_model_key_map(cur_model, ci)
- str_val = str(getattr(cur_model, model_key))
- print(self.colored_text(ci, bold_color) + ':', self.colored_text(str_val, success_color))
-
- selection = self.get_input(values=['y', 'n'], text='Continue?')
-
- if selection == 'y':
- schema_must = self.get_scheme_for_endpoint(endpoint)
- if schema_must['__schema_name__'] != cur_model.__class__.__name__:
- for e in endpoint.parent.children:
- if e.method == 'get':
- parent_model = self.process_get(e, return_value=True)
- break
-
-
- if parent_model and key_name and hasattr(parent_model, 'keys'):
- for i, wkey in enumerate(parent_model.keys):
- if getattr(wkey, key_name) == getattr(cur_model, key_name):
- parent_model.keys[i] = cur_model
- cur_model = parent_model
- break
-
- print("Please wait while posting data ...\n")
- api_caller = self.get_api_caller(endpoint)
- put_pname = self.get_url_param(endpoint.path)
-
- try:
- if put_pname:
- args_ = {'body': cur_model, put_pname: end_point_param_val}
- api_response = api_caller(**args_)
- else:
- api_response = api_caller(body=cur_model)
- except Exception as e:
- api_response = None
- self.print_exception(e)
-
- if api_response:
- api_response_unmapped = self.unmap_model(api_response)
- self.print_colored_output(api_response_unmapped)
- go_back = True
- break
-
- if go_back:
- selection = self.get_input(values=['q', 'x', 'b'])
- if selection == 'b':
- self.display_menu(endpoint.parent)
- else:
- self.get_input_for_schema_(schema, cur_model, initialised=initialised)
-
- def display_menu(self, menu):
- clear()
- self.current_menu = menu
-
- name_list = [menu.name]
- par = menu
- while True:
- par = par.parent
- if not par:
- break
- name_list.insert(0, par.name)
-
- if len(name_list) > 1:
- del name_list[0]
-
- self.print_underlined(': '.join(name_list))
-
- selection_values = ['q', 'x', 'b']
-
- menu_numbering = {}
-
- c = 0
- for i, item in enumerate(menu):
- if item.info.get('x-cli-ignore') or (item.parent.name == 'Main Menu' and not item.children):
- continue
-
- print(c + 1, item)
- selection_values.append(str(c + 1))
- menu_numbering[c + 1] = i
- c += 1
-
- selection = self.get_input(selection_values)
-
- if selection == 'b' and not menu.parent:
- print("Quiting...")
- sys.exit()
- elif selection == 'b':
- self.display_menu(menu.parent)
- elif int(selection) in menu_numbering and menu.get_child(menu_numbering[int(selection)]).children:
- self.display_menu(menu.get_child(menu_numbering[int(selection)]))
- else:
- m = menu.get_child(menu_numbering[int(selection)])
- getattr(self, 'process_' + m.method)(m)
-
- def parse_command_args(self, args):
- args_dict = {}
-
- if args:
- for arg in args.split(','):
- neq = arg.find(':')
- if neq > 1:
- arg_name = arg[:neq].strip()
- arg_val = arg[neq + 1:].strip()
- if arg_name and arg_val:
- args_dict[arg_name] = arg_val
-
- return args_dict
-
- def parse_args(self, args, path):
- param_names = []
- if not 'parameters' in path:
- return {}
- for param in path['parameters']:
- param_names.append(param['name'])
-
- args_dict = self.parse_command_args(args)
-
- for arg_name in args_dict:
- if not arg_name in param_names:
- self.exit_with_error("valid endpoint args are: {}".format(', '.join(param_names)))
-
- return args_dict
-
- def help_for(self, op_name):
- paths = self.get_tag_from_api_name(op_name + 'Api')
-
- schema_path = None
-
- for path in paths:
- if 'tags' in path:
- print('Operation ID:', path['operationId'])
- print(' Description:', path['description'])
- if path.get('__urlsuffix__'):
- print(' url-suffix:', path['__urlsuffix__'])
- if 'parameters' in path:
- param_names = []
- for param in path['parameters']:
- desc = param.get('description', 'No description is provided for this parameter')
- param_type = param.get('schema', {}).get('type')
- if param_type:
- desc += ' [{}]'.format(param_type)
- param_names.append((param['name'], desc))
- if param_names:
- print(' Parameters:')
- for param in param_names:
- print(' {}: {}'.format(param[0], param[1]))
-
- if 'requestBody' in path:
- for apptype in path['requestBody'].get('content', {}):
- if 'schema' in path['requestBody']['content'][apptype]:
- if path['requestBody']['content'][apptype]['schema'].get('type') == 'array':
- schema_path = path['requestBody']['content'][apptype]['schema']['items']['$ref']
- print(' Schema: Array of {}'.format(schema_path[1:]))
- else:
- spparent = path['requestBody']['content'][apptype]['schema']
- schema_path = spparent.get('$ref')
- if schema_path:
- print(' Schema: {}'.format(schema_path[1:]))
- else:
- print(' Data type: {}'.format(spparent.get('type')))
-
-
- if schema_path:
- print()
- print("To get sample schema type {0} --schema , for example {0} --schema {1}".format(sys.argv[0],
- schema_path[
- 1:]))
-
- def render_json_entry(self, val):
- if isinstance(val, str) and val.startswith('_file '):
- file_path = val[6:].strip()
- if os.path.exists(file_path):
- with open(file_path) as f:
- val = f.read()
- else:
- raise ValueError("File '{}' not found".format(file_path))
- return val
-
- def get_json_from_file(self, data_fn):
-
- if not os.path.exists(data_fn):
- self.exit_with_error("Can't find file {}".format(data_fn))
-
- try:
- with open(data_fn) as f:
- data = json.load(f)
- except:
- self.exit_with_error("Error parsing json file {}".format(data_fn))
-
- if isinstance(data, list):
- for entry in data:
- if isinstance(entry, dict):
- for k in entry:
- entry[k] = self.render_json_entry(entry[k])
-
- if isinstance(data, dict):
- for k in data:
- data[k] = self.render_json_entry(data[k])
-
- return data
-
- def get_api_instance(self, client):
- api_instance = client(swagger_client.ApiClient(self.swagger_configuration))
- if args.key_password:
- api_instance.api_client.rest_client.pool_manager.connection_pool_kw['key_password'] = args.key_password
- return api_instance
-
- def get_path_api_caller_for_path(self, path):
-
- dummy_enpoint = Menu(name='', info=path)
- security = self.get_scope_for_endpoint(dummy_enpoint)
- if security.strip():
- self.get_access_token(security)
- class_name = self.get_api_class_name(path['tags'][0])
- client = getattr(swagger_client, class_name)
- api_instance = self.get_api_instance(client)
- api_caller = getattr(api_instance, path['operationId'].replace('-', '_'))
-
- return api_caller
-
- def process_command_get(self, path, suffix_param, endpoint_params, data_fn, data=None):
- api_caller = self.get_path_api_caller_for_path(path)
- api_response = None
- encoded_param = urlencode(endpoint_params)
-
- if encoded_param:
- sys.stderr.write("Calling with params {}\n".format(encoded_param))
-
- try:
- if path.get('__urlsuffix__'):
- api_response = api_caller(suffix_param[path['__urlsuffix__']], **endpoint_params)
- else:
- api_response = api_caller(**endpoint_params)
- except Exception as e:
- if hasattr(e, 'reason'):
- sys.stderr.write(e.reason)
- if hasattr(e, 'body'):
- sys.stderr.write(e.body)
- sys.stderr.write('\n')
- sys.exit()
-
- api_response_unmapped = []
- if isinstance(api_response, list):
- for model in api_response:
- data_dict = self.unmap_model(model)
- api_response_unmapped.append(data_dict)
- else:
- data_dict = self.unmap_model(api_response)
- api_response_unmapped = data_dict
-
- print(json.dumps(api_response_unmapped, indent=2))
-
- def get_sub_model(self, field):
- sub_model_name_str = field.get('title') or field.get('description')
- sub_model_name = self.get_name_from_string(sub_model_name_str)
- if hasattr(swagger_client.models, sub_model_name):
- return getattr(swagger_client.models, sub_model_name)
-
- def exit_with_error(self, error_text):
- error_text += '\n'
- sys.stderr.write(self.colored_text(error_text, error_color))
- print()
- sys.exit()
-
- def process_command_post(self, path, suffix_param, endpoint_params, data_fn, data):
- api_caller = self.get_path_api_caller_for_path(path)
-
- endpoint = Menu(name='', info=path)
- schema = self.get_scheme_for_endpoint(endpoint)
- model_name = schema['__schema_name__']
- model = getattr(swagger_client.models, model_name)
-
- if not data:
-
- if data_fn.endswith('jwt'):
- with open(data_fn) as reader:
- data_org = jwt.decode(reader.read(),
- options={"verify_signature": False, "verify_exp": False, "verify_aud": False})
- else:
- try:
- data_org = self.get_json_from_file(data_fn)
- except ValueError as ve:
- self.exit_with_error(str(ve))
-
- data = {}
-
- for k in data_org:
- if k in model.attribute_map:
- mapped_key = model.attribute_map[k]
- data[mapped_key] = data_org[k]
- else:
- data[k] = data_org[k]
-
- try:
- body = myapi._ApiClient__deserialize_model(data, model)
- except Exception as e:
- self.exit_with_error(str(e))
-
- try:
- if suffix_param:
- api_response = api_caller(body=body, **suffix_param)
- else:
- api_response = api_caller(body=body)
- except Exception as e:
- self.print_exception(e)
- sys.exit()
-
- unmapped_response = self.unmap_model(api_response)
- sys.stderr.write("Server Response:\n")
- print(json.dumps(unmapped_response, indent=2))
-
- def process_command_put(self, path, suffix_param, endpoint_params, data_fn, data=None):
- self.process_command_post(path, suffix_param, endpoint_params, data_fn, data=None)
-
- def process_command_patch(self, path, suffix_param, endpoint_params, data_fn, data=None):
-
- if not data:
- try:
- data = self.get_json_from_file(data_fn)
- except ValueError as ve:
- self.exit_with_error(str(ve))
-
- if not isinstance(data, list):
- self.exit_with_error("{} must be array of /components/schemas/PatchRequest".format(data_fn))
-
- op_modes = ('add', 'remove', 'replace', 'move', 'copy', 'test')
-
- for item in data:
- if not item['op'] in op_modes:
- print("op must be one of {}".format(', '.join(op_modes)))
- sys.exit()
- if not item['path'].startswith('/'):
- item['path'] = '/' + item['path']
-
- api_caller = self.get_path_api_caller_for_path(path)
-
- try:
- if suffix_param:
- api_response = api_caller(suffix_param[path['__urlsuffix__']], body=data)
- else:
- api_response = api_caller(body=data)
- except Exception as e:
- self.print_exception(e)
- sys.exit()
-
- unmapped_response = self.unmap_model(api_response)
- sys.stderr.write("Server Response:\n")
- print(json.dumps(unmapped_response, indent=2))
-
- def process_command_delete(self, path, suffix_param, endpoint_params, data_fn, data=None):
-
- api_caller = self.get_path_api_caller_for_path(path)
- api_response = None
-
- try:
- api_response = api_caller(suffix_param[path['__urlsuffix__']], **endpoint_params)
- except Exception as e:
- self.print_exception(e)
- sys.exit()
-
- if api_response:
- unmapped_response = self.unmap_model(api_response)
- sys.stderr.write("Server Response:\n")
- print(json.dumps(unmapped_response, indent=2))
-
- def process_command_by_id(self, operation_id, url_suffix, endpoint_args, data_fn, data=None):
- path = self.get_path_by_id(operation_id)
-
- if not path:
- self.exit_with_error("No such Operation ID")
-
- suffix_param = self.parse_command_args(url_suffix)
- endpoint_params = self.parse_command_args(endpoint_args)
-
- if path.get('__urlsuffix__') and not path['__urlsuffix__'] in suffix_param:
- self.exit_with_error("This operation requires a value for url-suffix {}".format(path['__urlsuffix__']))
-
- endpoint = Menu('', info=path)
- schema = self.get_scheme_for_endpoint(endpoint)
-
- if not data:
- op_path = self.get_path_by_id(operation_id)
- if op_path['__method__'] == 'patch' and not data_fn:
- pop, pdata = '', ''
- if args.patch_add:
- pop = 'add'
- pdata = args.patch_add
- elif args.patch_remove:
- pop = 'remove'
- pdata = args.patch_remove
- elif args.patch_replace:
- pop = 'replace'
- pdata = args.patch_replace
-
- if pop:
- if pop != 'remove' and pdata.count(':') != 1:
- self.exit_with_error("Please provide --patch-data as colon delimited key:value pair")
-
- if pop != 'remove':
- ppath, pval = pdata.split(':')
- data = [{'op': pop, 'path': '/'+ ppath.lstrip('/'), 'value': pval}]
- else:
- data = [{'op': pop, 'path': '/'+ pdata.lstrip('/')}]
-
- if (schema and not data_fn) and not data:
- self.exit_with_error("Please provide schema with --data argument")
-
- caller_function = getattr(self, 'process_command_' + path['__method__'])
- caller_function(path, suffix_param, endpoint_params, data_fn, data=data)
-
- def make_schema_val(self, stype):
- if stype == 'object':
- return {}
- elif stype == 'list':
- return []
- elif stype == 'bool':
- return random.choice((True, False))
- else:
- return None
-
- def is_native_type(self, model, prop):
- stype = getattr(model, prop)
- if stype.startswith('list['):
- stype = re.match(r'list\[(.*)\]', stype).group(1)
- elif stype.startswith('dict('):
- stype = re.match(r'dict\(([^,]*), (.*)\)', stype).group(2)
-
- if stype in swagger_client.ApiClient.NATIVE_TYPES_MAPPING:
- return True
-
- def get_schema_from_model(self, model, schema):
-
- for key in model.attribute_map:
- stype = model.swagger_types[key]
- mapped_key = model.attribute_map[key]
- if stype in swagger_client.ApiClient.NATIVE_TYPES_MAPPING:
- schema[mapped_key] = self.make_schema_val(stype)
- else:
- if stype.startswith('list['):
- sub_cls = re.match(r'list\[(.*)\]', stype).group(1)
- sub_type = 'list'
- elif stype.startswith('dict('):
- sub_cls = re.match(r'dict\(([^,]*), (.*)\)', stype).group(2)
- sub_type = 'dict'
- else:
- sub_cls = stype
- sub_type = None
-
- if sub_cls in swagger_client.ApiClient.NATIVE_TYPES_MAPPING:
- schema[mapped_key] = self.make_schema_val(sub_type)
- else:
- sub_dict = {}
- sub_model = getattr(swagger_client.models, sub_cls)
- sub_schema = self.get_schema_from_model(sub_model, sub_dict)
- schema[mapped_key] = sub_dict
-
- def get_swagger_model_attr(self, model, attr):
- stype = model.swagger_types[attr]
- if stype in swagger_client.ApiClient.NATIVE_TYPES_MAPPING:
- return getattr(model, attr)
-
- def fill_defaults(self, schema, schema_={}):
-
- for k in schema:
- if isinstance(schema[k], dict):
- sub_schema_ = None
- if '$ref' in schema_['properties'][k]:
- sub_schema_ = self.cfg_yml['components']['schemas'][schema_['properties'][k]['$ref'].split('/')[-1]]
- elif schema_['properties'][k].get('items', {}).get('$ref'):
- sub_schema_ = self.cfg_yml['components']['schemas'][
- schema_['properties'][k]['items']['$ref'].split('/')[-1]]
- elif 'properties' in schema_['properties'][k]:
- sub_schema_ = schema_['properties'][k]
- if sub_schema_:
- self.fill_defaults(schema[k], sub_schema_)
-
- else:
- if 'enum' in schema_['properties'][k]:
- val = random.choice(schema_['properties'][k]['enum'])
- if schema_['properties'][k]['type'] == 'array':
- val = [val]
- schema[k] = val
- elif 'default' in schema_['properties'][k]:
- schema[k] = schema_['properties'][k]['default']
- elif 'example' in schema_['properties'][k]:
- schema[k] = schema_['properties'][k]['example']
- elif k in schema_.get('required', []):
- schema[k] = schema_['properties'][k]['type']
-
- def get_sample_schema(self, ref):
- schema_ = self.get_schema_from_reference('#' + args.schema)
- m = getattr(swagger_client.models, schema_['__schema_name__'])
- schema = {}
- self.get_schema_from_model(m, schema)
- self.fill_defaults(schema, schema_)
-
- print(json.dumps(schema, indent=2))
-
- def runApp(self):
- clear()
- self.display_menu(self.menu)
-
-
-def main():
-
- cli_object = JCA_CLI(host, client_id, client_secret, access_token, test_client)
- error_log_file = os.path.join(log_dir, 'error.log')
- try:
- if not access_token:
- cli_object.check_connection()
- if not (args.operation_id or args.info or args.schema):
- # reset previous color
- print('\033[0m', end='')
- cli_object.runApp()
- else:
- print()
- if args.info:
- cli_object.help_for(args.info)
- elif args.schema:
- cli_object.get_sample_schema(args.schema)
- elif args.operation_id:
- cli_object.process_command_by_id(args.operation_id, args.url_suffix, args.endpoint_args, args.data)
- print()
- except Exception as e:
- if os.environ.get('errstdout'):
- print(traceback.print_exc())
- print(u"\u001b[38;5;{}mAn Unhandled error raised: {}\u001b[0m".format(error_color, e))
- with open(error_log_file, 'a') as w:
- traceback.print_exc(file=w)
- print("Error is logged to {}".format(error_log_file))
-
-
-if __name__ == "__main__":
- main()
diff --git a/jans-cli/cli/pylib/__init__.py b/jans-cli/cli/pylib/__init__.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/jans-cli/cli/pylib/tabulate/LICENSE b/jans-cli/cli/pylib/tabulate/LICENSE
deleted file mode 100644
index 81241eca637..00000000000
--- a/jans-cli/cli/pylib/tabulate/LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2011-2020 Sergey Astanin and contributors
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/jans-cli/cli/pylib/tabulate/README b/jans-cli/cli/pylib/tabulate/README
deleted file mode 100644
index 42061c01a1c..00000000000
--- a/jans-cli/cli/pylib/tabulate/README
+++ /dev/null
@@ -1 +0,0 @@
-README.md
\ No newline at end of file
diff --git a/jans-cli/cli/pylib/tabulate/README.md b/jans-cli/cli/pylib/tabulate/README.md
deleted file mode 100644
index ce06dadc834..00000000000
--- a/jans-cli/cli/pylib/tabulate/README.md
+++ /dev/null
@@ -1,747 +0,0 @@
-python-tabulate
-===============
-
-Pretty-print tabular data in Python, a library and a command-line
-utility.
-
-The main use cases of the library are:
-
-- printing small tables without hassle: just one function call,
- formatting is guided by the data itself
-- authoring tabular data for lightweight plain-text markup: multiple
- output formats suitable for further editing or transformation
-- readable presentation of mixed textual and numeric data: smart
- column alignment, configurable number formatting, alignment by a
- decimal point
-
-Installation
-------------
-
-To install the Python library and the command line utility, run:
-
- pip install tabulate
-
-The command line utility will be installed as `tabulate` to `bin` on
-Linux (e.g. `/usr/bin`); or as `tabulate.exe` to `Scripts` in your
-Python installation on Windows (e.g.
-`C:\Python27\Scripts\tabulate.exe`).
-
-You may consider installing the library only for the current user:
-
- pip install tabulate --user
-
-In this case the command line utility will be installed to
-`~/.local/bin/tabulate` on Linux and to
-`%APPDATA%\Python\Scripts\tabulate.exe` on Windows.
-
-To install just the library on Unix-like operating systems:
-
- TABULATE_INSTALL=lib-only pip install tabulate
-
-On Windows:
-
- set TABULATE_INSTALL=lib-only
- pip install tabulate
-
-Build status
-------------
-
-[![Build status](https://circleci.com/gh/astanin/python-tabulate.svg?style=svg)](https://circleci.com/gh/astanin/python-tabulate/tree/master) [![Build status](https://ci.appveyor.com/api/projects/status/8745yksvvol7h3d7/branch/master?svg=true)](https://ci.appveyor.com/project/astanin/python-tabulate/branch/master)
-
-Library usage
--------------
-
-The module provides just one function, `tabulate`, which takes a list of
-lists or another tabular data type as the first argument, and outputs a
-nicely formatted plain-text table:
-
- >>> from tabulate import tabulate
-
- >>> table = [["Sun",696000,1989100000],["Earth",6371,5973.6],
- ... ["Moon",1737,73.5],["Mars",3390,641.85]]
- >>> print(tabulate(table))
- ----- ------ -------------
- Sun 696000 1.9891e+09
- Earth 6371 5973.6
- Moon 1737 73.5
- Mars 3390 641.85
- ----- ------ -------------
-
-The following tabular data types are supported:
-
-- list of lists or another iterable of iterables
-- list or another iterable of dicts (keys as columns)
-- dict of iterables (keys as columns)
-- two-dimensional NumPy array
-- NumPy record arrays (names as columns)
-- pandas.DataFrame
-
-Examples in this file use Python2. Tabulate supports Python3 too.
-
-### Headers
-
-The second optional argument named `headers` defines a list of column
-headers to be used:
-
- >>> print(tabulate(table, headers=["Planet","R (km)", "mass (x 10^29 kg)"]))
- Planet R (km) mass (x 10^29 kg)
- -------- -------- -------------------
- Sun 696000 1.9891e+09
- Earth 6371 5973.6
- Moon 1737 73.5
- Mars 3390 641.85
-
-If `headers="firstrow"`, then the first row of data is used:
-
- >>> print(tabulate([["Name","Age"],["Alice",24],["Bob",19]],
- ... headers="firstrow"))
- Name Age
- ------ -----
- Alice 24
- Bob 19
-
-If `headers="keys"`, then the keys of a dictionary/dataframe, or column
-indices are used. It also works for NumPy record arrays and lists of
-dictionaries or named tuples:
-
- >>> print(tabulate({"Name": ["Alice", "Bob"],
- ... "Age": [24, 19]}, headers="keys"))
- Age Name
- ----- ------
- 24 Alice
- 19 Bob
-
-### Row Indices
-
-By default, only pandas.DataFrame tables have an additional column
-called row index. To add a similar column to any other type of table,
-pass `showindex="always"` or `showindex=True` argument to `tabulate()`.
-To suppress row indices for all types of data, pass `showindex="never"`
-or `showindex=False`. To add a custom row index column, pass
-`showindex=rowIDs`, where `rowIDs` is some iterable:
-
- >>> print(tabulate([["F",24],["M",19]], showindex="always"))
- - - --
- 0 F 24
- 1 M 19
- - - --
-
-### Table format
-
-There is more than one way to format a table in plain text. The third
-optional argument named `tablefmt` defines how the table is formatted.
-
-Supported table formats are:
-
-- "plain"
-- "simple"
-- "github"
-- "grid"
-- "fancy\_grid"
-- "pipe"
-- "orgtbl"
-- "jira"
-- "presto"
-- "pretty"
-- "psql"
-- "rst"
-- "mediawiki"
-- "moinmoin"
-- "youtrack"
-- "html"
-- "unsafehtml"
-- "latex"
-- "latex\_raw"
-- "latex\_booktabs"
-- "textile"
-
-`plain` tables do not use any pseudo-graphics to draw lines:
-
- >>> table = [["spam",42],["eggs",451],["bacon",0]]
- >>> headers = ["item", "qty"]
- >>> print(tabulate(table, headers, tablefmt="plain"))
- item qty
- spam 42
- eggs 451
- bacon 0
-
-`simple` is the default format (the default may change in future
-versions). It corresponds to `simple_tables` in [Pandoc Markdown
-extensions](http://johnmacfarlane.net/pandoc/README.html#tables):
-
- >>> print(tabulate(table, headers, tablefmt="simple"))
- item qty
- ------ -----
- spam 42
- eggs 451
- bacon 0
-
-`github` follows the conventions of Github flavored Markdown. It
-corresponds to the `pipe` format without alignment colons:
-
- >>> print(tabulate(table, headers, tablefmt="github"))
- | item | qty |
- |--------|-------|
- | spam | 42 |
- | eggs | 451 |
- | bacon | 0 |
-
-`grid` is like tables formatted by Emacs'
-[table.el](http://table.sourceforge.net/) package. It corresponds to
-`grid_tables` in Pandoc Markdown extensions:
-
- >>> print(tabulate(table, headers, tablefmt="grid"))
- +--------+-------+
- | item | qty |
- +========+=======+
- | spam | 42 |
- +--------+-------+
- | eggs | 451 |
- +--------+-------+
- | bacon | 0 |
- +--------+-------+
-
-`fancy_grid` draws a grid using box-drawing characters:
-
- >>> print(tabulate(table, headers, tablefmt="fancy_grid"))
- ╒════════╤═══════╕
- │ item │ qty │
- ╞════════╪═══════╡
- │ spam │ 42 │
- ├────────┼───────┤
- │ eggs │ 451 │
- ├────────┼───────┤
- │ bacon │ 0 │
- ╘════════╧═══════╛
-
-`presto` is like tables formatted by Presto cli:
-
- >>> print(tabulate(table, headers, tablefmt="presto"))
- item | qty
- --------+-------
- spam | 42
- eggs | 451
- bacon | 0
-
-`pretty` attempts to be close to the format emitted by the PrettyTables
-library:
-
- >>> print(tabulate(table, headers, tablefmt="pretty"))
- +-------+-----+
- | item | qty |
- +-------+-----+
- | spam | 42 |
- | eggs | 451 |
- | bacon | 0 |
- +-------+-----+
-
-`psql` is like tables formatted by Postgres' psql cli:
-
- >>> print(tabulate(table, headers, tablefmt="psql"))
- +--------+-------+
- | item | qty |
- |--------+-------|
- | spam | 42 |
- | eggs | 451 |
- | bacon | 0 |
- +--------+-------+
-
-`pipe` follows the conventions of [PHP Markdown
-Extra](http://michelf.ca/projects/php-markdown/extra/#table) extension.
-It corresponds to `pipe_tables` in Pandoc. This format uses colons to
-indicate column alignment:
-
- >>> print(tabulate(table, headers, tablefmt="pipe"))
- | item | qty |
- |:-------|------:|
- | spam | 42 |
- | eggs | 451 |
- | bacon | 0 |
-
-`orgtbl` follows the conventions of Emacs
-[org-mode](http://orgmode.org/manual/Tables.html), and is editable also
-in the minor orgtbl-mode. Hence its name:
-
- >>> print(tabulate(table, headers, tablefmt="orgtbl"))
- | item | qty |
- |--------+-------|
- | spam | 42 |
- | eggs | 451 |
- | bacon | 0 |
-
-`jira` follows the conventions of Atlassian Jira markup language:
-
- >>> print(tabulate(table, headers, tablefmt="jira"))
- || item || qty ||
- | spam | 42 |
- | eggs | 451 |
- | bacon | 0 |
-
-`rst` formats data like a simple table of the
-[reStructuredText](http://docutils.sourceforge.net/docs/user/rst/quickref.html#tables)
-format:
-
- >>> print(tabulate(table, headers, tablefmt="rst"))
- ====== =====
- item qty
- ====== =====
- spam 42
- eggs 451
- bacon 0
- ====== =====
-
-`mediawiki` format produces a table markup used in
-[Wikipedia](http://www.mediawiki.org/wiki/Help:Tables) and on other
-MediaWiki-based sites:
-
- >>> print(tabulate(table, headers, tablefmt="mediawiki"))
- {| class="wikitable" style="text-align: left;"
- |+
- |-
- ! item !! align="right"| qty
- |-
- | spam || align="right"| 42
- |-
- | eggs || align="right"| 451
- |-
- | bacon || align="right"| 0
- |}
-
-`moinmoin` format produces a table markup used in
-[MoinMoin](https://moinmo.in/) wikis:
-
- >>> print(tabulate(table, headers, tablefmt="moinmoin"))
- || ''' item ''' || ''' quantity ''' ||
- || spam || 41.999 ||
- || eggs || 451 ||
- || bacon || ||
-
-`youtrack` format produces a table markup used in Youtrack tickets:
-
- >>> print(tabulate(table, headers, tablefmt="youtrack"))
- || item || quantity ||
- | spam | 41.999 |
- | eggs | 451 |
- | bacon | |
-
-`textile` format produces a table markup used in
-[Textile](http://redcloth.org/hobix.com/textile/) format:
-
- >>> print(tabulate(table, headers, tablefmt="textile"))
- |_. item |_. qty |
- |<. spam |>. 42 |
- |<. eggs |>. 451 |
- |<. bacon |>. 0 |
-
-`html` produces standard HTML markup as an html.escape'd str
-with a ._repr_html_ method so that Jupyter Lab and Notebook display the HTML
-and a .str property so that the raw HTML remains accessible.
-`unsafehtml` table format can be used if an unescaped HTML is required:
-
- >>> print(tabulate(table, headers, tablefmt="html"))
-
-
- item | qty |
- spam | 42 |
- eggs | 451 |
- bacon | 0 |
-
-
-
-`latex` format creates a `tabular` environment for LaTeX markup,
-replacing special characters like `_` or `\` to their LaTeX
-correspondents:
-
- >>> print(tabulate(table, headers, tablefmt="latex"))
- \begin{tabular}{lr}
- \hline
- item & qty \\
- \hline
- spam & 42 \\
- eggs & 451 \\
- bacon & 0 \\
- \hline
- \end{tabular}
-
-`latex_raw` behaves like `latex` but does not escape LaTeX commands and
-special characters.
-
-`latex_booktabs` creates a `tabular` environment for LaTeX markup using
-spacing and style from the `booktabs` package.
-
-### Column alignment
-
-`tabulate` is smart about column alignment. It detects columns which
-contain only numbers, and aligns them by a decimal point (or flushes
-them to the right if they appear to be integers). Text columns are
-flushed to the left.
-
-You can override the default alignment with `numalign` and `stralign`
-named arguments. Possible column alignments are: `right`, `center`,
-`left`, `decimal` (only for numbers), and `None` (to disable alignment).
-
-Aligning by a decimal point works best when you need to compare numbers
-at a glance:
-
- >>> print(tabulate([[1.2345],[123.45],[12.345],[12345],[1234.5]]))
- ----------
- 1.2345
- 123.45
- 12.345
- 12345
- 1234.5
- ----------
-
-Compare this with a more common right alignment:
-
- >>> print(tabulate([[1.2345],[123.45],[12.345],[12345],[1234.5]], numalign="right"))
- ------
- 1.2345
- 123.45
- 12.345
- 12345
- 1234.5
- ------
-
-For `tabulate`, anything which can be parsed as a number is a number.
-Even numbers represented as strings are aligned properly. This feature
-comes in handy when reading a mixed table of text and numbers from a
-file:
-
- >>> import csv ; from StringIO import StringIO
- >>> table = list(csv.reader(StringIO("spam, 42\neggs, 451\n")))
- >>> table
- [['spam', ' 42'], ['eggs', ' 451']]
- >>> print(tabulate(table))
- ---- ----
- spam 42
- eggs 451
- ---- ----
-
-
-To disable this feature use `disable_numparse=True`.
-
- >>> print(tabulate.tabulate([["Ver1", "18.0"], ["Ver2","19.2"]], tablefmt="simple", disable_numparse=True))
- ---- ----
- Ver1 18.0
- Ver2 19.2
- ---- ----
-
-
-### Custom column alignment
-
-`tabulate` allows a custom column alignment to override the above. The
-`colalign` argument can be a list or a tuple of `stralign` named
-arguments. Possible column alignments are: `right`, `center`, `left`,
-`decimal` (only for numbers), and `None` (to disable alignment).
-Omitting an alignment uses the default. For example:
-
- >>> print(tabulate([["one", "two"], ["three", "four"]], colalign=("right",))
- ----- ----
- one two
- three four
- ----- ----
-
-### Number formatting
-
-`tabulate` allows to define custom number formatting applied to all
-columns of decimal numbers. Use `floatfmt` named argument:
-
- >>> print(tabulate([["pi",3.141593],["e",2.718282]], floatfmt=".4f"))
- -- ------
- pi 3.1416
- e 2.7183
- -- ------
-
-`floatfmt` argument can be a list or a tuple of format strings, one per
-column, in which case every column may have different number formatting:
-
- >>> print(tabulate([[0.12345, 0.12345, 0.12345]], floatfmt=(".1f", ".3f")))
- --- ----- -------
- 0.1 0.123 0.12345
- --- ----- -------
-
-### Text formatting
-
-By default, `tabulate` removes leading and trailing whitespace from text
-columns. To disable whitespace removal, set the global module-level flag
-`PRESERVE_WHITESPACE`:
-
- import tabulate
- tabulate.PRESERVE_WHITESPACE = True
-
-### Wide (fullwidth CJK) symbols
-
-To properly align tables which contain wide characters (typically
-fullwidth glyphs from Chinese, Japanese or Korean languages), the user
-should install `wcwidth` library. To install it together with
-`tabulate`:
-
- pip install tabulate[widechars]
-
-Wide character support is enabled automatically if `wcwidth` library is
-already installed. To disable wide characters support without
-uninstalling `wcwidth`, set the global module-level flag
-`WIDE_CHARS_MODE`:
-
- import tabulate
- tabulate.WIDE_CHARS_MODE = False
-
-### Multiline cells
-
-Most table formats support multiline cell text (text containing newline
-characters). The newline characters are honored as line break
-characters.
-
-Multiline cells are supported for data rows and for header rows.
-
-Further automatic line breaks are not inserted. Of course, some output
-formats such as latex or html handle automatic formatting of the cell
-content on their own, but for those that don't, the newline characters
-in the input cell text are the only means to break a line in cell text.
-
-Note that some output formats (e.g. simple, or plain) do not represent
-row delimiters, so that the representation of multiline cells in such
-formats may be ambiguous to the reader.
-
-The following examples of formatted output use the following table with
-a multiline cell, and headers with a multiline cell:
-
- >>> table = [["eggs",451],["more\nspam",42]]
- >>> headers = ["item\nname", "qty"]
-
-`plain` tables:
-
- >>> print(tabulate(table, headers, tablefmt="plain"))
- item qty
- name
- eggs 451
- more 42
- spam
-
-`simple` tables:
-
- >>> print(tabulate(table, headers, tablefmt="simple"))
- item qty
- name
- ------ -----
- eggs 451
- more 42
- spam
-
-`grid` tables:
-
- >>> print(tabulate(table, headers, tablefmt="grid"))
- +--------+-------+
- | item | qty |
- | name | |
- +========+=======+
- | eggs | 451 |
- +--------+-------+
- | more | 42 |
- | spam | |
- +--------+-------+
-
-`fancy_grid` tables:
-
- >>> print(tabulate(table, headers, tablefmt="fancy_grid"))
- ╒════════╤═══════╕
- │ item │ qty │
- │ name │ │
- ╞════════╪═══════╡
- │ eggs │ 451 │
- ├────────┼───────┤
- │ more │ 42 │
- │ spam │ │
- ╘════════╧═══════╛
-
-`pipe` tables:
-
- >>> print(tabulate(table, headers, tablefmt="pipe"))
- | item | qty |
- | name | |
- |:-------|------:|
- | eggs | 451 |
- | more | 42 |
- | spam | |
-
-`orgtbl` tables:
-
- >>> print(tabulate(table, headers, tablefmt="orgtbl"))
- | item | qty |
- | name | |
- |--------+-------|
- | eggs | 451 |
- | more | 42 |
- | spam | |
-
-`jira` tables:
-
- >>> print(tabulate(table, headers, tablefmt="jira"))
- | item | qty |
- | name | |
- |:-------|------:|
- | eggs | 451 |
- | more | 42 |
- | spam | |
-
-`presto` tables:
-
- >>> print(tabulate(table, headers, tablefmt="presto"))
- item | qty
- name |
- --------+-------
- eggs | 451
- more | 42
- spam |
-
-`pretty` tables:
-
- >>> print(tabulate(table, headers, tablefmt="pretty"))
- +------+-----+
- | item | qty |
- | name | |
- +------+-----+
- | eggs | 451 |
- | more | 42 |
- | spam | |
- +------+-----+
-
-`psql` tables:
-
- >>> print(tabulate(table, headers, tablefmt="psql"))
- +--------+-------+
- | item | qty |
- | name | |
- |--------+-------|
- | eggs | 451 |
- | more | 42 |
- | spam | |
- +--------+-------+
-
-`rst` tables:
-
- >>> print(tabulate(table, headers, tablefmt="rst"))
- ====== =====
- item qty
- name
- ====== =====
- eggs 451
- more 42
- spam
- ====== =====
-
-Multiline cells are not well supported for the other table formats.
-
-Usage of the command line utility
----------------------------------
-
- Usage: tabulate [options] [FILE ...]
-
- FILE a filename of the file with tabular data;
- if "-" or missing, read data from stdin.
-
- Options:
-
- -h, --help show this message
- -1, --header use the first row of data as a table header
- -o FILE, --output FILE print table to FILE (default: stdout)
- -s REGEXP, --sep REGEXP use a custom column separator (default: whitespace)
- -F FPFMT, --float FPFMT floating point number format (default: g)
- -f FMT, --format FMT set output table format; supported formats:
- plain, simple, github, grid, fancy_grid, pipe,
- orgtbl, rst, mediawiki, html, latex, latex_raw,
- latex_booktabs, tsv
- (default: simple)
-
-Performance considerations
---------------------------
-
-Such features as decimal point alignment and trying to parse everything
-as a number imply that `tabulate`:
-
-- has to "guess" how to print a particular tabular data type
-- needs to keep the entire table in-memory
-- has to "transpose" the table twice
-- does much more work than it may appear
-
-It may not be suitable for serializing really big tables (but who's
-going to do that, anyway?) or printing tables in performance sensitive
-applications. `tabulate` is about two orders of magnitude slower than
-simply joining lists of values with a tab, coma or other separator.
-
-In the same time `tabulate` is comparable to other table
-pretty-printers. Given a 10x10 table (a list of lists) of mixed text and
-numeric data, `tabulate` appears to be slower than `asciitable`, and
-faster than `PrettyTable` and `texttable` The following mini-benchmark
-was run in Python 3.8.1 in Windows 10 x64:
-
- =========================== ========== ===========
- Table formatter time, μs rel. time
- =========================== ========== ===========
- csv to StringIO 12.4 1.0
- join with tabs and newlines 15.7 1.3
- asciitable (0.8.0) 208.3 16.7
- tabulate (0.8.7) 492.1 39.5
- PrettyTable (0.7.2) 945.5 76.0
- texttable (1.6.2) 1239.5 99.6
- =========================== ========== ===========
-
-
-Version history
----------------
-
-The full version history can be found at the [changelog](https://github.com/astanin/python-tabulate/blob/master/CHANGELOG).
-
-How to contribute
------------------
-
-Contributions should include tests and an explanation for the changes
-they propose. Documentation (examples, docstrings, README.md) should be
-updated accordingly.
-
-This project uses [nose](https://nose.readthedocs.org/) testing
-framework and [tox](https://tox.readthedocs.io/) to automate testing in
-different environments. Add tests to one of the files in the `test/`
-folder.
-
-To run tests on all supported Python versions, make sure all Python
-interpreters, `nose` and `tox` are installed, then run `tox` in the root
-of the project source tree.
-
-On Linux `tox` expects to find executables like `python2.6`,
-`python2.7`, `python3.4` etc. On Windows it looks for
-`C:\Python26\python.exe`, `C:\Python27\python.exe` and
-`C:\Python34\python.exe` respectively.
-
-To test only some Python environements, use `-e` option. For example, to
-test only against Python 2.7 and Python 3.6, run:
-
- tox -e py27,py36
-
-in the root of the project source tree.
-
-To enable NumPy and Pandas tests, run:
-
- tox -e py27-extra,py36-extra
-
-(this may take a long time the first time, because NumPy and Pandas will
-have to be installed in the new virtual environments)
-
-See `tox.ini` file to learn how to use `nosetests` directly to test
-individual Python versions.
-
-Contributors
-------------
-
-Sergey Astanin, Pau Tallada Crespí, Erwin Marsi, Mik Kocikowski, Bill
-Ryder, Zach Dwiel, Frederik Rietdijk, Philipp Bogensberger, Greg
-(anonymous), Stefan Tatschner, Emiel van Miltenburg, Brandon Bennett,
-Amjith Ramanujam, Jan Schulz, Simon Percivall, Javier Santacruz
-López-Cepero, Sam Denton, Alexey Ziyangirov, acaird, Cesar Sanchez,
-naught101, John Vandenberg, Zack Dever, Christian Clauss, Benjamin
-Maier, Andy MacKinlay, Thomas Roten, Jue Wang, Joe King, Samuel Phan,
-Nick Satterly, Daniel Robbins, Dmitry B, Lars Butler, Andreas Maier,
-Dick Marinus, Sébastien Celles, Yago González, Andrew Gaul, Wim Glenn,
-Jean Michel Rouly, Tim Gates, John Vandenberg, Sorin Sbarnea,
-Wes Turner, Andrew Tija, Marco Gorelli, Sean McGinnis, danja100.
diff --git a/jans-cli/cli/pylib/tabulate/__init__.py b/jans-cli/cli/pylib/tabulate/__init__.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/jans-cli/cli/pylib/tabulate/tabulate.py b/jans-cli/cli/pylib/tabulate/tabulate.py
deleted file mode 100644
index 2bbb913c62d..00000000000
--- a/jans-cli/cli/pylib/tabulate/tabulate.py
+++ /dev/null
@@ -1,1792 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""Pretty-print tabular data."""
-
-from __future__ import print_function
-from __future__ import unicode_literals
-from collections import namedtuple
-from platform import python_version_tuple
-import re
-import math
-import sys
-
-
-if sys.version_info.major >= 3 and sys.version_info.minor >= 3:
- from collections.abc import Iterable
-else:
- from collections import Iterable
-
-
-if python_version_tuple()[0] < "3":
- from itertools import izip_longest
- from functools import partial
-
- _none_type = type(None)
- _bool_type = bool
- _int_type = int
- _long_type = long # noqa
- _float_type = float
- _text_type = unicode # noqa
- _binary_type = str
-
- def _is_file(f):
- return hasattr(f, "read")
-
-
-else:
- from itertools import zip_longest as izip_longest
- from functools import reduce, partial
-
- _none_type = type(None)
- _bool_type = bool
- _int_type = int
- _long_type = int
- _float_type = float
- _text_type = str
- _binary_type = bytes
- basestring = str
-
- import io
-
- def _is_file(f):
- return isinstance(f, io.IOBase)
-
-
-try:
- import wcwidth # optional wide-character (CJK) support
-except ImportError:
- wcwidth = None
-
-try:
- from html import escape as htmlescape
-except ImportError:
- from cgi import escape as htmlescape
-
-
-__all__ = ["tabulate", "tabulate_formats", "simple_separated_format"]
-__version__ = "0.8.8"
-
-
-# minimum extra space in headers
-MIN_PADDING = 2
-
-# Whether or not to preserve leading/trailing whitespace in data.
-PRESERVE_WHITESPACE = False
-
-_DEFAULT_FLOATFMT = "g"
-_DEFAULT_MISSINGVAL = ""
-
-
-# if True, enable wide-character (CJK) support
-WIDE_CHARS_MODE = wcwidth is not None
-
-
-Line = namedtuple("Line", ["begin", "hline", "sep", "end"])
-
-
-DataRow = namedtuple("DataRow", ["begin", "sep", "end"])
-
-
-# A table structure is suppposed to be:
-#
-# --- lineabove ---------
-# headerrow
-# --- linebelowheader ---
-# datarow
-# --- linebewteenrows ---
-# ... (more datarows) ...
-# --- linebewteenrows ---
-# last datarow
-# --- linebelow ---------
-#
-# TableFormat's line* elements can be
-#
-# - either None, if the element is not used,
-# - or a Line tuple,
-# - or a function: [col_widths], [col_alignments] -> string.
-#
-# TableFormat's *row elements can be
-#
-# - either None, if the element is not used,
-# - or a DataRow tuple,
-# - or a function: [cell_values], [col_widths], [col_alignments] -> string.
-#
-# padding (an integer) is the amount of white space around data values.
-#
-# with_header_hide:
-#
-# - either None, to display all table elements unconditionally,
-# - or a list of elements not to be displayed if the table has column headers.
-#
-TableFormat = namedtuple(
- "TableFormat",
- [
- "lineabove",
- "linebelowheader",
- "linebetweenrows",
- "linebelow",
- "headerrow",
- "datarow",
- "padding",
- "with_header_hide",
- ],
-)
-
-
-def _pipe_segment_with_colons(align, colwidth):
- """Return a segment of a horizontal line with optional colons which
- indicate column's alignment (as in `pipe` output format)."""
- w = colwidth
- if align in ["right", "decimal"]:
- return ("-" * (w - 1)) + ":"
- elif align == "center":
- return ":" + ("-" * (w - 2)) + ":"
- elif align == "left":
- return ":" + ("-" * (w - 1))
- else:
- return "-" * w
-
-
-def _pipe_line_with_colons(colwidths, colaligns):
- """Return a horizontal line with optional colons to indicate column's
- alignment (as in `pipe` output format)."""
- if not colaligns: # e.g. printing an empty data frame (github issue #15)
- colaligns = [""] * len(colwidths)
- segments = [_pipe_segment_with_colons(a, w) for a, w in zip(colaligns, colwidths)]
- return "|" + "|".join(segments) + "|"
-
-
-def _mediawiki_row_with_attrs(separator, cell_values, colwidths, colaligns):
- alignment = {
- "left": "",
- "right": 'align="right"| ',
- "center": 'align="center"| ',
- "decimal": 'align="right"| ',
- }
- # hard-coded padding _around_ align attribute and value together
- # rather than padding parameter which affects only the value
- values_with_attrs = [
- " " + alignment.get(a, "") + c + " " for c, a in zip(cell_values, colaligns)
- ]
- colsep = separator * 2
- return (separator + colsep.join(values_with_attrs)).rstrip()
-
-
-def _textile_row_with_attrs(cell_values, colwidths, colaligns):
- cell_values[0] += " "
- alignment = {"left": "<.", "right": ">.", "center": "=.", "decimal": ">."}
- values = (alignment.get(a, "") + v for a, v in zip(colaligns, cell_values))
- return "|" + "|".join(values) + "|"
-
-
-def _html_begin_table_without_header(colwidths_ignore, colaligns_ignore):
- # this table header will be suppressed if there is a header row
- return "\n"
-
-
-def _html_row_with_attrs(celltag, unsafe, cell_values, colwidths, colaligns):
- alignment = {
- "left": "",
- "right": ' style="text-align: right;"',
- "center": ' style="text-align: center;"',
- "decimal": ' style="text-align: right;"',
- }
- if unsafe:
- values_with_attrs = [
- "<{0}{1}>{2}{0}>".format(celltag, alignment.get(a, ""), c)
- for c, a in zip(cell_values, colaligns)
- ]
- else:
- values_with_attrs = [
- "<{0}{1}>{2}{0}>".format(celltag, alignment.get(a, ""), htmlescape(c))
- for c, a in zip(cell_values, colaligns)
- ]
- rowhtml = "{}
".format("".join(values_with_attrs).rstrip())
- if celltag == "th": # it's a header row, create a new table header
- rowhtml = "\n\n{}\n\n".format(rowhtml)
- return rowhtml
-
-
-def _moin_row_with_attrs(celltag, cell_values, colwidths, colaligns, header=""):
- alignment = {
- "left": "",
- "right": '