From d1393544a66f31a7b2fae374335e9d49096b736e Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Tue, 22 Feb 2022 17:15:19 +0300 Subject: [PATCH 001/101] fix: flex-linux-setup start casa after install --- flex-linux-setup/flex_linux_setup/flex_setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/flex-linux-setup/flex_linux_setup/flex_setup.py b/flex-linux-setup/flex_linux_setup/flex_setup.py index e0dd50348..439eebbf4 100755 --- a/flex-linux-setup/flex_linux_setup/flex_setup.py +++ b/flex-linux-setup/flex_linux_setup/flex_setup.py @@ -270,6 +270,7 @@ def main(): if argsp.casa_integration: installer_obj.install_casa() + config_api_installer.start('casa') sys.exit() print("Restarting Janssen Config Api") From 4c1fa73140e55cd00e91f98ad7d484b41ffa6820 Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Tue, 22 Feb 2022 20:53:51 +0300 Subject: [PATCH 002/101] fix: flex-linux-setup casa installation fixes --- .../flex_linux_setup/flex_setup.py | 64 +++++++++++++++++-- .../templates/casa_config.ldif | 2 +- .../casa_person_authentication_script.ldif | 15 +++++ 3 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 flex-linux-setup/flex_linux_setup/templates/casa_person_authentication_script.ldif diff --git a/flex-linux-setup/flex_linux_setup/flex_setup.py b/flex-linux-setup/flex_linux_setup/flex_setup.py index 439eebbf4..72dc13f02 100755 --- a/flex-linux-setup/flex_linux_setup/flex_setup.py +++ b/flex-linux-setup/flex_linux_setup/flex_setup.py @@ -5,6 +5,7 @@ import zipfile import argparse import time +import glob from urllib.parse import urljoin if not os.path.join('/etc/jans/conf/jans.properties'): @@ -17,7 +18,6 @@ __STATIC_SETUP_DIR__ = '/opt/jans/jans-setup/' - try: import jans_setup sys.path.append(jans_setup.__path__[0]) @@ -53,6 +53,8 @@ from setup_app.installers.httpd import HttpdInstaller from setup_app.installers.config_api import ConfigApiInstaller from setup_app.installers.jetty import JettyInstaller +from setup_app.installers.jans_auth import JansAuthInstaller + Config.outputFolder = os.path.join(__STATIC_SETUP_DIR__, 'output') if not os.path.join(Config.outputFolder): @@ -82,11 +84,13 @@ "JANS_BUILD": "-SNAPSHOT", "NODE_VERSION": "v14.18.2", "CASA_VERSION": "5.0.0-SNAPSHOT", + "TWILIO_VERSION": "7.17.0", } node_installer = NodeInstaller() httpd_installer = HttpdInstaller() config_api_installer = ConfigApiInstaller() +jansAuthInstaller = JansAuthInstaller() class flex_installer(JettyInstaller): @@ -103,8 +107,13 @@ def __init__(self): self.flex_setup_dir = os.path.join(self.source_dir, 'flex-linux-setup') self.templates_dir = os.path.join(self.flex_setup_dir, 'templates') self.admin_ui_config_properties_path = os.path.join(self.templates_dir, 'auiConfiguration.properties') - self.casa_web_resources_fn = os.path.join(Config.distJansFolder, 'casa_web_resources.xml') - self.casa_war_fn = os.path.join(Config.distJansFolder, 'casa.war') + self.casa_dist_dir = os.path.join(Config.distJansFolder, 'gluu-casa') + self.casa_web_resources_fn = os.path.join(self.casa_dist_dir, 'casa_web_resources.xml') + self.casa_war_fn = os.path.join(self.casa_dist_dir, 'casa.war') + self.casa_config_fn = os.path.join(self.casa_dist_dir,'casa-config.jar') + self.casa_script_fn = os.path.join(self.casa_dist_dir,'Casa.py') + self.twillo_fn = os.path.join(self.casa_dist_dir,'twilio.jar') + self.py_lib_dir = '/opt/gluu/python/libs/' def download_files(self): print("Downloading components") @@ -114,6 +123,14 @@ def download_files(self): base.download('https://github.com/GluuFederation/flex/archive/refs/heads/{}.zip'.format(app_versions['FLEX_BRANCH']), self.flex_path) base.download('https://github.com/GluuFederation/casa/raw/gluu_cloud/extras/casa_web_resources.xml', self.casa_web_resources_fn) base.download('https://maven.gluu.org/maven/org/gluu/casa/{0}/casa-{0}.war'.format(app_versions['CASA_VERSION']), self.casa_war_fn) + base.download('https://maven.gluu.org/maven/org/gluu/casa-config/{0}/casa-config-{0}.jar'.format(app_versions['CASA_VERSION']), self.casa_config_fn) + base.download('https://repo1.maven.org/maven2/com/twilio/sdk/twilio/{0}/twilio-{0}.jar'.format(app_versions['TWILIO_VERSION']), self.twillo_fn) + base.download('https://raw.githubusercontent.com/GluuFederation/casa/gluu_cloud/extras/Casa.py', self.casa_script_fn) + base.download('https://raw.githubusercontent.com/GluuFederation/casa/gluu_cloud/extras/casa-external_fido2.py', os.path.join(self.casa_dist_dir, 'pylib/casa-external_fido2.py')) + base.download('https://raw.githubusercontent.com/GluuFederation/casa/gluu_cloud/extras/casa-external_otp.py', os.path.join(self.casa_dist_dir, 'pylib/casa-external_otp.py')) + base.download('https://raw.githubusercontent.com/GluuFederation/casa/gluu_cloud/extras/casa-external_super_gluu.py', os.path.join(self.casa_dist_dir, 'pylib/casa-external_super_gluu.py')) + base.download('https://raw.githubusercontent.com/GluuFederation/casa/gluu_cloud/extras/casa-external_twilio_sms.py', os.path.join(self.casa_dist_dir, 'pylib/casa-external_twilio_sms.py')) + base.download('https://raw.githubusercontent.com/GluuFederation/casa/gluu_cloud/extras/casa-external_u2f.py', os.path.join(self.casa_dist_dir, 'pylib/casa-external_u2f.py')) def install_gluu_admin_ui(self): @@ -156,6 +173,38 @@ def install_gluu_admin_ui(self): def install_casa(self): + + + jans_auth_dir = os.path.join(Config.jetty_base, jansAuthInstaller.service_name) + jans_auth_custom_lib_dir = os.path.join(jans_auth_dir, 'custom/libs') + + print("Adding twillo and casa config to jans-auth") + self.copyFile(self.casa_config_fn, jans_auth_custom_lib_dir) + self.copyFile(self.twillo_fn, jans_auth_custom_lib_dir) + class_path = '{},{}'.format( + os.path.join(jans_auth_custom_lib_dir, os.path.basename(self.casa_config_fn)), + os.path.join(jans_auth_custom_lib_dir, os.path.basename(self.twillo_fn)), + ) + xml_fn = os.path.join(jans_auth_dir, 'webapps', jansAuthInstaller.service_name+'.xml') + jansAuthInstaller.add_extra_class(class_path, xml_fn) + + simple_auth_scr_inum = 'A51E-76DA' + print("Enabling script", simple_auth_scr_inum) + self.dbUtils.enable_script(simple_auth_scr_inum) + + # copy casa scripts + if not os.path.exists(self.py_lib_dir): + os.makedirs(self.py_lib_dir) + for fn in glob.glob(os.path.join(self.casa_dist_dir, 'pylib/*.py')): + print("Copying", fn, "to", self.py_lib_dir) + self.copyFile(fn, self.py_lib_dir) + + # prepare casa scipt ldif + casa_auth_script_fn = os.path.join(self.templates_dir, 'casa_person_authentication_script.ldif') + base64_script_file = self.generate_base64_file(self.casa_script_fn, 1) + Config.templateRenderingDict['casa_person_authentication_script'] = base64_script_file + self.renderTemplateInOut(casa_auth_script_fn, self.templates_dir, self.source_dir) + Config.templateRenderingDict['casa_redirect_uri'] = 'https://{}/casa'.format(Config.hostname) Config.templateRenderingDict['casa_redirect_logout_uri'] = 'https://{}/casa/bye.zul'.format(Config.hostname) Config.templateRenderingDict['casa_frontchannel_logout_uri'] = 'https://{}/casa/autologout'.format(Config.hostname) @@ -171,11 +220,14 @@ def install_casa(self): print(Config.casa_client_encoded_pw) print(Config.casa_client_pw) + print("Importing LDIF Files") + self.renderTemplateInOut(self.casa_client_fn, self.templates_dir, self.source_dir) self.renderTemplateInOut(self.casa_config_fn, self.templates_dir, self.source_dir) self.dbUtils.import_ldif([ os.path.join(self.source_dir, os.path.basename(self.casa_client_fn)), os.path.join(self.source_dir, os.path.basename(self.casa_config_fn)), + os.path.join(self.source_dir, os.path.basename(casa_auth_script_fn)), ]) @@ -216,6 +268,7 @@ def install_casa(self): self.calculate_aplications_memory(Config.application_max_ram, self.jetty_app_configuration, installedComponents) + print("Deploying casa as Jetty application") self.installJettyService(self.jetty_app_configuration[self.service_name], True) self.copyFile(os.path.join(self.templates_dir, 'casa.service'), '/etc/systemd/system') jetty_service_dir = os.path.join(Config.jetty_base, self.service_name) @@ -231,6 +284,8 @@ def install_casa(self): self.run([paths.cmd_mkdir, '-p', os.path.join(gluu_python_dir, 'libs')]) self.run([paths.cmd_chown, '-R', '{0}:{0}'.format(Config.jetty_user), gluu_python_dir]) + + print("Updating apache configuration") apache_directive_template_text = self.readFile(os.path.join(self.templates_dir, 'casa_apache_directive')) apache_directive_text = self.fomatWithDict(apache_directive_template_text, Config.templateRenderingDict) @@ -266,13 +321,14 @@ def main(): installer_obj = flex_installer() installer_obj.download_files() + installer_obj.install_gluu_admin_ui() if argsp.casa_integration: installer_obj.install_casa() config_api_installer.start('casa') + config_api_installer.restart('jans-auth') - sys.exit() print("Restarting Janssen Config Api") config_api_installer.restart() diff --git a/flex-linux-setup/flex_linux_setup/templates/casa_config.ldif b/flex-linux-setup/flex_linux_setup/templates/casa_config.ldif index 7d0bf9171..e0ab45fdf 100644 --- a/flex-linux-setup/flex_linux_setup/templates/casa_config.ldif +++ b/flex-linux-setup/flex_linux_setup/templates/casa_config.ldif @@ -2,4 +2,4 @@ dn: ou=casa,ou=configuration,o=jans objectClass: jansAppConf objectClass: top ou: casa -jansConfApp: {"enable_pass_reset": true, "oidc_config": {"authz_redirect_uri": "%(casa_redirect_uri)s", "post_logout_uri": "%(casa_redirect_logout_uri)s", "frontchannel_logout_uri": "%(casa_frontchannel_logout_uri)s", "scopes": ["openid", "profile", "user_name", "clientinfo"], "op_host": "%(hostname)s", "client": {"clientId": "%(casa_client_id)s", "clientSecret": "casa_client_pw", "clientName": "Client for Casa"}}} +jansConfApp: {"enable_pass_reset": true, "oidc_config": {"authz_redirect_uri": "%(casa_redirect_uri)s", "post_logout_uri": "%(casa_redirect_logout_uri)s", "frontchannel_logout_uri": "%(casa_frontchannel_logout_uri)s", "scopes": ["openid", "profile", "user_name", "clientinfo"], "op_host": "%(hostname)s", "client": {"clientId": "%(casa_client_id)s", "clientSecret": "%(casa_client_pw)s", "clientName": "Client for Casa"}}} diff --git a/flex-linux-setup/flex_linux_setup/templates/casa_person_authentication_script.ldif b/flex-linux-setup/flex_linux_setup/templates/casa_person_authentication_script.ldif new file mode 100644 index 000000000..ed345ed92 --- /dev/null +++ b/flex-linux-setup/flex_linux_setup/templates/casa_person_authentication_script.ldif @@ -0,0 +1,15 @@ +dn: inum=3000-F75A,ou=scripts,o=jans +description: Gluu Casa Person Authentication +displayName: casa +inum: 3000-F75A +jansLevel: 1 +jansModuleProperty: {"value1":"location_type","value2":"ldap","description":""} +jansRevision: 1 +jansScr::%(casa_person_authentication_script)s +jansConfProperty: {"value1":"supergluu_app_id","value2":"https://%(hostname)s/casa","description":""} +jansConfProperty: {"value1":"u2f_app_id","value2":"https://%(hostname)s","description":""} +jansScrTyp: person_authentication +objectClass: top +objectClass: jansCustomScr +jansEnabled: false +jansProgLng: python \ No newline at end of file From 44abfb2f85715f1a78453ca900e4799c484c9ffb Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Tue, 22 Feb 2022 21:11:21 +0300 Subject: [PATCH 003/101] fix: flex-linux-setup more verbosity --- flex-linux-setup/flex_linux_setup/flex_setup.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/flex-linux-setup/flex_linux_setup/flex_setup.py b/flex-linux-setup/flex_linux_setup/flex_setup.py index 72dc13f02..7372ed8bb 100755 --- a/flex-linux-setup/flex_linux_setup/flex_setup.py +++ b/flex-linux-setup/flex_linux_setup/flex_setup.py @@ -216,9 +216,9 @@ def install_casa(self): self.check_clients([('casa_client_id', '3000.')]) - print(Config.casa_client_id) - print(Config.casa_client_encoded_pw) - print(Config.casa_client_pw) + print("Casa client id", Config.casa_client_id) + print("Casa client password", Config.casa_client_pw) + print("Casa client encoded password", Config.casa_client_encoded_pw) print("Importing LDIF Files") @@ -303,6 +303,7 @@ def install_casa(self): https_jans_list.insert(n+1, '\n' + apache_directive_text + '\n') self.writeFile(httpd_installer.https_jans_fn, '\n'.join(https_jans_list)) + print("Restarting Apache") httpd_installer.restart() self.enable() @@ -326,8 +327,10 @@ def main(): if argsp.casa_integration: installer_obj.install_casa() - config_api_installer.start('casa') + print("Restarting Jans Auth") config_api_installer.restart('jans-auth') + print("Starting Casa") + config_api_installer.start('casa') print("Restarting Janssen Config Api") config_api_installer.restart() From 589f8a8a77f6a8baa6f909ac26107e4120c4f38b Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Wed, 23 Feb 2022 16:58:08 +0300 Subject: [PATCH 004/101] fix: flex-linux-setup enable Casa Person Auth script --- .../templates/casa_person_authentication_script.ldif | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flex-linux-setup/flex_linux_setup/templates/casa_person_authentication_script.ldif b/flex-linux-setup/flex_linux_setup/templates/casa_person_authentication_script.ldif index ed345ed92..32f391dbd 100644 --- a/flex-linux-setup/flex_linux_setup/templates/casa_person_authentication_script.ldif +++ b/flex-linux-setup/flex_linux_setup/templates/casa_person_authentication_script.ldif @@ -11,5 +11,5 @@ jansConfProperty: {"value1":"u2f_app_id","value2":"https://%(hostname)s","descri jansScrTyp: person_authentication objectClass: top objectClass: jansCustomScr -jansEnabled: false -jansProgLng: python \ No newline at end of file +jansEnabled: true +jansProgLng: python From 78999c00bda6dbad607621c873239abb03fce905 Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Thu, 24 Feb 2022 16:48:31 +0300 Subject: [PATCH 005/101] fix: flex-linux-setup set jansTrustedClnt: true in casa client --- flex-linux-setup/flex_linux_setup/flex_setup.py | 3 ++- flex-linux-setup/flex_linux_setup/templates/casa_client.ldif | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/flex-linux-setup/flex_linux_setup/flex_setup.py b/flex-linux-setup/flex_linux_setup/flex_setup.py index 7372ed8bb..4eb7a66de 100755 --- a/flex-linux-setup/flex_linux_setup/flex_setup.py +++ b/flex-linux-setup/flex_linux_setup/flex_setup.py @@ -64,7 +64,8 @@ parser.add_argument('--setup-branch', help="Jannsen setup github branch", default='main') parser.add_argument('--flex-branch', help="Jannsen flex setup github branch", default='main') parser.add_argument('--jans-branch', help="Jannsen github branch", default='main') -parser.add_argument('-casa-integration', help="Install Casa Integration", action='store_true') +parser.add_argument('--install-args', help="Arguments for Jannsen installatin application") +parser.add_argument('--setup-args', help="Arguments for Jannsen setup") argsp = parser.parse_args() diff --git a/flex-linux-setup/flex_linux_setup/templates/casa_client.ldif b/flex-linux-setup/flex_linux_setup/templates/casa_client.ldif index d01d6a8c1..4f5e67051 100644 --- a/flex-linux-setup/flex_linux_setup/templates/casa_client.ldif +++ b/flex-linux-setup/flex_linux_setup/templates/casa_client.ldif @@ -30,4 +30,5 @@ jansScope: inum=341A,ou=scopes,o=jans jansSignedRespAlg: RS256 jansSubjectTyp: pairwise jansTknEndpointAuthMethod: client_secret_basic -jansTrustedClnt: false +jansTrustedClnt: true + From bdcfdb74c3b7bada0ccec76a984b520da614ef20 Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Thu, 24 Feb 2022 18:28:16 +0300 Subject: [PATCH 006/101] fix: flex-linux-setup casa jansPostLogoutRedirectURI --- flex-linux-setup/flex_linux_setup/templates/casa_client.ldif | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flex-linux-setup/flex_linux_setup/templates/casa_client.ldif b/flex-linux-setup/flex_linux_setup/templates/casa_client.ldif index 4f5e67051..dedabe1e4 100644 --- a/flex-linux-setup/flex_linux_setup/templates/casa_client.ldif +++ b/flex-linux-setup/flex_linux_setup/templates/casa_client.ldif @@ -18,7 +18,7 @@ jansInclClaimsInIdTkn: false jansLogoutSessRequired: false jansPersistClntAuthzs: true jansRedirectURI: %(casa_redirect_uri)s -jansRedirectURI: %(casa_redirect_logout_uri)s +jansPostLogoutRedirectURI: %(casa_redirect_logout_uri)s jansLogoutURI: %(casa_frontchannel_logout_uri)s jansRequireAuthTime: false jansRespTyp: code From 1e4752b1d8c796e4402c2aef93cf006345d036ed Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Thu, 24 Feb 2022 18:50:20 +0300 Subject: [PATCH 007/101] fix: flex-linux-setup casa python lib dir --- .../flex_linux_setup/flex_setup.py | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/flex-linux-setup/flex_linux_setup/flex_setup.py b/flex-linux-setup/flex_linux_setup/flex_setup.py index 4eb7a66de..37d2a1ead 100755 --- a/flex-linux-setup/flex_linux_setup/flex_setup.py +++ b/flex-linux-setup/flex_linux_setup/flex_setup.py @@ -114,7 +114,7 @@ def __init__(self): self.casa_config_fn = os.path.join(self.casa_dist_dir,'casa-config.jar') self.casa_script_fn = os.path.join(self.casa_dist_dir,'Casa.py') self.twillo_fn = os.path.join(self.casa_dist_dir,'twilio.jar') - self.py_lib_dir = '/opt/gluu/python/libs/' + self.py_lib_dir = '/opt/jans/python/libs/' def download_files(self): print("Downloading components") @@ -175,7 +175,6 @@ def install_gluu_admin_ui(self): def install_casa(self): - jans_auth_dir = os.path.join(Config.jetty_base, jansAuthInstaller.service_name) jans_auth_custom_lib_dir = os.path.join(jans_auth_dir, 'custom/libs') @@ -200,6 +199,8 @@ def install_casa(self): print("Copying", fn, "to", self.py_lib_dir) self.copyFile(fn, self.py_lib_dir) + self.run([paths.cmd_chown, '-R', '{0}:{0}'.format(Config.jetty_user), self.py_lib_dir]) + # prepare casa scipt ldif casa_auth_script_fn = os.path.join(self.templates_dir, 'casa_person_authentication_script.ldif') base64_script_file = self.generate_base64_file(self.casa_script_fn, 1) @@ -281,10 +282,6 @@ def install_casa(self): self.copyFile(self.casa_war_fn, jetty_service_webapps_dir) self.copyFile(self.casa_web_resources_fn, jetty_service_webapps_dir) self.run([paths.cmd_chown, '-R', '{0}:{0}'.format(Config.jetty_user), jetty_service_dir]) - gluu_python_dir = '/opt/gluu/python/' - self.run([paths.cmd_mkdir, '-p', os.path.join(gluu_python_dir, 'libs')]) - self.run([paths.cmd_chown, '-R', '{0}:{0}'.format(Config.jetty_user), gluu_python_dir]) - print("Updating apache configuration") apache_directive_template_text = self.readFile(os.path.join(self.templates_dir, 'casa_apache_directive')) @@ -326,17 +323,18 @@ def main(): installer_obj.install_gluu_admin_ui() - if argsp.casa_integration: - installer_obj.install_casa() - print("Restarting Jans Auth") - config_api_installer.restart('jans-auth') - print("Starting Casa") - config_api_installer.start('casa') + installer_obj.install_casa() + print("Restarting Jans Auth") + config_api_installer.restart('jans-auth') + print("Starting Casa") + config_api_installer.start('casa') print("Restarting Janssen Config Api") config_api_installer.restart() - print("Installation was completed. Browse https://{}/admin".format(Config.hostname)) + print("Installation was completed.") + print("Browse https://{}/admin".format(Config.hostname)) + print("Browse https://{}/casa".format(Config.hostname)) if __name__ == "__main__": main() From d05061f1fd99ace2c6d3962722d484cfc71a7139 Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Fri, 25 Feb 2022 19:44:40 +0300 Subject: [PATCH 008/101] feat: flex-linux-setup self installer --- .../flex_linux_setup/flex_setup.py | 112 +++++++++++------- 1 file changed, 72 insertions(+), 40 deletions(-) diff --git a/flex-linux-setup/flex_linux_setup/flex_setup.py b/flex-linux-setup/flex_linux_setup/flex_setup.py index 37d2a1ead..eb198a572 100755 --- a/flex-linux-setup/flex_linux_setup/flex_setup.py +++ b/flex-linux-setup/flex_linux_setup/flex_setup.py @@ -6,15 +6,11 @@ import argparse import time import glob -from urllib.parse import urljoin +import code -if not os.path.join('/etc/jans/conf/jans.properties'): - print("Please install Jans server then execute this script.") - sys.exit() +from urllib import request +from urllib.parse import urljoin -if not os.path.exists('/opt/jans/jetty/jans-config-api/start.ini'): - print("Please install Jans Config Api then execute this script.") - sys.exit() __STATIC_SETUP_DIR__ = '/opt/jans/jans-setup/' @@ -22,13 +18,27 @@ import jans_setup sys.path.append(jans_setup.__path__[0]) except ModuleNotFoundError: - if os.path.exists(__STATIC_SETUP_DIR__): + if os.path.exists(os.path.join(__STATIC_SETUP_DIR__, 'setup_app')): sys.path.append(__STATIC_SETUP_DIR__) else: - print("Unable to locate jans-setup, exiting ...") - sys.exit() + print("Unable to locate jans-setup, installing ...") + # split args to find jans setup branch + arg_list = [] + for arg in sys.argv[1:]: + if '=' in arg: + arg_list.append(arg.split('=', maxsplit=1)) + sys_args = dict(arg_list) + + setup_branch = sys_args.get('--jans-setup-branch') or 'main' + install_url = 'https://raw.githubusercontent.com/JanssenProject/jans/{}/jans-linux-setup/jans_setup/install.py'.format(setup_branch) + request.urlretrieve(install_url, 'install.py') + install_cmd = 'python3 install.py --setup-branch={} --no-setup'.format(setup_branch) + print("Executing", install_cmd) + os.system(install_cmd) + sys.path.append(__STATIC_SETUP_DIR__) logs_dir = os.path.join(__STATIC_SETUP_DIR__, 'logs') +argsp = None if not os.path.exists(logs_dir): os.makedirs(logs_dir) @@ -43,6 +53,26 @@ print(paths.LOG_ERROR_FILE) print('\033[0m') + +parser = argparse.ArgumentParser(description="This script downloads Gluu Admin UI components and installs") +parser.add_argument('--jans-setup-branch', help="Jannsen setup github branch", default='main') +parser.add_argument('--flex-branch', help="Jannsen flex setup github branch", default='main') +parser.add_argument('--jans-branch', help="Jannsen github branch", default='main') +parser.add_argument('-shell', help="Jannsen github branch", action='store_true') + + +if not os.path.exists('/etc/jans/conf/jans.properties'): + parser.add_to_setup_parser = True + from setup_app.utils import arg_parser + try: + from jans_setup import jans_setup + except ImportError: + import jans_setup + jans_setup.main() + argsp = arg_parser.get_parser() + + + from setup_app import static from setup_app.utils import base @@ -60,25 +90,22 @@ if not os.path.join(Config.outputFolder): os.makedirs(Config.outputFolder) -parser = argparse.ArgumentParser(description="This script downloads Gluu Admin UI components and installs") -parser.add_argument('--setup-branch', help="Jannsen setup github branch", default='main') -parser.add_argument('--flex-branch', help="Jannsen flex setup github branch", default='main') -parser.add_argument('--jans-branch', help="Jannsen github branch", default='main') -parser.add_argument('--install-args', help="Arguments for Jannsen installatin application") -parser.add_argument('--setup-args', help="Arguments for Jannsen setup") +if not argsp: + argsp = parser.parse_known_args()[0] -argsp = parser.parse_args() + # initialize config object + Config.init(paths.INSTALL_DIR) + Config.determine_version() -# initialize config object -Config.init(paths.INSTALL_DIR) -Config.determine_version() + collectProperties = CollectProperties() + collectProperties.collect() -collectProperties = CollectProperties() -collectProperties.collect() +if os.environ.get('jans_setup_branch') and not argsp.jans_setup_branch: + argsp.jans_setup_branch = os.environ['jans_setup_branch'] maven_base_url = 'https://jenkins.jans.io/maven/io/jans/' app_versions = { - "SETUP_BRANCH": argsp.setup_branch, + "SETUP_BRANCH": argsp.jans_setup_branch, "FLEX_BRANCH": argsp.flex_branch, "JANS_BRANCH": argsp.jans_branch, "JANS_APP_VERSION": "1.0.0", @@ -116,22 +143,24 @@ def __init__(self): self.twillo_fn = os.path.join(self.casa_dist_dir,'twilio.jar') self.py_lib_dir = '/opt/jans/python/libs/' + self.dbUtils.bind(force=True) + def download_files(self): print("Downloading components") - base.download(urljoin(maven_base_url, 'jans-config-api/plugins/admin-ui-plugin/{0}{1}/admin-ui-plugin-{0}{1}-distribution.jar'.format(app_versions['JANS_APP_VERSION'], app_versions['JANS_BUILD'])), self.admin_ui_plugin_source_path) - base.download('https://raw.githubusercontent.com/JanssenProject/jans/{}/jans-config-api/server/src/main/resources/log4j2.xml'.format(app_versions['JANS_BRANCH']), self.log4j2_path) - base.download('https://raw.githubusercontent.com/JanssenProject/jans/{}/jans-config-api/plugins/admin-ui-plugin/config/log4j2-adminui.xml'.format(app_versions['JANS_BRANCH']), self.log4j2_adminui_path) - base.download('https://github.com/GluuFederation/flex/archive/refs/heads/{}.zip'.format(app_versions['FLEX_BRANCH']), self.flex_path) - base.download('https://github.com/GluuFederation/casa/raw/gluu_cloud/extras/casa_web_resources.xml', self.casa_web_resources_fn) - base.download('https://maven.gluu.org/maven/org/gluu/casa/{0}/casa-{0}.war'.format(app_versions['CASA_VERSION']), self.casa_war_fn) - base.download('https://maven.gluu.org/maven/org/gluu/casa-config/{0}/casa-config-{0}.jar'.format(app_versions['CASA_VERSION']), self.casa_config_fn) - base.download('https://repo1.maven.org/maven2/com/twilio/sdk/twilio/{0}/twilio-{0}.jar'.format(app_versions['TWILIO_VERSION']), self.twillo_fn) - base.download('https://raw.githubusercontent.com/GluuFederation/casa/gluu_cloud/extras/Casa.py', self.casa_script_fn) - base.download('https://raw.githubusercontent.com/GluuFederation/casa/gluu_cloud/extras/casa-external_fido2.py', os.path.join(self.casa_dist_dir, 'pylib/casa-external_fido2.py')) - base.download('https://raw.githubusercontent.com/GluuFederation/casa/gluu_cloud/extras/casa-external_otp.py', os.path.join(self.casa_dist_dir, 'pylib/casa-external_otp.py')) - base.download('https://raw.githubusercontent.com/GluuFederation/casa/gluu_cloud/extras/casa-external_super_gluu.py', os.path.join(self.casa_dist_dir, 'pylib/casa-external_super_gluu.py')) - base.download('https://raw.githubusercontent.com/GluuFederation/casa/gluu_cloud/extras/casa-external_twilio_sms.py', os.path.join(self.casa_dist_dir, 'pylib/casa-external_twilio_sms.py')) - base.download('https://raw.githubusercontent.com/GluuFederation/casa/gluu_cloud/extras/casa-external_u2f.py', os.path.join(self.casa_dist_dir, 'pylib/casa-external_u2f.py')) + base.download(urljoin(maven_base_url, 'jans-config-api/plugins/admin-ui-plugin/{0}{1}/admin-ui-plugin-{0}{1}-distribution.jar'.format(app_versions['JANS_APP_VERSION'], app_versions['JANS_BUILD'])), self.admin_ui_plugin_source_path, verbose=True) + base.download('https://raw.githubusercontent.com/JanssenProject/jans/{}/jans-config-api/server/src/main/resources/log4j2.xml'.format(app_versions['JANS_BRANCH']), self.log4j2_path, verbose=True) + base.download('https://raw.githubusercontent.com/JanssenProject/jans/{}/jans-config-api/plugins/admin-ui-plugin/config/log4j2-adminui.xml'.format(app_versions['JANS_BRANCH']), self.log4j2_adminui_path, verbose=True) + base.download('https://github.com/GluuFederation/flex/archive/refs/heads/{}.zip'.format(app_versions['FLEX_BRANCH']), self.flex_path, verbose=True) + base.download('https://github.com/GluuFederation/casa/raw/gluu_cloud/extras/casa_web_resources.xml', self.casa_web_resources_fn, verbose=True) + base.download('https://maven.gluu.org/maven/org/gluu/casa/{0}/casa-{0}.war'.format(app_versions['CASA_VERSION']), self.casa_war_fn, verbose=True) + base.download('https://maven.gluu.org/maven/org/gluu/casa-config/{0}/casa-config-{0}.jar'.format(app_versions['CASA_VERSION']), self.casa_config_fn, verbose=True) + base.download('https://repo1.maven.org/maven2/com/twilio/sdk/twilio/{0}/twilio-{0}.jar'.format(app_versions['TWILIO_VERSION']), self.twillo_fn, verbose=True) + base.download('https://raw.githubusercontent.com/GluuFederation/flex/main/casa/extras/Casa.py', self.casa_script_fn, verbose=True) + base.download('https://raw.githubusercontent.com/GluuFederation/flex/main/casa/extras/casa-external_fido2.py', os.path.join(self.casa_dist_dir, 'pylib/casa-external_fido2.py'), verbose=True) + base.download('https://raw.githubusercontent.com/GluuFederation/flex/main/casa/extras/casa-external_otp.py', os.path.join(self.casa_dist_dir, 'pylib/casa-external_otp.py'), verbose=True) + base.download('https://raw.githubusercontent.com/GluuFederation/flex/main/casa/extras/casa-external_super_gluu.py', os.path.join(self.casa_dist_dir, 'pylib/casa-external_super_gluu.py'), verbose=True) + base.download('https://raw.githubusercontent.com/GluuFederation/flex/main/casa/extras/casa-external_twilio_sms.py', os.path.join(self.casa_dist_dir, 'pylib/casa-external_twilio_sms.py'), verbose=True) + base.download('https://raw.githubusercontent.com/GluuFederation/flex/main/casa/extras/casa-external_u2f.py', os.path.join(self.casa_dist_dir, 'pylib/casa-external_u2f.py'), verbose=True) def install_gluu_admin_ui(self): @@ -313,8 +342,7 @@ def main(): node_fn = 'node-{0}-linux-x64.tar.xz'.format(app_versions['NODE_VERSION']) node_path = os.path.join(Config.distAppFolder, node_fn) if not os.path.exists(node_path): - print("Downloading", node_fn) - base.download('https://nodejs.org/dist/{0}/node-{0}-linux-x64.tar.xz'.format(app_versions['NODE_VERSION']), node_path) + base.download('https://nodejs.org/dist/{0}/node-{0}-linux-x64.tar.xz'.format(app_versions['NODE_VERSION']), node_path, verbose=True) print("Installing node") node_installer.install() @@ -337,5 +365,9 @@ def main(): print("Browse https://{}/casa".format(Config.hostname)) if __name__ == "__main__": - main() + if argsp.shell: + code.interact(local=locals()) + sys.exit() + else: + main() From d235ef4410d7481e284c757016abf82bf135fb8a Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Fri, 25 Feb 2022 22:07:15 +0300 Subject: [PATCH 009/101] feat: flex-linux-setup argparse pkg issue --- .../flex_linux_setup/flex_setup.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/flex-linux-setup/flex_linux_setup/flex_setup.py b/flex-linux-setup/flex_linux_setup/flex_setup.py index eb198a572..b5b533754 100755 --- a/flex-linux-setup/flex_linux_setup/flex_setup.py +++ b/flex-linux-setup/flex_linux_setup/flex_setup.py @@ -38,7 +38,6 @@ sys.path.append(__STATIC_SETUP_DIR__) logs_dir = os.path.join(__STATIC_SETUP_DIR__, 'logs') -argsp = None if not os.path.exists(logs_dir): os.makedirs(logs_dir) @@ -60,18 +59,22 @@ parser.add_argument('--jans-branch', help="Jannsen github branch", default='main') parser.add_argument('-shell', help="Jannsen github branch", action='store_true') +from setup_app.utils import arg_parser + +arg_parser.add_to_me(parser) + +installed = False + if not os.path.exists('/etc/jans/conf/jans.properties'): - parser.add_to_setup_parser = True - from setup_app.utils import arg_parser + installed = True try: from jans_setup import jans_setup except ImportError: import jans_setup jans_setup.main() - argsp = arg_parser.get_parser() - +argsp = arg_parser.get_parser() from setup_app import static from setup_app.utils import base @@ -90,8 +93,7 @@ if not os.path.join(Config.outputFolder): os.makedirs(Config.outputFolder) -if not argsp: - argsp = parser.parse_known_args()[0] +if not installed: # initialize config object Config.init(paths.INSTALL_DIR) @@ -100,9 +102,6 @@ collectProperties = CollectProperties() collectProperties.collect() -if os.environ.get('jans_setup_branch') and not argsp.jans_setup_branch: - argsp.jans_setup_branch = os.environ['jans_setup_branch'] - maven_base_url = 'https://jenkins.jans.io/maven/io/jans/' app_versions = { "SETUP_BRANCH": argsp.jans_setup_branch, From 116b791b80544ac9fe9fd4266f662b18397bea19 Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Fri, 25 Feb 2022 23:49:09 +0300 Subject: [PATCH 010/101] fix: flex-linux-setup pass -yes to install.py --- flex-linux-setup/flex_linux_setup/flex_setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flex-linux-setup/flex_linux_setup/flex_setup.py b/flex-linux-setup/flex_linux_setup/flex_setup.py index b5b533754..793393378 100755 --- a/flex-linux-setup/flex_linux_setup/flex_setup.py +++ b/flex-linux-setup/flex_linux_setup/flex_setup.py @@ -33,6 +33,8 @@ install_url = 'https://raw.githubusercontent.com/JanssenProject/jans/{}/jans-linux-setup/jans_setup/install.py'.format(setup_branch) request.urlretrieve(install_url, 'install.py') install_cmd = 'python3 install.py --setup-branch={} --no-setup'.format(setup_branch) + if '-yes' in sys.argv: + install_cmd += ' -yes' print("Executing", install_cmd) os.system(install_cmd) sys.path.append(__STATIC_SETUP_DIR__) From 32b4f561f1474b1e4fdd5fddb58b3fd4b4f2ddf7 Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Mon, 28 Feb 2022 12:07:22 +0300 Subject: [PATCH 011/101] fix: flex-linux-setup restart jans-auth after casa --- flex-linux-setup/flex_linux_setup/flex_setup.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/flex-linux-setup/flex_linux_setup/flex_setup.py b/flex-linux-setup/flex_linux_setup/flex_setup.py index 793393378..03fd022ca 100755 --- a/flex-linux-setup/flex_linux_setup/flex_setup.py +++ b/flex-linux-setup/flex_linux_setup/flex_setup.py @@ -353,11 +353,13 @@ def main(): installer_obj.install_gluu_admin_ui() installer_obj.install_casa() - print("Restarting Jans Auth") - config_api_installer.restart('jans-auth') + print("Starting Casa") config_api_installer.start('casa') + print("Restarting Jans Auth") + config_api_installer.restart('jans-auth') + print("Restarting Janssen Config Api") config_api_installer.restart() From d97258a6ebc74171359c8449cd6f936bd5c9cae1 Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Mon, 28 Feb 2022 14:34:03 +0300 Subject: [PATCH 012/101] fix: flex-linux-setup import package --- flex-linux-setup/flex_linux_setup/flex_setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flex-linux-setup/flex_linux_setup/flex_setup.py b/flex-linux-setup/flex_linux_setup/flex_setup.py index 03fd022ca..69510750f 100755 --- a/flex-linux-setup/flex_linux_setup/flex_setup.py +++ b/flex-linux-setup/flex_linux_setup/flex_setup.py @@ -16,7 +16,8 @@ try: import jans_setup - sys.path.append(jans_setup.__path__[0]) + path_ = list(jans_setup.__path__) + sys.path.append(path_[0]) except ModuleNotFoundError: if os.path.exists(os.path.join(__STATIC_SETUP_DIR__, 'setup_app')): sys.path.append(__STATIC_SETUP_DIR__) From 1f2015bf78bb26ffb73ef026bca96496ab9c7d70 Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Wed, 2 Mar 2022 12:53:45 +0300 Subject: [PATCH 013/101] fix: flex-linux-setup get py_lib_dir from Config --- flex-linux-setup/flex_linux_setup/flex_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flex-linux-setup/flex_linux_setup/flex_setup.py b/flex-linux-setup/flex_linux_setup/flex_setup.py index 69510750f..603e18e62 100755 --- a/flex-linux-setup/flex_linux_setup/flex_setup.py +++ b/flex-linux-setup/flex_linux_setup/flex_setup.py @@ -143,7 +143,7 @@ def __init__(self): self.casa_config_fn = os.path.join(self.casa_dist_dir,'casa-config.jar') self.casa_script_fn = os.path.join(self.casa_dist_dir,'Casa.py') self.twillo_fn = os.path.join(self.casa_dist_dir,'twilio.jar') - self.py_lib_dir = '/opt/jans/python/libs/' + self.py_lib_dir = os.path.join(Config.jansOptPythonFolder, 'libs') self.dbUtils.bind(force=True) From 7fb45c5107e0585b47a95cfbc539b86caf52d4fb Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Fri, 4 Mar 2022 12:40:55 +0300 Subject: [PATCH 014/101] fix: flex-linux-setup change admin-ui-plugin name --- flex-linux-setup/flex_linux_setup/flex_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flex-linux-setup/flex_linux_setup/flex_setup.py b/flex-linux-setup/flex_linux_setup/flex_setup.py index 603e18e62..ae6af14f5 100755 --- a/flex-linux-setup/flex_linux_setup/flex_setup.py +++ b/flex-linux-setup/flex_linux_setup/flex_setup.py @@ -131,7 +131,7 @@ def __init__(self): self.gluu_admin_ui_source_path = os.path.join(Config.distJansFolder, 'gluu-admin-ui.zip') self.log4j2_adminui_path = os.path.join(Config.distJansFolder, 'log4j2-adminui.xml') self.log4j2_path = os.path.join(Config.distJansFolder, 'log4j2.xml') - self.admin_ui_plugin_source_path = os.path.join(Config.distJansFolder, 'admin-ui-plugin-distribution.jar') + self.admin_ui_plugin_source_path = os.path.join(Config.distJansFolder, 'admin-ui-plugin.jar') self.flex_path = os.path.join(Config.distJansFolder, 'flex.zip') self.source_dir = os.path.join(Config.outputFolder, 'admin-ui') self.flex_setup_dir = os.path.join(self.source_dir, 'flex-linux-setup') From d2ec21f25ed5ffc6f378ca9f6b61adf9acea91eb Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Fri, 4 Mar 2022 18:17:42 +0300 Subject: [PATCH 015/101] fix: flex-linux-setup update config-cli plugins --- .../flex_linux_setup/flex_setup.py | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/flex-linux-setup/flex_linux_setup/flex_setup.py b/flex-linux-setup/flex_linux_setup/flex_setup.py index ae6af14f5..936bddafe 100755 --- a/flex-linux-setup/flex_linux_setup/flex_setup.py +++ b/flex-linux-setup/flex_linux_setup/flex_setup.py @@ -7,7 +7,9 @@ import time import glob import code +import configparser +from pathlib import Path from urllib import request from urllib.parse import urljoin @@ -90,6 +92,7 @@ from setup_app.installers.config_api import ConfigApiInstaller from setup_app.installers.jetty import JettyInstaller from setup_app.installers.jans_auth import JansAuthInstaller +from setup_app.installers.jans_cli import JansCliInstaller Config.outputFolder = os.path.join(__STATIC_SETUP_DIR__, 'output') @@ -121,7 +124,7 @@ httpd_installer = HttpdInstaller() config_api_installer = ConfigApiInstaller() jansAuthInstaller = JansAuthInstaller() - +jans_cli_installer = JansCliInstaller() class flex_installer(JettyInstaller): @@ -196,13 +199,31 @@ def install_gluu_admin_ui(self): config_api_installer.check_clients([('role_based_client_id', '2000.')]) config_api_installer.renderTemplateInOut(self.admin_ui_config_properties_path, os.path.join(self.flex_setup_dir, 'templates'), config_api_installer.custom_config_dir) admin_ui_plugin_path = os.path.join(config_api_installer.libDir, os.path.basename(self.admin_ui_plugin_source_path)) - config_api_installer.web_app_xml_fn = os.path.join(config_api_installer.jetty_base, config_api_installer.service_name, 'webapps/jans-config-api.xml') config_api_installer.copyFile(self.admin_ui_plugin_source_path, config_api_installer.libDir) config_api_installer.add_extra_class(admin_ui_plugin_path) for logfn in (self.log4j2_adminui_path, self.log4j2_path): config_api_installer.copyFile(logfn, config_api_installer.custom_config_dir) + cli_config = Path(jans_cli_installer.config_ini_fn) + + current_plugins = [] + + if cli_config.exists(): + + config = configparser.ConfigParser() + config.read_file(cli_config.open()) + current_plugins = config['DEFAULT'].get('jca_plugins', '').split(',') + + plugins = config_api_installer.get_plugins() + + for plugin in plugins: + if not plugin in current_plugins: + current_plugins.append(plugin) + + config['DEFAULT']['jca_plugins'] = ','.join(current_plugins) + config.write(cli_config.open('w')) + cli_config.chmod(0o600) def install_casa(self): @@ -216,8 +237,7 @@ def install_casa(self): os.path.join(jans_auth_custom_lib_dir, os.path.basename(self.casa_config_fn)), os.path.join(jans_auth_custom_lib_dir, os.path.basename(self.twillo_fn)), ) - xml_fn = os.path.join(jans_auth_dir, 'webapps', jansAuthInstaller.service_name+'.xml') - jansAuthInstaller.add_extra_class(class_path, xml_fn) + jansAuthInstaller.add_extra_class(class_path) simple_auth_scr_inum = 'A51E-76DA' print("Enabling script", simple_auth_scr_inum) From 83d280e731197f732936d39d97de5d80f769de26 Mon Sep 17 00:00:00 2001 From: Mustafa Baser Date: Fri, 4 Mar 2022 19:13:59 +0300 Subject: [PATCH 016/101] fix: flex-linux-setup parser --- .../flex_linux_setup/flex_setup.py | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/flex-linux-setup/flex_linux_setup/flex_setup.py b/flex-linux-setup/flex_linux_setup/flex_setup.py index 936bddafe..66e02f1db 100755 --- a/flex-linux-setup/flex_linux_setup/flex_setup.py +++ b/flex-linux-setup/flex_linux_setup/flex_setup.py @@ -14,6 +14,14 @@ from urllib.parse import urljoin +def get_flex_setup_parser(): + parser = argparse.ArgumentParser(description="This script downloads Gluu Admin UI components and installs") + parser.add_argument('--jans-setup-branch', help="Jannsen setup github branch", default='main') + parser.add_argument('--flex-branch', help="Jannsen flex setup github branch", default='main') + parser.add_argument('--jans-branch', help="Jannsen github branch", default='main') + + return parser + __STATIC_SETUP_DIR__ = '/opt/jans/jans-setup/' try: @@ -24,15 +32,11 @@ if os.path.exists(os.path.join(__STATIC_SETUP_DIR__, 'setup_app')): sys.path.append(__STATIC_SETUP_DIR__) else: + argsp = get_flex_setup_parser().parse_known_args()[0] + print("Unable to locate jans-setup, installing ...") - # split args to find jans setup branch - arg_list = [] - for arg in sys.argv[1:]: - if '=' in arg: - arg_list.append(arg.split('=', maxsplit=1)) - sys_args = dict(arg_list) - - setup_branch = sys_args.get('--jans-setup-branch') or 'main' + + setup_branch = argsp.jans_setup_branch or 'main' install_url = 'https://raw.githubusercontent.com/JanssenProject/jans/{}/jans-linux-setup/jans_setup/install.py'.format(setup_branch) request.urlretrieve(install_url, 'install.py') install_cmd = 'python3 install.py --setup-branch={} --no-setup'.format(setup_branch) @@ -58,19 +62,11 @@ print('\033[0m') -parser = argparse.ArgumentParser(description="This script downloads Gluu Admin UI components and installs") -parser.add_argument('--jans-setup-branch', help="Jannsen setup github branch", default='main') -parser.add_argument('--flex-branch', help="Jannsen flex setup github branch", default='main') -parser.add_argument('--jans-branch', help="Jannsen github branch", default='main') -parser.add_argument('-shell', help="Jannsen github branch", action='store_true') - +parser = get_flex_setup_parser() from setup_app.utils import arg_parser - arg_parser.add_to_me(parser) - installed = False - if not os.path.exists('/etc/jans/conf/jans.properties'): installed = True try: From 1e66f1bcea52539ff9b0043733ff41de6871661c Mon Sep 17 00:00:00 2001 From: Jose Date: Sun, 6 Mar 2022 07:58:18 -0500 Subject: [PATCH 017/101] chore: remove/adjust files as required per #125 --- .../authnmethod/SecurityKeyExtension.java | 82 -- .../plugins/authnmethod/conf/U2FConfig.java | 28 - .../rs/SecurityKeyEnrollingWS.java | 196 ----- .../authnmethod/rs/SuperGluuEnrollingWS.java | 2 +- .../authnmethod/service/U2fService.java | 141 --- .../gluu/casa/timer/AuthnScriptsReloader.java | 2 +- .../casa/ui/vm/user/SecurityKeyViewModel.java | 283 ------ .../src/main/resources/labels/user.properties | 31 +- .../src/main/webapp/scripts/gluu/u2f-util.js | 41 - casa/app/src/main/webapp/scripts/u2f-api.js | 830 ------------------ casa/app/src/main/webapp/user/u2f-detail.zul | 123 --- casa/extras/enrollment-client/README.md | 4 +- casa/extras/enrollment-client/index.html | 3 +- casa/extras/enrollment-client/super_gluu.html | 4 +- casa/extras/enrollment-client/u2f-api.js | 830 ------------------ casa/extras/enrollment-client/u2f.html | 123 --- casa/installer/casa_cleanup.py | 216 ----- casa/installer/setup_casa.py | 391 --------- 18 files changed, 11 insertions(+), 3319 deletions(-) delete mode 100644 casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/SecurityKeyExtension.java delete mode 100644 casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/conf/U2FConfig.java delete mode 100644 casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/rs/SecurityKeyEnrollingWS.java delete mode 100644 casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/service/U2fService.java delete mode 100644 casa/app/src/main/java/org/gluu/casa/ui/vm/user/SecurityKeyViewModel.java delete mode 100644 casa/app/src/main/webapp/scripts/gluu/u2f-util.js delete mode 100644 casa/app/src/main/webapp/scripts/u2f-api.js delete mode 100644 casa/app/src/main/webapp/user/u2f-detail.zul delete mode 100644 casa/extras/enrollment-client/u2f-api.js delete mode 100644 casa/extras/enrollment-client/u2f.html delete mode 100755 casa/installer/casa_cleanup.py delete mode 100755 casa/installer/setup_casa.py diff --git a/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/SecurityKeyExtension.java b/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/SecurityKeyExtension.java deleted file mode 100644 index bcfaf42f0..000000000 --- a/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/SecurityKeyExtension.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.gluu.casa.plugins.authnmethod; - -import org.gluu.casa.credential.BasicCredential; -import org.gluu.casa.extension.AuthnMethod; -import org.gluu.casa.misc.Utils; -import org.gluu.casa.plugins.authnmethod.service.U2fService; -import org.pf4j.Extension; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -/** - * @author jgomer - */ -@Extension -public class SecurityKeyExtension implements AuthnMethod { - - public static final String ACR = "u2f"; - - private Logger logger = LoggerFactory.getLogger(getClass()); - - private U2fService u2fService; - - public SecurityKeyExtension() { - u2fService = Utils.managedBean(U2fService.class); - } - - public String getUINameKey() { - return "usr.u2f_label"; - } - - public String getAcr() { - return ACR; - } - - public String getPanelTitleKey() { - return "usr.u2f_title"; - } - - public String getPanelTextKey() { - return "usr.u2f_text"; - } - - public String getPanelButtonKey() { - return "usr.u2f_manage"; - } - - public String getPanelBottomTextKey() { - return "usr.u2f_buy_title"; - } - - public String getPageUrl() { - return "/user/u2f-detail.zul"; - } - - public List getEnrolledCreds(String id) { - - try { - return u2fService.getDevices(id, true).stream() - .map(dev -> new BasicCredential(dev.getNickName(), dev.getCreationDate().getTime())).collect(Collectors.toList()); - } catch (Exception e) { - logger.error(e.getMessage(), e); - return Collections.emptyList(); - } - } - - public int getTotalUserCreds(String id) { - return u2fService.getDevicesTotal(id, true); - } - - public void reloadConfiguration() { - u2fService.reloadConfiguration(); - } - - public boolean mayBe2faActivationRequisite() { - return Boolean.parseBoolean(u2fService.getScriptPropertyValue("2fa_requisite")); - } - -} diff --git a/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/conf/U2FConfig.java b/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/conf/U2FConfig.java deleted file mode 100644 index a6f0318a8..000000000 --- a/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/conf/U2FConfig.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.gluu.casa.plugins.authnmethod.conf; - -/** - * @author jgomer - */ -public class U2FConfig { - - private String appId; - - private String endpointUrl; - - public String getAppId() { - return appId; - } - - public void setAppId(String appId) { - this.appId = appId; - } - - public String getEndpointUrl() { - return endpointUrl; - } - - public void setEndpointUrl(String endpointUrl) { - this.endpointUrl = endpointUrl; - } - -} diff --git a/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/rs/SecurityKeyEnrollingWS.java b/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/rs/SecurityKeyEnrollingWS.java deleted file mode 100644 index 0d1c2f71e..000000000 --- a/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/rs/SecurityKeyEnrollingWS.java +++ /dev/null @@ -1,196 +0,0 @@ -package org.gluu.casa.plugins.authnmethod.rs; - -import com.fasterxml.jackson.databind.ObjectMapper; -import net.jodah.expiringmap.ExpiringMap; -import org.gluu.casa.core.PersistenceService; -import org.gluu.casa.core.UserService; -import org.gluu.casa.core.model.Person; -import org.gluu.casa.core.pojo.FidoDevice; -import org.gluu.casa.core.pojo.SecurityKey; -import org.gluu.casa.misc.Utils; -import org.gluu.casa.plugins.authnmethod.rs.status.u2f.FinishCode; -import org.gluu.casa.plugins.authnmethod.rs.status.u2f.RegisterMessageCode; -import org.gluu.casa.plugins.authnmethod.rs.status.u2f.RegistrationCode; -import org.gluu.casa.plugins.authnmethod.service.U2fService; -import org.gluu.casa.rest.ProtectedApi; -import org.slf4j.Logger; - -import javax.annotation.PostConstruct; -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import java.util.stream.Stream; - -/** - * @author jgomer - */ -@ApplicationScoped -//Disabled: u2f will be deprecated in favor of FIDO 2, see SecurityKey2EnrollingWS -//@Path("/enrollment/" + SecurityKeyExtension.ACR) -public class SecurityKeyEnrollingWS { - - private static final int TIME_WINDOW_DEFAULT = 2; - private static final int MAX_STORED_ENTRIES = 1000; //one thousand entries stored at most - - @Inject - private Logger logger; - - @Inject - private U2fService u2fService; - - @Inject - private PersistenceService persistenceService; - - @Inject - private UserService userService; - - private ObjectMapper mapper; - - private Map usersWithPendingRegistrations; - - private Map recentlyEnrolledDevices; - - @GET - @Path("registration-message") - @Produces(MediaType.APPLICATION_JSON) - @ProtectedApi - public Response getRegistrationMessage(@QueryParam("userid") String userId) { - - String request = null; - RegisterMessageCode result; - logger.trace("getRegistrationMessage WS operation called"); - - if (Utils.isEmpty(userId)) { - result = RegisterMessageCode.NO_USER_ID; - } else { - Person person = persistenceService.get(Person.class, persistenceService.getPersonDn(userId)); - if (person == null) { - result = RegisterMessageCode.UNKNOWN_USER_ID; - } else { - try { - String userName = person.getUid(); - String code = userService.generateRandEnrollmentCode(userId); - request = u2fService.generateJsonRegisterMessage(userName, code); - result = RegisterMessageCode.SUCCESS; - usersWithPendingRegistrations.put(userId, null); - } catch (Exception e) { - result = RegisterMessageCode.FAILED; - logger.error(e.getMessage(), e); - } - } - } - return result.getResponse(request); - - } - - @POST - @Path("registration/{userid}") - @Produces(MediaType.APPLICATION_JSON) - @ProtectedApi - public Response sendRegistrationResult(Map registrationResult, - @PathParam("userid") String userId) { - - RegistrationCode result; - SecurityKey newDevice = null; - logger.trace("sendRegistrationResult WS operation called"); - - if (usersWithPendingRegistrations.containsKey(userId)) { - usersWithPendingRegistrations.remove(userId); - - Person person = persistenceService.get(Person.class, persistenceService.getPersonDn(userId)); - if (person == null) { - result = RegistrationCode.UNKNOWN_USER_ID; - } else { - try { - String userName = person.getUid(); - String jsonStr = mapper.writeValueAsString(registrationResult); - String error = u2fService.getRegistrationResult(jsonStr); - - if (error == null) { - u2fService.finishRegistration(userName, jsonStr); - newDevice = u2fService.getLatestSecurityKey(userId, System.currentTimeMillis()); - if (newDevice == null){ - result = RegistrationCode.FAILED; - } else { - recentlyEnrolledDevices.put(newDevice.getId(), userId); - result = RegistrationCode.SUCCESS; - userService.cleanRandEnrollmentCode(userId); - } - } else { - result = RegistrationCode.FAILED; - } - } catch (Exception e) { - logger.error(e.getMessage(), e); - result = RegistrationCode.FAILED; - } - } - - } else { - result = RegistrationCode.NO_MATCH_OR_EXPIRED; - } - return result.getResponse(newDevice); - - } - - @GET - @Path("creds/{userid}/{id}") - @Produces(MediaType.APPLICATION_JSON) - @ProtectedApi - public Response getEnrollments() { - return Response.status(Response.Status.NOT_IMPLEMENTED).build(); - } - - @POST - @Path("creds/{userid}") - @Produces(MediaType.APPLICATION_JSON) - @ProtectedApi - public Response nameEnrollment(NamedCredential credential, - @PathParam("userid") String userId) { - - logger.trace("nameEnrollment WS operation called"); - String nickName = Optional.ofNullable(credential).map(NamedCredential::getNickName).orElse(null); - String deviceId = Optional.ofNullable(credential).map(NamedCredential::getKey).orElse(null); - - FinishCode result; - - if (Stream.of(nickName, deviceId).anyMatch(Utils::isEmpty)) { - result = FinishCode.MISSING_PARAMS; - } else if (!recentlyEnrolledDevices.containsKey(deviceId)) { - result = FinishCode.NO_MATCH_OR_EXPIRED; - } else { - FidoDevice dev = new FidoDevice(); - dev.setId(deviceId); - dev.setNickName(nickName); - - if (u2fService.updateDevice(dev)) { - result = FinishCode.SUCCESS; - recentlyEnrolledDevices.remove(deviceId); - } else { - result = FinishCode.FAILED; - } - } - return result.getResponse(); - - } - - @PostConstruct - private void init() { - - logger.trace("Service inited"); - mapper = new ObjectMapper(); - - usersWithPendingRegistrations = ExpiringMap.builder() - .maxSize(MAX_STORED_ENTRIES).expiration(TIME_WINDOW_DEFAULT, TimeUnit.MINUTES) - .asyncExpirationListener((userId, nothing) -> userService.cleanRandEnrollmentCode(userId.toString())) - .build(); - - recentlyEnrolledDevices = ExpiringMap.builder() - .maxSize(MAX_STORED_ENTRIES).expiration(TIME_WINDOW_DEFAULT, TimeUnit.MINUTES).build(); - } - -} diff --git a/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/rs/SuperGluuEnrollingWS.java b/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/rs/SuperGluuEnrollingWS.java index de3957723..115f9bd36 100644 --- a/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/rs/SuperGluuEnrollingWS.java +++ b/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/rs/SuperGluuEnrollingWS.java @@ -34,7 +34,7 @@ * @author jgomer */ @ApplicationScoped -@Path("/enrollment/" + SuperGluuExtension.ACR) +//@Path("/enrollment/" + SuperGluuExtension.ACR) public class SuperGluuEnrollingWS { private static final String BANNED_KEYS_PREFIX = "casa_blk_"; //Banned lookup keys diff --git a/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/service/U2fService.java b/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/service/U2fService.java deleted file mode 100644 index 2898b86a7..000000000 --- a/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/service/U2fService.java +++ /dev/null @@ -1,141 +0,0 @@ -package org.gluu.casa.plugins.authnmethod.service; - -import com.fasterxml.jackson.databind.JsonNode; - -import io.jans.as.client.fido.u2f.FidoU2fClientFactory; -import io.jans.as.client.fido.u2f.RegistrationRequestService; -import io.jans.as.client.fido.u2f.U2fConfigurationService; -import io.jans.as.model.fido.u2f.U2fConfiguration; -import io.jans.as.model.fido.u2f.protocol.RegisterRequestMessage; -import io.jans.as.model.fido.u2f.protocol.RegisterStatus; - -import org.gluu.casa.conf.MainSettings; -import org.gluu.casa.core.ConfigurationHandler; -import org.gluu.casa.core.pojo.SecurityKey; -import org.gluu.casa.plugins.authnmethod.SecurityKeyExtension; -import org.gluu.casa.plugins.authnmethod.conf.U2FConfig; -import org.slf4j.Logger; - -import java.util.*; -import javax.annotation.PostConstruct; -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; -import javax.inject.Named; - -/** - * An app. scoped bean that encapsulates logic related to management of registration requests for u2f devices - * @author jgomer - */ -@Named -@ApplicationScoped -public class U2fService extends FidoService { - - private static final String METADATA_URI = ".well-known/fido-configuration"; - - @Inject - private Logger logger; - - @Inject - private MainSettings settings; - - private U2FConfig conf; - private RegistrationRequestService registrationRequestService; - - @PostConstruct - private void inited() { - reloadConfiguration(); - } - - public void reloadConfiguration() { - - conf = new U2FConfig(); - conf.setEndpointUrl(String.format("%s/%s", persistenceService.getIssuerUrl(), METADATA_URI)); - - try { - props = persistenceService.getCustScriptConfigProperties(SecurityKeyExtension.ACR); - conf.setAppId(persistenceService.getCustScriptConfigProperties(ConfigurationHandler.DEFAULT_ACR).get("u2f_app_id")); - - logger.info("U2f settings found were: {}", mapper.writeValueAsString(conf)); - - U2fConfigurationService u2fCfgServ = FidoU2fClientFactory.instance().createMetaDataConfigurationService(conf.getEndpointUrl()); - U2fConfiguration metadataConf = u2fCfgServ.getMetadataConfiguration(); - registrationRequestService = FidoU2fClientFactory.instance().createRegistrationRequestService(metadataConf); - } catch (Exception e) { - logger.error(e.getMessage(), e); - } - - } - - public int getDevicesTotal(String userId, boolean active) { - return getDevicesTotal(conf.getAppId(), userId, active); - } - - public List getDevices(String userId, boolean active) { - return getSortedDevices(userId, active, conf.getAppId(), SecurityKey.class); - } - - /** - * Triggers a registration request to a U2F endpoint and outputs the request message returned by the service in form of JSON - * @param userName As required per io.jans.as.client.fido.u2f.RegistrationRequestService#startRegistration - * @param enrollmentCode A previously generated random code stored under user's LDAP entry - * @return Json string representation - * @throws Exception Network problem, De/Serialization error, ... - */ - public String generateJsonRegisterMessage(String userName, String enrollmentCode) throws Exception { - RegisterRequestMessage message = registrationRequestService.startRegistration(userName, conf.getAppId(), null, enrollmentCode); - logger.info("Beginning registration start with uid={}, app_id={}", userName, conf.getAppId()); - return mapper.writeValueAsString(message); - } - - /** - * Executes the finish registration step of the U2F service - * @param userName As required per io.jans.as.client.fido.u2f.RegistrationRequestService#finishRegistration - * @param response This is the Json response obtained in the web browser after calling the u2f.register function in Javascript - */ - public void finishRegistration(String userName, String response) { - //first parameter is not used in current implementation, see: io.jans.as.server.ws.rs.fido.u2f.U2fRegistrationWS#finishRegistration - RegisterStatus status = registrationRequestService.finishRegistration(userName, response); - logger.info("Response of finish registration: {}", status.getStatus()); - } - - public String getRegistrationResult(String jsonString) throws Exception { - - String value = null; - JsonNode tree = mapper.readTree(jsonString); - - logger.info("Finished registration start with response: {}", jsonString); - JsonNode tmp = tree.get("errorCode"); - - if (tmp != null) { - try { - int code = tmp.asInt(); - if (code > 0) { - value = U2fClientCodes.get(code).toString(); - logger.error("Registration failed with error: {}", value); - value = value.toLowerCase(); - } - } catch (Exception e) { - logger.error(e.getMessage(), e); - value = e.getMessage(); - } - } - return value; - - } - - public SecurityKey getLatestSecurityKey(String userId, long time) { - - SecurityKey sk = null; - try { - sk = getLatestFidoDevice(userId, time, conf.getAppId(), SecurityKey.class); - if (sk != null && sk.getNickName() != null) { - sk = null; //should have no name - } - } catch (Exception e) { - logger.error(e.getMessage(), e); - } - return sk; - - } - -} diff --git a/casa/app/src/main/java/org/gluu/casa/timer/AuthnScriptsReloader.java b/casa/app/src/main/java/org/gluu/casa/timer/AuthnScriptsReloader.java index 09cfe5437..0ffe3d308 100644 --- a/casa/app/src/main/java/org/gluu/casa/timer/AuthnScriptsReloader.java +++ b/casa/app/src/main/java/org/gluu/casa/timer/AuthnScriptsReloader.java @@ -37,7 +37,7 @@ public class AuthnScriptsReloader extends JobListenerSupport { private static final String LOCATION_PATH_PROPERTY = "location_path"; private static final String FILENAME_TEMPLATE = "casa-external_{0}.py"; private static final List NOT_REPLACEABLE_SCRIPTS = Arrays.asList(SecurityKey2Extension.ACR, - SecurityKeyExtension.ACR, OTPExtension.ACR, SuperGluuExtension.ACR, OTPTwilioExtension.ACR, OTPSmppExtension.ACR); + OTPExtension.ACR, SuperGluuExtension.ACR, OTPTwilioExtension.ACR, OTPSmppExtension.ACR); @Inject private Logger logger; diff --git a/casa/app/src/main/java/org/gluu/casa/ui/vm/user/SecurityKeyViewModel.java b/casa/app/src/main/java/org/gluu/casa/ui/vm/user/SecurityKeyViewModel.java deleted file mode 100644 index a005f8a86..000000000 --- a/casa/app/src/main/java/org/gluu/casa/ui/vm/user/SecurityKeyViewModel.java +++ /dev/null @@ -1,283 +0,0 @@ -package org.gluu.casa.ui.vm.user; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.gluu.casa.core.pojo.BrowserInfo; -import org.gluu.casa.core.pojo.SecurityKey; -import org.gluu.casa.ui.UIUtils; -import org.gluu.casa.misc.Utils; -import org.gluu.casa.plugins.authnmethod.SecurityKeyExtension; -import org.gluu.casa.plugins.authnmethod.service.U2fService; -import org.zkoss.bind.BindUtils; -import org.zkoss.bind.annotation.*; -import org.zkoss.json.JavaScriptValue; -import org.zkoss.util.Pair; -import org.zkoss.util.resource.Labels; -import org.zkoss.zk.au.out.AuInvoke; -import org.zkoss.zk.ui.Component; -import org.zkoss.zk.ui.event.Event; -import org.zkoss.zk.ui.event.Events; -import org.zkoss.zk.ui.select.Selectors; -import org.zkoss.zk.ui.select.annotation.Listen; -import org.zkoss.zk.ui.select.annotation.WireVariable; -import org.zkoss.zk.ui.util.Clients; -import org.zkoss.zul.Messagebox; - -import java.util.List; - -/** - * Created by jgomer on 2017-07-23. - * This is the ViewModel of page u2f-detail.zul. It controls the CRUD of security keys - */ -public class SecurityKeyViewModel extends UserViewModel { - - private static final int REGISTRATION_TIMEOUT = 8000; - - private Logger logger = LogManager.getLogger(getClass()); - - @WireVariable - private U2fService u2fService; - - private ObjectMapper mapper; - private SecurityKey newDevice; - private List devices; - - private boolean uiAwaiting; - private boolean uiEnrolled; - private String editingId; - - private String u2fSupportMessage; - private boolean u2fMayBeSupported; - - public boolean isUiAwaiting() { - return uiAwaiting; - } - - public boolean isUiEnrolled() { - return uiEnrolled; - } - - public String getEditingId() { - return editingId; - } - - public String getU2fSupportMessage() { - return u2fSupportMessage; - } - - public boolean isU2fMayBeSupported() { - return u2fMayBeSupported; - } - - public SecurityKey getNewDevice() { - return newDevice; - } - - public List getDevices() { - return devices; - } - - public void setEditingId(String editingId) { - this.editingId = editingId; - } - - public void setNewDevice(SecurityKey newDevice) { - this.newDevice = newDevice; - } - - @Init(superclass = true) - public void childInit() throws Exception { - mapper = new ObjectMapper(); - newDevice = new SecurityKey(); - devices = u2fService.getDevices(user.getId(), true); - checkU2fSupport(); - } - - @AfterCompose - public void afterCompose(@ContextParam(ContextType.VIEW) Component view) { - Selectors.wireEventListeners(view, this); - } - - public void triggerU2fRegisterRequest() { - try { - uiAwaiting = true; - BindUtils.postNotifyChange(this, "uiAwaiting"); - String jsonRequest = u2fService.generateJsonRegisterMessage(user.getUserName(), userService.generateRandEnrollmentCode(user.getId())); - - //Notify browser to exec proper function - UIUtils.showMessageUI(Clients.NOTIFICATION_TYPE_INFO, Labels.getLabel("usr.u2f_touch")); - Clients.response(new AuInvoke("triggerU2fRegistration", new JavaScriptValue(jsonRequest), REGISTRATION_TIMEOUT)); - } catch (Exception e) { - UIUtils.showMessageUI(false); - logger.error(e.getMessage(), e); - } - - } - - @Listen("onData=#readyButton") - public void notified(Event event) throws Exception { - - String jsonStr = mapper.writeValueAsString(event.getData()); - String error = u2fService.getRegistrationResult(jsonStr); - - if (error == null) { - u2fService.finishRegistration(user.getUserName(), jsonStr); - //To know exactly which entry is, we pass the current timestamp so we can pick the most suitable - //entry by inspecting the creationDate attribute among all existing entries - newDevice = u2fService.getLatestSecurityKey(user.getId(), System.currentTimeMillis()); - - if (newDevice != null) { - uiEnrolled = true; - BindUtils.postNotifyChange(this, "uiEnrolled"); - } else { - UIUtils.showMessageUI(false); - logger.error(Labels.getLabel("app.finish_registration_error")); - } - } else { - UIUtils.showMessageUI(false, Labels.getLabel("general.error.detailed", new String[] { error })); - } - - uiAwaiting = false; - BindUtils.postNotifyChange(this, "uiAwaiting"); - userService.cleanRandEnrollmentCode(user.getId()); - - } - - @NotifyChange({"uiEnrolled", "newDevice", "devices"}) - public void add() { - - if (Utils.isNotEmpty(newDevice.getNickName())) { - try { - u2fService.updateDevice(newDevice); - devices.add(newDevice); - UIUtils.showMessageUI(true, Labels.getLabel("usr.enroll.success")); - userService.notifyEnrollment(user, SecurityKeyExtension.ACR); - } catch (Exception e) { - UIUtils.showMessageUI(false, Labels.getLabel("usr.error_updating")); - logger.error(e.getMessage(), e); - } - resetAddSettings(); - } - - } - - @NotifyChange({"uiEnrolled", "newDevice"}) - public void cancel() { - - boolean success; - try { - /* - Remove the recently enrolled key. This is so because once the user touches his key button, jans-auth-server creates the - corresponding entry in LDAP, and if the user regrets adding the current key by not supplying a nickname - (and thus pressing cancel), we need to be obliterate the entry - */ - success = u2fService.removeDevice(newDevice); - } catch (Exception e) { - success = false; - logger.error(e.getMessage(), e); - } - if (!success) { - UIUtils.showMessageUI(false); - } - resetAddSettings(); - - } - - @NotifyChange({"editingId", "newDevice"}) - public void prepareForUpdate(SecurityKey dev) { - //This will make the modal window to become visible - editingId = dev.getId(); - newDevice = new SecurityKey(); - newDevice.setNickName(dev.getNickName()); - } - - @NotifyChange({"editingId", "newDevice"}) - public void cancelUpdate(Event event) { - newDevice.setNickName(null); - editingId = null; - if (event != null && event.getName().equals(Events.ON_CLOSE)) { - event.stopPropagation(); - } - } - - @NotifyChange({"devices", "editingId", "newDevice"}) - public void update() { - - String nick = newDevice.getNickName(); - if (Utils.isNotEmpty(nick)) { - int i = Utils.firstTrue(devices, dev -> dev.getId().equals(editingId)); - SecurityKey dev = devices.get(i); - dev.setNickName(nick); - cancelUpdate(null); - - try { - u2fService.updateDevice(dev); - UIUtils.showMessageUI(true); - } catch (Exception e) { - UIUtils.showMessageUI(false); - logger.error(e.getMessage(), e); - } - } - - } - - public void delete(SecurityKey device) { - - String resetMessages = resetPreferenceMessage(SecurityKeyExtension.ACR, devices.size()); - boolean reset = resetMessages != null; - Pair delMessages = getDeleteMessages(device.getNickName(), resetMessages); - - Messagebox.show(delMessages.getY(), delMessages.getX(), Messagebox.YES | Messagebox.NO, - reset ? Messagebox.EXCLAMATION : Messagebox.QUESTION, - event -> { - if (Messagebox.ON_YES.equals(event.getName())) { - try { - devices.remove(device); - boolean success = u2fService.removeDevice(device); - - if (success) { - if (reset) { - userService.turn2faOff(user); - } - //trigger refresh (this method is asynchronous...) - BindUtils.postNotifyChange(SecurityKeyViewModel.this, "devices"); - } else { - devices.add(device); - } - UIUtils.showMessageUI(success); - } catch (Exception e) { - UIUtils.showMessageUI(false); - logger.error(e.getMessage(), e); - } - } - }); - } - - private void checkU2fSupport() { - //Assume u2f is not supported - u2fMayBeSupported = false; - try { - BrowserInfo binfo = getBrowserInfo(); - String name = binfo.getName().toLowerCase(); - int browserVer = binfo.getMainVersion(); - - //I can guarantee it only works in the following versions, however older browsers might work too (with some - //config flag tweaking or plugin installation) - u2fMayBeSupported = (name.contains("chrome") && browserVer >= 70) || (name.contains("opera") && browserVer >= 57) - || (name.contains("safari") && browserVer > 10) || (name.contains("firefox") && browserVer >= 71); - - if (u2fMayBeSupported && name.contains("safari")) { - u2fSupportMessage = Labels.getLabel("usr.u2f_unsupported_safari"); - } - } catch (Exception e) { - logger.error(e.getMessage(), e); - } - } - - private void resetAddSettings() { - uiEnrolled = false; - newDevice = new SecurityKey(); - } - -} diff --git a/casa/app/src/main/resources/labels/user.properties b/casa/app/src/main/resources/labels/user.properties index 630c6e7ce..344eba478 100644 --- a/casa/app/src/main/resources/labels/user.properties +++ b/casa/app/src/main/resources/labels/user.properties @@ -22,15 +22,14 @@ usr.mfa_notenough=You cannot turn on 2FA until you register at least {0} credent #fido 2 security keys usr.fido2_label=Security key and Platform Authenticators usr.fido2_title=Security Keys and built-in Platform Authenticators -usr.fido2_text=Protect your online accounts against unauthorized access by using 2 factor authentication with Security keys ( USB / NFC) and built-in Platform authenticators (Apple's Touch ID). -usr.fido2_buy_title=Buy keys from Amazon or Yubico +usr.fido2_text=Protect your online accounts against unauthorized access by using 2 factor authentication with Security keys (USB / NFC) and built-in Platform authenticators (Apple's Touch ID). +usr.fido2_buy_title=Buy keys from Amazon or Yubico usr.fido2_buy_link1=https://www.amazon.com/fido2/s?k=fido2 +usr.fido2_buy_link2=https://www.yubico.com/store usr.fido2_label.security.key=Security key usr.fido2_title.security.key=Security Keys and built-in Platform Authenticators -usr.fido2_text.security.key=Protect your online accounts against unauthorized access by using 2 factor authentication with Security keys ( USB / NFC) and built-in Platform authenticators (Apple's Touch ID). -usr.fido2_buy_title.security.key=Buy keys from Amazon or Yubico -usr.fido2_buy_link1.security.key=https://www.amazon.com/fido2/s?k=fido2 +usr.fido2_text.security.key=Protect your online accounts against unauthorized access by using 2 factor authentication with Security keys (USB / NFC) and built-in Platform authenticators (Apple's Touch ID). usr.fido2_manage=Manage security keys usr.fido2_add=Register a security key @@ -55,28 +54,6 @@ Security keys are apparently not supported by your browser. If your enrollment a version of Chrome, Opera, Firefox or Edge. } -#u2f security keys -usr.u2f_label=Security key -usr.u2f_title=U2F Security Keys -usr.u2f_text=Security keys are the most secure form of authentication available online. Most U2F tokens only support USB; newer tokens support NFC also. -usr.u2f_buy_title=Buy U2F keys from Amazon or Yubico -usr.u2f_buy_link1=https://www.amazon.com/u2f-security-key/s?ie=UTF8&page=1&rh=i%3Aaps%2Ck%3Au2f%20security%20key -usr.u2f_buy_link2=https://www.yubico.com/store -#https://www.amazon.com/FIDO-U2F-Security-Key/s?ie=UTF8&page=1&rh=i%3Aaps%2Ck%3A%22FIDO%20U2F%20Security%20Key%22 -usr.u2f_manage=Manage security keys -usr.u2f_add=Add a security key -usr.u2f_touch=Touch your key button -usr.u2f_pressready=Insert your security key and press the "${general.ready}" button below -usr.u2f_edit=Change nickname of your security key -usr.u2f_del=Remove this key from your credentials -usr.u2f_mobile_unsupported=You can only enroll security keys on a desktop device - -usr.u2f_unsupported_browser=This feature is not available in your browser. Please use a recent version of Opera, Firefox or Chrome. -usr.u2f_unsupported_safari={ -You can use this feature in Safari only if the \ -Safari FIDO U2F plugin is installed. -} - #super gluu usr.supergluu_label=Super Gluu device usr.supergluu_title=Super Gluu Devices diff --git a/casa/app/src/main/webapp/scripts/gluu/u2f-util.js b/casa/app/src/main/webapp/scripts/gluu/u2f-util.js deleted file mode 100644 index 27d4c5a95..000000000 --- a/casa/app/src/main/webapp/scripts/gluu/u2f-util.js +++ /dev/null @@ -1,41 +0,0 @@ -var register_request = null; -var register_timeout; - -//Special widget to send data to server directly -var widget; - -//This is called when the ready button is pushed -function initialize(wgt){ - if (!widget) - widget=wgt; -} - -//Triggers the u2f once some time has passed (allowing users to plug the security key to the USB port) -function triggerU2fRegistration(req, timeout){ - register_request=req; - register_timeout=timeout; - //Wait half a second to start registration - setTimeout(startRegistration, 500); -} - -//Performs u2f registration -function startRegistration() { - //The key should start blinking upon this call. In FF this is not always true ... I've faced cases where - //30 seconds elapse :( - u2fApi.register(register_request.registerRequests, Math.floor(register_timeout/1000)) //expects seconds - .then(function (data) { - sendBack(data); - }) - .catch(function (err) { - sendBack({ errorCode: err.metaData.code }); - }) -} - -//Captures the response of the registration start and sends it to backend as is -function sendBack(obj){ - zAu.send(new zk.Event(widget, "onData", obj, {toServer:true})); -} - -function prepareAlert() { - alertRef = $('#feedback-key-edit'); -} diff --git a/casa/app/src/main/webapp/scripts/u2f-api.js b/casa/app/src/main/webapp/scripts/u2f-api.js deleted file mode 100644 index 37f49dc0d..000000000 --- a/casa/app/src/main/webapp/scripts/u2f-api.js +++ /dev/null @@ -1,830 +0,0 @@ -(function () { - 'use strict'; - - //Copyright 2014-2015 Google Inc. All rights reserved. - - //Use of this source code is governed by a BSD-style - //license that can be found in the LICENSE file or at - //https://developers.google.com/open-source/licenses/bsd - - /** - * @fileoverview The U2F api. - */ - // 'use strict'; - - - /** - * Namespace for the U2F api. - * @type {Object} - */ - var u2f = u2f || {}; - - const chromeApi = u2f; // Adaptation for u2f-api package - - /** - * FIDO U2F Javascript API Version - * @number - */ - var js_api_version; - - /** - * The U2F extension id - * @const {string} - */ - // The Chrome packaged app extension ID. - // Uncomment this if you want to deploy a server instance that uses - // the package Chrome app and does not require installing the U2F Chrome extension. - u2f.EXTENSION_ID = 'kmendfapggjehodndflmmgagdbamhnfd'; - // The U2F Chrome extension ID. - // Uncomment this if you want to deploy a server instance that uses - // the U2F Chrome extension to authenticate. - // u2f.EXTENSION_ID = 'pfboblefjcgdjicmnffhdgionmgcdmne'; - - - /** - * Message types for messsages to/from the extension - * @const - * @enum {string} - */ - u2f.MessageTypes = { - 'U2F_REGISTER_REQUEST': 'u2f_register_request', - 'U2F_REGISTER_RESPONSE': 'u2f_register_response', - 'U2F_SIGN_REQUEST': 'u2f_sign_request', - 'U2F_SIGN_RESPONSE': 'u2f_sign_response', - 'U2F_GET_API_VERSION_REQUEST': 'u2f_get_api_version_request', - 'U2F_GET_API_VERSION_RESPONSE': 'u2f_get_api_version_response' - }; - - - /** - * Response status codes - * @const - * @enum {number} - */ - u2f.ErrorCodes = { - 'OK': 0, - 'OTHER_ERROR': 1, - 'BAD_REQUEST': 2, - 'CONFIGURATION_UNSUPPORTED': 3, - 'DEVICE_INELIGIBLE': 4, - 'TIMEOUT': 5 - }; - - - //Low level MessagePort API support - - /** - * Sets up a MessagePort to the U2F extension using the - * available mechanisms. - * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback - */ - u2f.getMessagePort = function(callback) { - if (typeof chrome != 'undefined' && chrome.runtime) { - // The actual message here does not matter, but we need to get a reply - // for the callback to run. Thus, send an empty signature request - // in order to get a failure response. - var msg = { - type: u2f.MessageTypes.U2F_SIGN_REQUEST, - signRequests: [] - }; - chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function() { - if (!chrome.runtime.lastError) { - // We are on a whitelisted origin and can talk directly - // with the extension. - u2f.getChromeRuntimePort_(callback); - } else { - // chrome.runtime was available, but we couldn't message - // the extension directly, use iframe - u2f.getIframePort_(callback); - } - }); - } else if (u2f.isAndroidChrome_()) { - u2f.getAuthenticatorPort_(callback); - } else if (u2f.isIosChrome_()) { - u2f.getIosPort_(callback); - } else { - // chrome.runtime was not available at all, which is normal - // when this origin doesn't have access to any extensions. - u2f.getIframePort_(callback); - } - }; - - /** - * Detect chrome running on android based on the browser's useragent. - * @private - */ - u2f.isAndroidChrome_ = function() { - var userAgent = navigator.userAgent; - return userAgent.indexOf('Chrome') != -1 && - userAgent.indexOf('Android') != -1; - }; - - /** - * Detect chrome running on iOS based on the browser's platform. - * @private - */ - u2f.isIosChrome_ = function() { - return ["iPhone", "iPad", "iPod"].indexOf(navigator.platform) > -1; - }; - - /** - * Connects directly to the extension via chrome.runtime.connect. - * @param {function(u2f.WrappedChromeRuntimePort_)} callback - * @private - */ - u2f.getChromeRuntimePort_ = function(callback) { - var port = chrome.runtime.connect(u2f.EXTENSION_ID, - {'includeTlsChannelId': true}); - setTimeout(function() { - callback(new u2f.WrappedChromeRuntimePort_(port)); - }, 0); - }; - - /** - * Return a 'port' abstraction to the Authenticator app. - * @param {function(u2f.WrappedAuthenticatorPort_)} callback - * @private - */ - u2f.getAuthenticatorPort_ = function(callback) { - setTimeout(function() { - callback(new u2f.WrappedAuthenticatorPort_()); - }, 0); - }; - - /** - * Return a 'port' abstraction to the iOS client app. - * @param {function(u2f.WrappedIosPort_)} callback - * @private - */ - u2f.getIosPort_ = function(callback) { - setTimeout(function() { - callback(new u2f.WrappedIosPort_()); - }, 0); - }; - - /** - * A wrapper for chrome.runtime.Port that is compatible with MessagePort. - * @param {Port} port - * @constructor - * @private - */ - u2f.WrappedChromeRuntimePort_ = function(port) { - this.port_ = port; - }; - - /** - * Format and return a sign request compliant with the JS API version supported by the extension. - * @param {Array} signRequests - * @param {number} timeoutSeconds - * @param {number} reqId - * @return {Object} - */ - u2f.formatSignRequest_ = - function(appId, challenge, registeredKeys, timeoutSeconds, reqId) { - if (js_api_version === undefined || js_api_version < 1.1) { - // Adapt request to the 1.0 JS API - var signRequests = []; - for (var i = 0; i < registeredKeys.length; i++) { - signRequests[i] = { - version: registeredKeys[i].version, - challenge: challenge, - keyHandle: registeredKeys[i].keyHandle, - appId: appId - }; - } - return { - type: u2f.MessageTypes.U2F_SIGN_REQUEST, - signRequests: signRequests, - timeoutSeconds: timeoutSeconds, - requestId: reqId - }; - } - // JS 1.1 API - return { - type: u2f.MessageTypes.U2F_SIGN_REQUEST, - appId: appId, - challenge: challenge, - registeredKeys: registeredKeys, - timeoutSeconds: timeoutSeconds, - requestId: reqId - }; - }; - - /** - * Format and return a register request compliant with the JS API version supported by the extension.. - * @param {Array} signRequests - * @param {Array} signRequests - * @param {number} timeoutSeconds - * @param {number} reqId - * @return {Object} - */ - u2f.formatRegisterRequest_ = - function(appId, registeredKeys, registerRequests, timeoutSeconds, reqId) { - if (js_api_version === undefined || js_api_version < 1.1) { - // Adapt request to the 1.0 JS API - for (var i = 0; i < registerRequests.length; i++) { - registerRequests[i].appId = appId; - } - var signRequests = []; - for (var i = 0; i < registeredKeys.length; i++) { - signRequests[i] = { - version: registeredKeys[i].version, - challenge: registerRequests[0], - keyHandle: registeredKeys[i].keyHandle, - appId: appId - }; - } - return { - type: u2f.MessageTypes.U2F_REGISTER_REQUEST, - signRequests: signRequests, - registerRequests: registerRequests, - timeoutSeconds: timeoutSeconds, - requestId: reqId - }; - } - // JS 1.1 API - return { - type: u2f.MessageTypes.U2F_REGISTER_REQUEST, - appId: appId, - registerRequests: registerRequests, - registeredKeys: registeredKeys, - timeoutSeconds: timeoutSeconds, - requestId: reqId - }; - }; - - - /** - * Posts a message on the underlying channel. - * @param {Object} message - */ - u2f.WrappedChromeRuntimePort_.prototype.postMessage = function(message) { - this.port_.postMessage(message); - }; - - - /** - * Emulates the HTML 5 addEventListener interface. Works only for the - * onmessage event, which is hooked up to the chrome.runtime.Port.onMessage. - * @param {string} eventName - * @param {function({data: Object})} handler - */ - u2f.WrappedChromeRuntimePort_.prototype.addEventListener = - function(eventName, handler) { - var name = eventName.toLowerCase(); - if (name == 'message' || name == 'onmessage') { - this.port_.onMessage.addListener(function(message) { - // Emulate a minimal MessageEvent object - handler({'data': message}); - }); - } else { - console.error('WrappedChromeRuntimePort only supports onMessage'); - } - }; - - /** - * Wrap the Authenticator app with a MessagePort interface. - * @constructor - * @private - */ - u2f.WrappedAuthenticatorPort_ = function() { - this.requestId_ = -1; - this.requestObject_ = null; - }; - - /** - * Launch the Authenticator intent. - * @param {Object} message - */ - u2f.WrappedAuthenticatorPort_.prototype.postMessage = function(message) { - var intentUrl = - u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ + - ';S.request=' + encodeURIComponent(JSON.stringify(message)) + - ';end'; - document.location = intentUrl; - }; - - /** - * Tells what type of port this is. - * @return {String} port type - */ - u2f.WrappedAuthenticatorPort_.prototype.getPortType = function() { - return "WrappedAuthenticatorPort_"; - }; - - - /** - * Emulates the HTML 5 addEventListener interface. - * @param {string} eventName - * @param {function({data: Object})} handler - */ - u2f.WrappedAuthenticatorPort_.prototype.addEventListener = function(eventName, handler) { - var name = eventName.toLowerCase(); - if (name == 'message') { - var self = this; - /* Register a callback to that executes when - * chrome injects the response. */ - window.addEventListener( - 'message', self.onRequestUpdate_.bind(self, handler), false); - } else { - console.error('WrappedAuthenticatorPort only supports message'); - } - }; - - /** - * Callback invoked when a response is received from the Authenticator. - * @param function({data: Object}) callback - * @param {Object} message message Object - */ - u2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ = - function(callback, message) { - var messageObject = JSON.parse(message.data); - var intentUrl = messageObject['intentURL']; - - var errorCode = messageObject['errorCode']; - var responseObject = null; - if (messageObject.hasOwnProperty('data')) { - responseObject = /** @type {Object} */ ( - JSON.parse(messageObject['data'])); - } - - callback({'data': responseObject}); - }; - - /** - * Base URL for intents to Authenticator. - * @const - * @private - */ - u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ = - 'intent:#Intent;action=com.google.android.apps.authenticator.AUTHENTICATE'; - - /** - * Wrap the iOS client app with a MessagePort interface. - * @constructor - * @private - */ - u2f.WrappedIosPort_ = function() {}; - - /** - * Launch the iOS client app request - * @param {Object} message - */ - u2f.WrappedIosPort_.prototype.postMessage = function(message) { - var str = JSON.stringify(message); - var url = "u2f://auth?" + encodeURI(str); - location.replace(url); - }; - - /** - * Tells what type of port this is. - * @return {String} port type - */ - u2f.WrappedIosPort_.prototype.getPortType = function() { - return "WrappedIosPort_"; - }; - - /** - * Emulates the HTML 5 addEventListener interface. - * @param {string} eventName - * @param {function({data: Object})} handler - */ - u2f.WrappedIosPort_.prototype.addEventListener = function(eventName, handler) { - var name = eventName.toLowerCase(); - if (name !== 'message') { - console.error('WrappedIosPort only supports message'); - } - }; - - /** - * Sets up an embedded trampoline iframe, sourced from the extension. - * @param {function(MessagePort)} callback - * @private - */ - u2f.getIframePort_ = function(callback) { - // Create the iframe - var iframeOrigin = 'chrome-extension://' + u2f.EXTENSION_ID; - var iframe = document.createElement('iframe'); - iframe.src = iframeOrigin + '/u2f-comms.html'; - iframe.setAttribute('style', 'display:none'); - document.body.appendChild(iframe); - - var channel = new MessageChannel(); - var ready = function(message) { - if (message.data == 'ready') { - channel.port1.removeEventListener('message', ready); - callback(channel.port1); - } else { - console.error('First event on iframe port was not "ready"'); - } - }; - channel.port1.addEventListener('message', ready); - channel.port1.start(); - - iframe.addEventListener('load', function() { - // Deliver the port to the iframe and initialize - iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]); - }); - }; - - - //High-level JS API - - /** - * Default extension response timeout in seconds. - * @const - */ - u2f.EXTENSION_TIMEOUT_SEC = 30; - - /** - * A singleton instance for a MessagePort to the extension. - * @type {MessagePort|u2f.WrappedChromeRuntimePort_} - * @private - */ - u2f.port_ = null; - - /** - * Callbacks waiting for a port - * @type {Array} - * @private - */ - u2f.waitingForPort_ = []; - - /** - * A counter for requestIds. - * @type {number} - * @private - */ - u2f.reqCounter_ = 0; - - /** - * A map from requestIds to client callbacks - * @type {Object.} - * @private - */ - u2f.callbackMap_ = {}; - - /** - * Creates or retrieves the MessagePort singleton to use. - * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback - * @private - */ - u2f.getPortSingleton_ = function(callback) { - if (u2f.port_) { - callback(u2f.port_); - } else { - if (u2f.waitingForPort_.length == 0) { - u2f.getMessagePort(function(port) { - u2f.port_ = port; - u2f.port_.addEventListener('message', - /** @type {function(Event)} */ (u2f.responseHandler_)); - - // Careful, here be async callbacks. Maybe. - while (u2f.waitingForPort_.length) - u2f.waitingForPort_.shift()(u2f.port_); - }); - } - u2f.waitingForPort_.push(callback); - } - }; - - /** - * Handles response messages from the extension. - * @param {MessageEvent.} message - * @private - */ - u2f.responseHandler_ = function(message) { - var response = message.data; - var reqId = response['requestId']; - if (!reqId || !u2f.callbackMap_[reqId]) { - console.error('Unknown or missing requestId in response.'); - return; - } - var cb = u2f.callbackMap_[reqId]; - delete u2f.callbackMap_[reqId]; - cb(response['responseData']); - }; - - /** - * Calls the callback with true or false as first and only argument - * @param {Function} callback - */ - u2f.isSupported = function(callback) { - var hasCalledBack = false; - function reply(value) { - if (hasCalledBack) - return; - hasCalledBack = true; - callback(value); - } - u2f.getApiVersion( - function (response) { - js_api_version = response['js_api_version'] === undefined ? 0 : response['js_api_version']; - reply(true); - } - ); - // No response from extension within 1500ms -> no support - setTimeout(reply.bind(null, false), 1500); - }; - - /** - * Dispatches an array of sign requests to available U2F tokens. - * If the JS API version supported by the extension is unknown, it first sends a - * message to the extension to find out the supported API version and then it sends - * the sign request. - * @param {string=} appId - * @param {string=} challenge - * @param {Array} registeredKeys - * @param {function((u2f.Error|u2f.SignResponse))} callback - * @param {number=} opt_timeoutSeconds - */ - u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) { - if (js_api_version === undefined) { - // Send a message to get the extension to JS API version, then send the actual sign request. - u2f.getApiVersion( - function (response) { - js_api_version = response['js_api_version'] === undefined ? 0 : response['js_api_version']; - console.log("Extension JS API Version: ", js_api_version); - u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds); - }); - } else { - // We know the JS API version. Send the actual sign request in the supported API version. - u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds); - } - }; - - /** - * Dispatches an array of sign requests to available U2F tokens. - * @param {string=} appId - * @param {string=} challenge - * @param {Array} registeredKeys - * @param {function((u2f.Error|u2f.SignResponse))} callback - * @param {number=} opt_timeoutSeconds - */ - u2f.sendSignRequest = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) { - u2f.getPortSingleton_(function(port) { - var reqId = ++u2f.reqCounter_; - u2f.callbackMap_[reqId] = callback; - var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ? - opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC); - var req = u2f.formatSignRequest_(appId, challenge, registeredKeys, timeoutSeconds, reqId); - port.postMessage(req); - }); - }; - - /** - * Dispatches register requests to available U2F tokens. An array of sign - * requests identifies already registered tokens. - * If the JS API version supported by the extension is unknown, it first sends a - * message to the extension to find out the supported API version and then it sends - * the register request. - * @param {string=} appId - * @param {Array} registerRequests - * @param {Array} registeredKeys - * @param {function((u2f.Error|u2f.RegisterResponse))} callback - * @param {number=} opt_timeoutSeconds - */ - u2f.register = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) { - if (js_api_version === undefined) { - // Send a message to get the extension to JS API version, then send the actual register request. - u2f.getApiVersion( - function (response) { - js_api_version = response['js_api_version'] === undefined ? 0: response['js_api_version']; - console.log("Extension JS API Version: ", js_api_version); - u2f.sendRegisterRequest(appId, registerRequests, registeredKeys, - callback, opt_timeoutSeconds); - }); - } else { - // We know the JS API version. Send the actual register request in the supported API version. - u2f.sendRegisterRequest(appId, registerRequests, registeredKeys, - callback, opt_timeoutSeconds); - } - }; - - /** - * Dispatches register requests to available U2F tokens. An array of sign - * requests identifies already registered tokens. - * @param {string=} appId - * @param {Array} registerRequests - * @param {Array} registeredKeys - * @param {function((u2f.Error|u2f.RegisterResponse))} callback - * @param {number=} opt_timeoutSeconds - */ - u2f.sendRegisterRequest = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) { - u2f.getPortSingleton_(function(port) { - var reqId = ++u2f.reqCounter_; - u2f.callbackMap_[reqId] = callback; - var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ? - opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC); - var req = u2f.formatRegisterRequest_( - appId, registeredKeys, registerRequests, timeoutSeconds, reqId); - port.postMessage(req); - }); - }; - - - /** - * Dispatches a message to the extension to find out the supported - * JS API version. - * If the user is on a mobile phone and is thus using Google Authenticator instead - * of the Chrome extension, don't send the request and simply return 0. - * @param {function((u2f.Error|u2f.GetJsApiVersionResponse))} callback - * @param {number=} opt_timeoutSeconds - */ - u2f.getApiVersion = function(callback, opt_timeoutSeconds) { - u2f.getPortSingleton_(function(port) { - // If we are using Android Google Authenticator or iOS client app, - // do not fire an intent to ask which JS API version to use. - if (port.getPortType) { - var apiVersion; - switch (port.getPortType()) { - case 'WrappedIosPort_': - case 'WrappedAuthenticatorPort_': - apiVersion = 1.1; - break; - - default: - apiVersion = 0; - break; - } - callback({ 'js_api_version': apiVersion }); - return; - } - var reqId = ++u2f.reqCounter_; - u2f.callbackMap_[reqId] = callback; - var req = { - type: u2f.MessageTypes.U2F_GET_API_VERSION_REQUEST, - timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ? - opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC), - requestId: reqId - }; - port.postMessage(req); - }); - }; - - // Feature detection (yes really) - // For IE and Edge detection, see https://stackoverflow.com/questions/31757852#31757969 - // and https://stackoverflow.com/questions/56360225#56361977 - var isBrowser = (typeof navigator !== 'undefined') && !!navigator.userAgent; - var isSafari = isBrowser && navigator.userAgent.match(/Safari\//) - && !navigator.userAgent.match(/Chrome\//); - var isEDGE = isBrowser && /(Edge\/)|(edg\/)/i.test(navigator.userAgent); - var isIE = isBrowser && /(MSIE 9|MSIE 10|rv:11.0)/i.test(navigator.userAgent); - var _backend = null; - function getBackend() { - if (_backend) - return _backend; - var supportChecker = new Promise(function (resolve, reject) { - function notSupported() { - resolve({ u2f: null }); - } - if (!isBrowser) - return notSupported(); - if (isSafari) - // Safari doesn't support U2F, and the Safari-FIDO-U2F - // extension lacks full support (Multi-facet apps), so we - // block it until proper support. - return notSupported(); - var hasNativeSupport = (typeof window.u2f !== 'undefined') && - (typeof window.u2f.sign === 'function'); - if (hasNativeSupport) - return resolve({ u2f: window.u2f }); - if (isEDGE || isIE) - // We don't want to check for Google's extension hack on EDGE & IE - // as it'll cause trouble (popups, etc) - return notSupported(); - if (location.protocol === 'http:') - // U2F isn't supported over http, only https - return notSupported(); - if (typeof MessageChannel === 'undefined') - // Unsupported browser, the chrome hack would throw - return notSupported(); - // Test for google extension support - chromeApi.isSupported(function (ok) { - if (ok) - resolve({ u2f: chromeApi }); - else - notSupported(); - }); - }) - .then(function (response) { - _backend = response.u2f ? supportChecker : null; - return response; - }); - return supportChecker; - } - var ErrorCodes = { - OK: 0, - OTHER_ERROR: 1, - BAD_REQUEST: 2, - CONFIGURATION_UNSUPPORTED: 3, - DEVICE_INELIGIBLE: 4, - TIMEOUT: 5 - }; - var ErrorNames = { - "0": "OK", - "1": "OTHER_ERROR", - "2": "BAD_REQUEST", - "3": "CONFIGURATION_UNSUPPORTED", - "4": "DEVICE_INELIGIBLE", - "5": "TIMEOUT" - }; - function makeError(msg, err) { - var code = err != null ? err.errorCode : 1; // Default to OTHER_ERROR - var type = ErrorNames[('' + code)]; - var error = new Error(msg); - error.metaData = { type: type, code: code }; - return error; - } - function isSupported() { - return getBackend() - .then(function (backend) { return !!backend.u2f; }); - } - function _ensureSupport(backend) { - if (!backend.u2f) { - if (location.protocol === 'http:') - throw new Error("U2F isn't supported over http, only https"); - throw new Error("U2F not supported"); - } - } - function ensureSupport() { - return getBackend() - .then(_ensureSupport); - } - function arrayify(value) { - if (value != null && Array.isArray(value)) - return value; - return value == null - ? [] - : Array.isArray(value) - ? value.slice() : [value]; - } - function register(registerRequests, signRequests, timeout) { - var _registerRequests = arrayify(registerRequests); - if (typeof signRequests === 'number' && typeof timeout === 'undefined') { - timeout = signRequests; - signRequests = []; - } - var _signRequests = arrayify(signRequests); - return getBackend() - .then(function (backend) { - _ensureSupport(backend); - var u2f = backend.u2f; - return new Promise(function (resolve, reject) { - function callback(response) { - if (response.errorCode) - reject(makeError("Registration failed", response)); - else { - delete response.errorCode; - resolve(response); - } - } - var appId = _registerRequests[0].appId; - u2f.register(appId, _registerRequests, _signRequests, callback, timeout); - }); - }); - } - function sign(signRequests, timeout) { - var _signRequests = arrayify(signRequests); - return getBackend() - .then(function (backend) { - _ensureSupport(backend); - var u2f = backend.u2f; - return new Promise(function (resolve, reject) { - var _a; - function callback(response) { - if (response.errorCode) - reject(makeError("Sign failed", response)); - else { - delete response.errorCode; - resolve(response); - } - } - var appId = _signRequests[0].appId; - var challenge = _signRequests[0].challenge; - var registeredKeys = (_a = []).concat.apply(_a, _signRequests - .map(function (_a) { - var version = _a.version, keyHandle = _a.keyHandle, appId = _a.appId; - return arrayify(keyHandle) - .map(function (keyHandle) { - return ({ version: version, keyHandle: keyHandle, appId: appId }); - }); - })); - u2f.sign(appId, challenge, registeredKeys, callback, timeout); - }); - }); - } - - var u2fApi = /*#__PURE__*/Object.freeze({ - ErrorCodes: ErrorCodes, - ErrorNames: ErrorNames, - isSupported: isSupported, - ensureSupport: ensureSupport, - register: register, - sign: sign - }); - - window.u2fApi = u2fApi; - -}()); diff --git a/casa/app/src/main/webapp/user/u2f-detail.zul b/casa/app/src/main/webapp/user/u2f-detail.zul deleted file mode 100644 index f0a91b3b3..000000000 --- a/casa/app/src/main/webapp/user/u2f-detail.zul +++ /dev/null @@ -1,123 +0,0 @@ - - - - - ${zkService.appName} - ${labels.usr.u2f_title} - - - - -
- - -
-
- -
-

${labels.usr.u2f_title}

-

${labels.usr.u2f_text}

- -
- - - - -
-
- - -

- -
- - - - - - - - -
- - -

-
-
- - - - - - -
-
-
-
- -
-

${labels.usr.u2f_add}

- - - -
-
- - - -
- ${labels.general.new_nick} -
- -
-
-
- - -
- -
- - - - - - - - - diff --git a/casa/extras/enrollment-client/README.md b/casa/extras/enrollment-client/README.md index 7d7dd2c68..35d3d3e9a 100644 --- a/casa/extras/enrollment-client/README.md +++ b/casa/extras/enrollment-client/README.md @@ -18,9 +18,9 @@ This is a client-side only application (HTML+JS). Intended to be illustrative an ## Troubleshooting If you face errors (in the browser's developer console) when accessing endpoints such as `/.well-known/openid-configuration` -or `/oxauth/restv1/token`, obtain a token manually and then gently add a line like `token = "token-value"` as the first statement +or `/jans-auth/restv1/token`, obtain a token manually and then gently add a line like `token = "token-value"` as the first statement in the document.ready event handler for the page you are trying to visualize. The following shows how to obtain a token: -`curl -k -u : -d grant_type=client_credentials https:///oxauth/restv1/token` +`curl -k -u : -d grant_type=client_credentials https:///jans-auth/restv1/token` Alternatively, enrollment pages have a text field at the bottom where you can simply paste the token value. diff --git a/casa/extras/enrollment-client/index.html b/casa/extras/enrollment-client/index.html index bc3ab3848..3aed80ca2 100644 --- a/casa/extras/enrollment-client/index.html +++ b/casa/extras/enrollment-client/index.html @@ -8,8 +8,7 @@

Sample client for Casa credentials enrollment

diff --git a/casa/extras/enrollment-client/super_gluu.html b/casa/extras/enrollment-client/super_gluu.html index 61e53ac24..22be62d8d 100644 --- a/casa/extras/enrollment-client/super_gluu.html +++ b/casa/extras/enrollment-client/super_gluu.html @@ -1,6 +1,6 @@ - + diff --git a/casa/extras/enrollment-client/u2f-api.js b/casa/extras/enrollment-client/u2f-api.js deleted file mode 100644 index 37f49dc0d..000000000 --- a/casa/extras/enrollment-client/u2f-api.js +++ /dev/null @@ -1,830 +0,0 @@ -(function () { - 'use strict'; - - //Copyright 2014-2015 Google Inc. All rights reserved. - - //Use of this source code is governed by a BSD-style - //license that can be found in the LICENSE file or at - //https://developers.google.com/open-source/licenses/bsd - - /** - * @fileoverview The U2F api. - */ - // 'use strict'; - - - /** - * Namespace for the U2F api. - * @type {Object} - */ - var u2f = u2f || {}; - - const chromeApi = u2f; // Adaptation for u2f-api package - - /** - * FIDO U2F Javascript API Version - * @number - */ - var js_api_version; - - /** - * The U2F extension id - * @const {string} - */ - // The Chrome packaged app extension ID. - // Uncomment this if you want to deploy a server instance that uses - // the package Chrome app and does not require installing the U2F Chrome extension. - u2f.EXTENSION_ID = 'kmendfapggjehodndflmmgagdbamhnfd'; - // The U2F Chrome extension ID. - // Uncomment this if you want to deploy a server instance that uses - // the U2F Chrome extension to authenticate. - // u2f.EXTENSION_ID = 'pfboblefjcgdjicmnffhdgionmgcdmne'; - - - /** - * Message types for messsages to/from the extension - * @const - * @enum {string} - */ - u2f.MessageTypes = { - 'U2F_REGISTER_REQUEST': 'u2f_register_request', - 'U2F_REGISTER_RESPONSE': 'u2f_register_response', - 'U2F_SIGN_REQUEST': 'u2f_sign_request', - 'U2F_SIGN_RESPONSE': 'u2f_sign_response', - 'U2F_GET_API_VERSION_REQUEST': 'u2f_get_api_version_request', - 'U2F_GET_API_VERSION_RESPONSE': 'u2f_get_api_version_response' - }; - - - /** - * Response status codes - * @const - * @enum {number} - */ - u2f.ErrorCodes = { - 'OK': 0, - 'OTHER_ERROR': 1, - 'BAD_REQUEST': 2, - 'CONFIGURATION_UNSUPPORTED': 3, - 'DEVICE_INELIGIBLE': 4, - 'TIMEOUT': 5 - }; - - - //Low level MessagePort API support - - /** - * Sets up a MessagePort to the U2F extension using the - * available mechanisms. - * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback - */ - u2f.getMessagePort = function(callback) { - if (typeof chrome != 'undefined' && chrome.runtime) { - // The actual message here does not matter, but we need to get a reply - // for the callback to run. Thus, send an empty signature request - // in order to get a failure response. - var msg = { - type: u2f.MessageTypes.U2F_SIGN_REQUEST, - signRequests: [] - }; - chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function() { - if (!chrome.runtime.lastError) { - // We are on a whitelisted origin and can talk directly - // with the extension. - u2f.getChromeRuntimePort_(callback); - } else { - // chrome.runtime was available, but we couldn't message - // the extension directly, use iframe - u2f.getIframePort_(callback); - } - }); - } else if (u2f.isAndroidChrome_()) { - u2f.getAuthenticatorPort_(callback); - } else if (u2f.isIosChrome_()) { - u2f.getIosPort_(callback); - } else { - // chrome.runtime was not available at all, which is normal - // when this origin doesn't have access to any extensions. - u2f.getIframePort_(callback); - } - }; - - /** - * Detect chrome running on android based on the browser's useragent. - * @private - */ - u2f.isAndroidChrome_ = function() { - var userAgent = navigator.userAgent; - return userAgent.indexOf('Chrome') != -1 && - userAgent.indexOf('Android') != -1; - }; - - /** - * Detect chrome running on iOS based on the browser's platform. - * @private - */ - u2f.isIosChrome_ = function() { - return ["iPhone", "iPad", "iPod"].indexOf(navigator.platform) > -1; - }; - - /** - * Connects directly to the extension via chrome.runtime.connect. - * @param {function(u2f.WrappedChromeRuntimePort_)} callback - * @private - */ - u2f.getChromeRuntimePort_ = function(callback) { - var port = chrome.runtime.connect(u2f.EXTENSION_ID, - {'includeTlsChannelId': true}); - setTimeout(function() { - callback(new u2f.WrappedChromeRuntimePort_(port)); - }, 0); - }; - - /** - * Return a 'port' abstraction to the Authenticator app. - * @param {function(u2f.WrappedAuthenticatorPort_)} callback - * @private - */ - u2f.getAuthenticatorPort_ = function(callback) { - setTimeout(function() { - callback(new u2f.WrappedAuthenticatorPort_()); - }, 0); - }; - - /** - * Return a 'port' abstraction to the iOS client app. - * @param {function(u2f.WrappedIosPort_)} callback - * @private - */ - u2f.getIosPort_ = function(callback) { - setTimeout(function() { - callback(new u2f.WrappedIosPort_()); - }, 0); - }; - - /** - * A wrapper for chrome.runtime.Port that is compatible with MessagePort. - * @param {Port} port - * @constructor - * @private - */ - u2f.WrappedChromeRuntimePort_ = function(port) { - this.port_ = port; - }; - - /** - * Format and return a sign request compliant with the JS API version supported by the extension. - * @param {Array} signRequests - * @param {number} timeoutSeconds - * @param {number} reqId - * @return {Object} - */ - u2f.formatSignRequest_ = - function(appId, challenge, registeredKeys, timeoutSeconds, reqId) { - if (js_api_version === undefined || js_api_version < 1.1) { - // Adapt request to the 1.0 JS API - var signRequests = []; - for (var i = 0; i < registeredKeys.length; i++) { - signRequests[i] = { - version: registeredKeys[i].version, - challenge: challenge, - keyHandle: registeredKeys[i].keyHandle, - appId: appId - }; - } - return { - type: u2f.MessageTypes.U2F_SIGN_REQUEST, - signRequests: signRequests, - timeoutSeconds: timeoutSeconds, - requestId: reqId - }; - } - // JS 1.1 API - return { - type: u2f.MessageTypes.U2F_SIGN_REQUEST, - appId: appId, - challenge: challenge, - registeredKeys: registeredKeys, - timeoutSeconds: timeoutSeconds, - requestId: reqId - }; - }; - - /** - * Format and return a register request compliant with the JS API version supported by the extension.. - * @param {Array} signRequests - * @param {Array} signRequests - * @param {number} timeoutSeconds - * @param {number} reqId - * @return {Object} - */ - u2f.formatRegisterRequest_ = - function(appId, registeredKeys, registerRequests, timeoutSeconds, reqId) { - if (js_api_version === undefined || js_api_version < 1.1) { - // Adapt request to the 1.0 JS API - for (var i = 0; i < registerRequests.length; i++) { - registerRequests[i].appId = appId; - } - var signRequests = []; - for (var i = 0; i < registeredKeys.length; i++) { - signRequests[i] = { - version: registeredKeys[i].version, - challenge: registerRequests[0], - keyHandle: registeredKeys[i].keyHandle, - appId: appId - }; - } - return { - type: u2f.MessageTypes.U2F_REGISTER_REQUEST, - signRequests: signRequests, - registerRequests: registerRequests, - timeoutSeconds: timeoutSeconds, - requestId: reqId - }; - } - // JS 1.1 API - return { - type: u2f.MessageTypes.U2F_REGISTER_REQUEST, - appId: appId, - registerRequests: registerRequests, - registeredKeys: registeredKeys, - timeoutSeconds: timeoutSeconds, - requestId: reqId - }; - }; - - - /** - * Posts a message on the underlying channel. - * @param {Object} message - */ - u2f.WrappedChromeRuntimePort_.prototype.postMessage = function(message) { - this.port_.postMessage(message); - }; - - - /** - * Emulates the HTML 5 addEventListener interface. Works only for the - * onmessage event, which is hooked up to the chrome.runtime.Port.onMessage. - * @param {string} eventName - * @param {function({data: Object})} handler - */ - u2f.WrappedChromeRuntimePort_.prototype.addEventListener = - function(eventName, handler) { - var name = eventName.toLowerCase(); - if (name == 'message' || name == 'onmessage') { - this.port_.onMessage.addListener(function(message) { - // Emulate a minimal MessageEvent object - handler({'data': message}); - }); - } else { - console.error('WrappedChromeRuntimePort only supports onMessage'); - } - }; - - /** - * Wrap the Authenticator app with a MessagePort interface. - * @constructor - * @private - */ - u2f.WrappedAuthenticatorPort_ = function() { - this.requestId_ = -1; - this.requestObject_ = null; - }; - - /** - * Launch the Authenticator intent. - * @param {Object} message - */ - u2f.WrappedAuthenticatorPort_.prototype.postMessage = function(message) { - var intentUrl = - u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ + - ';S.request=' + encodeURIComponent(JSON.stringify(message)) + - ';end'; - document.location = intentUrl; - }; - - /** - * Tells what type of port this is. - * @return {String} port type - */ - u2f.WrappedAuthenticatorPort_.prototype.getPortType = function() { - return "WrappedAuthenticatorPort_"; - }; - - - /** - * Emulates the HTML 5 addEventListener interface. - * @param {string} eventName - * @param {function({data: Object})} handler - */ - u2f.WrappedAuthenticatorPort_.prototype.addEventListener = function(eventName, handler) { - var name = eventName.toLowerCase(); - if (name == 'message') { - var self = this; - /* Register a callback to that executes when - * chrome injects the response. */ - window.addEventListener( - 'message', self.onRequestUpdate_.bind(self, handler), false); - } else { - console.error('WrappedAuthenticatorPort only supports message'); - } - }; - - /** - * Callback invoked when a response is received from the Authenticator. - * @param function({data: Object}) callback - * @param {Object} message message Object - */ - u2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ = - function(callback, message) { - var messageObject = JSON.parse(message.data); - var intentUrl = messageObject['intentURL']; - - var errorCode = messageObject['errorCode']; - var responseObject = null; - if (messageObject.hasOwnProperty('data')) { - responseObject = /** @type {Object} */ ( - JSON.parse(messageObject['data'])); - } - - callback({'data': responseObject}); - }; - - /** - * Base URL for intents to Authenticator. - * @const - * @private - */ - u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ = - 'intent:#Intent;action=com.google.android.apps.authenticator.AUTHENTICATE'; - - /** - * Wrap the iOS client app with a MessagePort interface. - * @constructor - * @private - */ - u2f.WrappedIosPort_ = function() {}; - - /** - * Launch the iOS client app request - * @param {Object} message - */ - u2f.WrappedIosPort_.prototype.postMessage = function(message) { - var str = JSON.stringify(message); - var url = "u2f://auth?" + encodeURI(str); - location.replace(url); - }; - - /** - * Tells what type of port this is. - * @return {String} port type - */ - u2f.WrappedIosPort_.prototype.getPortType = function() { - return "WrappedIosPort_"; - }; - - /** - * Emulates the HTML 5 addEventListener interface. - * @param {string} eventName - * @param {function({data: Object})} handler - */ - u2f.WrappedIosPort_.prototype.addEventListener = function(eventName, handler) { - var name = eventName.toLowerCase(); - if (name !== 'message') { - console.error('WrappedIosPort only supports message'); - } - }; - - /** - * Sets up an embedded trampoline iframe, sourced from the extension. - * @param {function(MessagePort)} callback - * @private - */ - u2f.getIframePort_ = function(callback) { - // Create the iframe - var iframeOrigin = 'chrome-extension://' + u2f.EXTENSION_ID; - var iframe = document.createElement('iframe'); - iframe.src = iframeOrigin + '/u2f-comms.html'; - iframe.setAttribute('style', 'display:none'); - document.body.appendChild(iframe); - - var channel = new MessageChannel(); - var ready = function(message) { - if (message.data == 'ready') { - channel.port1.removeEventListener('message', ready); - callback(channel.port1); - } else { - console.error('First event on iframe port was not "ready"'); - } - }; - channel.port1.addEventListener('message', ready); - channel.port1.start(); - - iframe.addEventListener('load', function() { - // Deliver the port to the iframe and initialize - iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]); - }); - }; - - - //High-level JS API - - /** - * Default extension response timeout in seconds. - * @const - */ - u2f.EXTENSION_TIMEOUT_SEC = 30; - - /** - * A singleton instance for a MessagePort to the extension. - * @type {MessagePort|u2f.WrappedChromeRuntimePort_} - * @private - */ - u2f.port_ = null; - - /** - * Callbacks waiting for a port - * @type {Array} - * @private - */ - u2f.waitingForPort_ = []; - - /** - * A counter for requestIds. - * @type {number} - * @private - */ - u2f.reqCounter_ = 0; - - /** - * A map from requestIds to client callbacks - * @type {Object.} - * @private - */ - u2f.callbackMap_ = {}; - - /** - * Creates or retrieves the MessagePort singleton to use. - * @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback - * @private - */ - u2f.getPortSingleton_ = function(callback) { - if (u2f.port_) { - callback(u2f.port_); - } else { - if (u2f.waitingForPort_.length == 0) { - u2f.getMessagePort(function(port) { - u2f.port_ = port; - u2f.port_.addEventListener('message', - /** @type {function(Event)} */ (u2f.responseHandler_)); - - // Careful, here be async callbacks. Maybe. - while (u2f.waitingForPort_.length) - u2f.waitingForPort_.shift()(u2f.port_); - }); - } - u2f.waitingForPort_.push(callback); - } - }; - - /** - * Handles response messages from the extension. - * @param {MessageEvent.} message - * @private - */ - u2f.responseHandler_ = function(message) { - var response = message.data; - var reqId = response['requestId']; - if (!reqId || !u2f.callbackMap_[reqId]) { - console.error('Unknown or missing requestId in response.'); - return; - } - var cb = u2f.callbackMap_[reqId]; - delete u2f.callbackMap_[reqId]; - cb(response['responseData']); - }; - - /** - * Calls the callback with true or false as first and only argument - * @param {Function} callback - */ - u2f.isSupported = function(callback) { - var hasCalledBack = false; - function reply(value) { - if (hasCalledBack) - return; - hasCalledBack = true; - callback(value); - } - u2f.getApiVersion( - function (response) { - js_api_version = response['js_api_version'] === undefined ? 0 : response['js_api_version']; - reply(true); - } - ); - // No response from extension within 1500ms -> no support - setTimeout(reply.bind(null, false), 1500); - }; - - /** - * Dispatches an array of sign requests to available U2F tokens. - * If the JS API version supported by the extension is unknown, it first sends a - * message to the extension to find out the supported API version and then it sends - * the sign request. - * @param {string=} appId - * @param {string=} challenge - * @param {Array} registeredKeys - * @param {function((u2f.Error|u2f.SignResponse))} callback - * @param {number=} opt_timeoutSeconds - */ - u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) { - if (js_api_version === undefined) { - // Send a message to get the extension to JS API version, then send the actual sign request. - u2f.getApiVersion( - function (response) { - js_api_version = response['js_api_version'] === undefined ? 0 : response['js_api_version']; - console.log("Extension JS API Version: ", js_api_version); - u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds); - }); - } else { - // We know the JS API version. Send the actual sign request in the supported API version. - u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds); - } - }; - - /** - * Dispatches an array of sign requests to available U2F tokens. - * @param {string=} appId - * @param {string=} challenge - * @param {Array} registeredKeys - * @param {function((u2f.Error|u2f.SignResponse))} callback - * @param {number=} opt_timeoutSeconds - */ - u2f.sendSignRequest = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) { - u2f.getPortSingleton_(function(port) { - var reqId = ++u2f.reqCounter_; - u2f.callbackMap_[reqId] = callback; - var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ? - opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC); - var req = u2f.formatSignRequest_(appId, challenge, registeredKeys, timeoutSeconds, reqId); - port.postMessage(req); - }); - }; - - /** - * Dispatches register requests to available U2F tokens. An array of sign - * requests identifies already registered tokens. - * If the JS API version supported by the extension is unknown, it first sends a - * message to the extension to find out the supported API version and then it sends - * the register request. - * @param {string=} appId - * @param {Array} registerRequests - * @param {Array} registeredKeys - * @param {function((u2f.Error|u2f.RegisterResponse))} callback - * @param {number=} opt_timeoutSeconds - */ - u2f.register = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) { - if (js_api_version === undefined) { - // Send a message to get the extension to JS API version, then send the actual register request. - u2f.getApiVersion( - function (response) { - js_api_version = response['js_api_version'] === undefined ? 0: response['js_api_version']; - console.log("Extension JS API Version: ", js_api_version); - u2f.sendRegisterRequest(appId, registerRequests, registeredKeys, - callback, opt_timeoutSeconds); - }); - } else { - // We know the JS API version. Send the actual register request in the supported API version. - u2f.sendRegisterRequest(appId, registerRequests, registeredKeys, - callback, opt_timeoutSeconds); - } - }; - - /** - * Dispatches register requests to available U2F tokens. An array of sign - * requests identifies already registered tokens. - * @param {string=} appId - * @param {Array} registerRequests - * @param {Array} registeredKeys - * @param {function((u2f.Error|u2f.RegisterResponse))} callback - * @param {number=} opt_timeoutSeconds - */ - u2f.sendRegisterRequest = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) { - u2f.getPortSingleton_(function(port) { - var reqId = ++u2f.reqCounter_; - u2f.callbackMap_[reqId] = callback; - var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ? - opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC); - var req = u2f.formatRegisterRequest_( - appId, registeredKeys, registerRequests, timeoutSeconds, reqId); - port.postMessage(req); - }); - }; - - - /** - * Dispatches a message to the extension to find out the supported - * JS API version. - * If the user is on a mobile phone and is thus using Google Authenticator instead - * of the Chrome extension, don't send the request and simply return 0. - * @param {function((u2f.Error|u2f.GetJsApiVersionResponse))} callback - * @param {number=} opt_timeoutSeconds - */ - u2f.getApiVersion = function(callback, opt_timeoutSeconds) { - u2f.getPortSingleton_(function(port) { - // If we are using Android Google Authenticator or iOS client app, - // do not fire an intent to ask which JS API version to use. - if (port.getPortType) { - var apiVersion; - switch (port.getPortType()) { - case 'WrappedIosPort_': - case 'WrappedAuthenticatorPort_': - apiVersion = 1.1; - break; - - default: - apiVersion = 0; - break; - } - callback({ 'js_api_version': apiVersion }); - return; - } - var reqId = ++u2f.reqCounter_; - u2f.callbackMap_[reqId] = callback; - var req = { - type: u2f.MessageTypes.U2F_GET_API_VERSION_REQUEST, - timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ? - opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC), - requestId: reqId - }; - port.postMessage(req); - }); - }; - - // Feature detection (yes really) - // For IE and Edge detection, see https://stackoverflow.com/questions/31757852#31757969 - // and https://stackoverflow.com/questions/56360225#56361977 - var isBrowser = (typeof navigator !== 'undefined') && !!navigator.userAgent; - var isSafari = isBrowser && navigator.userAgent.match(/Safari\//) - && !navigator.userAgent.match(/Chrome\//); - var isEDGE = isBrowser && /(Edge\/)|(edg\/)/i.test(navigator.userAgent); - var isIE = isBrowser && /(MSIE 9|MSIE 10|rv:11.0)/i.test(navigator.userAgent); - var _backend = null; - function getBackend() { - if (_backend) - return _backend; - var supportChecker = new Promise(function (resolve, reject) { - function notSupported() { - resolve({ u2f: null }); - } - if (!isBrowser) - return notSupported(); - if (isSafari) - // Safari doesn't support U2F, and the Safari-FIDO-U2F - // extension lacks full support (Multi-facet apps), so we - // block it until proper support. - return notSupported(); - var hasNativeSupport = (typeof window.u2f !== 'undefined') && - (typeof window.u2f.sign === 'function'); - if (hasNativeSupport) - return resolve({ u2f: window.u2f }); - if (isEDGE || isIE) - // We don't want to check for Google's extension hack on EDGE & IE - // as it'll cause trouble (popups, etc) - return notSupported(); - if (location.protocol === 'http:') - // U2F isn't supported over http, only https - return notSupported(); - if (typeof MessageChannel === 'undefined') - // Unsupported browser, the chrome hack would throw - return notSupported(); - // Test for google extension support - chromeApi.isSupported(function (ok) { - if (ok) - resolve({ u2f: chromeApi }); - else - notSupported(); - }); - }) - .then(function (response) { - _backend = response.u2f ? supportChecker : null; - return response; - }); - return supportChecker; - } - var ErrorCodes = { - OK: 0, - OTHER_ERROR: 1, - BAD_REQUEST: 2, - CONFIGURATION_UNSUPPORTED: 3, - DEVICE_INELIGIBLE: 4, - TIMEOUT: 5 - }; - var ErrorNames = { - "0": "OK", - "1": "OTHER_ERROR", - "2": "BAD_REQUEST", - "3": "CONFIGURATION_UNSUPPORTED", - "4": "DEVICE_INELIGIBLE", - "5": "TIMEOUT" - }; - function makeError(msg, err) { - var code = err != null ? err.errorCode : 1; // Default to OTHER_ERROR - var type = ErrorNames[('' + code)]; - var error = new Error(msg); - error.metaData = { type: type, code: code }; - return error; - } - function isSupported() { - return getBackend() - .then(function (backend) { return !!backend.u2f; }); - } - function _ensureSupport(backend) { - if (!backend.u2f) { - if (location.protocol === 'http:') - throw new Error("U2F isn't supported over http, only https"); - throw new Error("U2F not supported"); - } - } - function ensureSupport() { - return getBackend() - .then(_ensureSupport); - } - function arrayify(value) { - if (value != null && Array.isArray(value)) - return value; - return value == null - ? [] - : Array.isArray(value) - ? value.slice() : [value]; - } - function register(registerRequests, signRequests, timeout) { - var _registerRequests = arrayify(registerRequests); - if (typeof signRequests === 'number' && typeof timeout === 'undefined') { - timeout = signRequests; - signRequests = []; - } - var _signRequests = arrayify(signRequests); - return getBackend() - .then(function (backend) { - _ensureSupport(backend); - var u2f = backend.u2f; - return new Promise(function (resolve, reject) { - function callback(response) { - if (response.errorCode) - reject(makeError("Registration failed", response)); - else { - delete response.errorCode; - resolve(response); - } - } - var appId = _registerRequests[0].appId; - u2f.register(appId, _registerRequests, _signRequests, callback, timeout); - }); - }); - } - function sign(signRequests, timeout) { - var _signRequests = arrayify(signRequests); - return getBackend() - .then(function (backend) { - _ensureSupport(backend); - var u2f = backend.u2f; - return new Promise(function (resolve, reject) { - var _a; - function callback(response) { - if (response.errorCode) - reject(makeError("Sign failed", response)); - else { - delete response.errorCode; - resolve(response); - } - } - var appId = _signRequests[0].appId; - var challenge = _signRequests[0].challenge; - var registeredKeys = (_a = []).concat.apply(_a, _signRequests - .map(function (_a) { - var version = _a.version, keyHandle = _a.keyHandle, appId = _a.appId; - return arrayify(keyHandle) - .map(function (keyHandle) { - return ({ version: version, keyHandle: keyHandle, appId: appId }); - }); - })); - u2f.sign(appId, challenge, registeredKeys, callback, timeout); - }); - }); - } - - var u2fApi = /*#__PURE__*/Object.freeze({ - ErrorCodes: ErrorCodes, - ErrorNames: ErrorNames, - isSupported: isSupported, - ensureSupport: ensureSupport, - register: register, - sign: sign - }); - - window.u2fApi = u2fApi; - -}()); diff --git a/casa/extras/enrollment-client/u2f.html b/casa/extras/enrollment-client/u2f.html deleted file mode 100644 index bf50d0887..000000000 --- a/casa/extras/enrollment-client/u2f.html +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - diff --git a/casa/installer/casa_cleanup.py b/casa/installer/casa_cleanup.py deleted file mode 100755 index 66686eb44..000000000 --- a/casa/installer/casa_cleanup.py +++ /dev/null @@ -1,216 +0,0 @@ -#!/usr/bin/python - -import re -from setup import * -from pylib import Properties -from pylib.cbm import CBM -import ldap -import ldap.modlist as modlist -ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_ALLOW) - -from setup_casa import get_properties, unobscure, cur_dir, SetupCasa - - -COUCHBASE = 1 -LDAP = 2 - -storage_types = {'ldap': LDAP, 'couchbase': COUCHBASE} - -class casaCleanup(object): - - def __init__(self, install_dir): - - self.install_dir = install_dir - self.cbm = None - self.ldap_conn = None - self.conf_prop = get_properties(setupObject.gluu_properties_fn) - self.detectedHostname = setupObject.detect_hostname() - self.twilio_version = '7.17.0' - - if os.path.exists(setupObject.gluu_hybrid_roperties): - get_properties(setupObject.gluu_hybrid_roperties, self.conf_prop) - - - def get_storage_location(self, storage): - - retval = LDAP - couchbase_mappings = [ s.strip() for s in self.conf_prop['storage.couchbase.mapping'].split(',') ] - - if self.conf_prop["persistence.type"] in storage_types: - retval = storage_types[self.conf_prop["persistence.type"]] - - elif storage == 'default': - retval = storage_types[self.conf_prop['storage.default']] - - else: - if storage in couchbase_mappings: - retval = COUCHBASE - - if (retval == COUCHBASE) and (not self.cbm): - cbp = get_properties(setupObject.gluuCouchebaseProperties) - cbm_hostname = cbp['servers'].split(',')[0] - cbm_username = cbp['auth.userName'] - cbm_pass = unobscure(cbp['auth.userPassword']) - self.cbm = CBM(cbm_hostname, cbm_username, cbm_pass) - - elif (retval == LDAP) and (not self.ldap_conn): - lp = get_properties(setupObject.ox_ldap_properties) - ldap_pass = unobscure(lp['bindPassword']) - ldap_hostname, ldap_port = lp['servers'].split(',')[0].split(':') - - self.ldap_conn = ldap.initialize('ldaps://{0}:{1}'.format(ldap_hostname, ldap_port)) - self.ldap_conn.simple_bind_s('cn=directory manager',ldap_pass) - - return retval - - def del_casa_custom_scripts(self): - - print "Deleting Casa Custom Scripts" - - default_location = self.get_storage_location('default') - - if default_location == LDAP: - for inum in ('BABA-CACA', 'DAA9-F7F8'): - dn = 'inum={0},ou=scripts,o=gluu'.format(inum) - try: - self.ldap_conn.delete_s(dn) - except: - pass - - elif default_location == COUCHBASE: - for inum in ('BABA-CACA', 'DAA9-F7F8'): - self.cbm.exec_query('DELETE FROM `gluu` USE KEYS "scripts_{}"'.format(inum)) - - - def del_casa_clients(self): - - print "Deleting Casa Clients" - - clients_location = self.get_storage_location('clients') - - if clients_location == LDAP: - result = self.ldap_conn.search_s('ou=clients,o=gluu', - ldap.SCOPE_SUBTREE, - '(oxAuthDefaultAcrValues=casa)', - attrlist=['inum'], - ) - if result: - for client in result: - self.ldap_conn.delete_s(client[0]) - - elif clients_location == COUCHBASE: - self.cbm.exec_query('DELETE FROM `gluu_clients` WHERE objectClass="oxAuthClient" AND oxAuthDefaultAcrValues="casa"') - - - def del_casa_user_attributes(self): - print "Removing Casa attributes for people. This may take a while..." - - attrlist = [ - 'oxPreferredMethod', - 'oxOTPDevices', - 'oxMobileDevices', - 'oxStrongAuthPolicy', - 'oxTrustedDevicesInfo', - 'oxUnlinkedExternalUids' - ] - - people_location = self.get_storage_location('people') - - if people_location == LDAP: - result = self.ldap_conn.search_s('ou=people,o=gluu', ldap.SCOPE_SUBTREE, attrlist=attrlist) - - for people in result: - mod_list = [] - - for attr in attrlist: - if attr in people[1]: - mod_list.append((ldap.MOD_REPLACE, attr, [])) - - if mod_list: - print "Cleaning", people[0], mod_list - self.ldap_conn.modify_s(people[0], mod_list) - - elif people_location == COUCHBASE: - self.cbm.exec_query('UPDATE gluu_user UNSET {}'.format(', '.join(attrlist))) - - - def delCasaFiles(self): - - print('Deleting Casa Files..') - - if os.path.exists('/opt/gluu/jetty/casa/'): - setupObject.run(['rm','-r', '-f', '/opt/gluu/jetty/casa/']) - - casafiles = [ - '/etc/gluu/conf/casa.json', - '/etc/default/casa', - '/etc/init.d/casa', - '/etc/init.d/casa.gluu-3.1.6~', - '/etc/rc0.d/K01casa', - '/etc/rc2.d/S01casa', - '/etc/rc3.d/S01casa', - '/etc/rc4.d/S01casa', - '/etc/rc5.d/S01casa', - '/etc/rc6.d/K01casa', - '/run/jetty/casa-start.log', - '/run/jetty/casa.pid', - '/opt/dist/scripts/casa', - ] - - for fn in casafiles: - if os.path.exists(fn): - setupObject.run(['rm', '-f', fn]) - - libdir = '/opt/gluu/python/libs' - - for lib in os.listdir(libdir): - if lib.startswith('casa'): - setupObject.run(['rm', '-f', (os.path.join(libdir,lib))]) - - def removeTwilioPathOxauth(self): - print "Removing twilio jar path form oxauth.xml" - oxauth_xml_fn = '/opt/gluu/jetty/oxauth/webapps/oxauth.xml' - if os.path.exists(oxauth_xml_fn): - oxauth_xml = setupObject.readFile(oxauth_xml_fn) - oxauth_xml = oxauth_xml.splitlines() - - for l in oxauth_xml[:]: - if re.search('twilio-(.*)\.jar', l): - oxauth_xml.remove(l) - break - - oxauth_xml = '\n'.join(oxauth_xml) - setupObject.writeFile(oxauth_xml_fn, oxauth_xml) - - -if __name__ == '__main__': - - if not os.path.exists('/etc/gluu/conf/casa.json'): - print "Casa is not installed." - sys.exit() - - print "\033[91m\nThis script will remove all information pertaining to Casa from the disk and," - print "DB backends including Casa related entries and user attributes.\033[0m" - prompt = raw_input("Do you want to continue? [N/y] ") - - if not prompt.strip() or prompt[0].lower() != 'y': - print "Giving up Casa cleanup..." - sys.exit() - - setupObject = Setup(cur_dir) - setupObject.log = os.path.join(cur_dir, 'clean_casa.log') - setupObject.logError = os.path.join(cur_dir, 'clean_casa_error.log') - - # Get the OS and init type - (setupObject.os_type, setupObject.os_version) = setupObject.detect_os_type() - setupObject.os_initdaemon = setupObject.detect_initd() - - casaCleanupObject = casaCleanup(cur_dir) - print "Stopping Casa" - setupObject.run_service_command('casa', 'stop') - - casaCleanupObject.del_casa_custom_scripts() - casaCleanupObject.del_casa_clients() - casaCleanupObject.del_casa_user_attributes() - casaCleanupObject.delCasaFiles() - casaCleanupObject.removeTwilioPathOxauth() diff --git a/casa/installer/setup_casa.py b/casa/installer/setup_casa.py deleted file mode 100755 index 637cd6bb5..000000000 --- a/casa/installer/setup_casa.py +++ /dev/null @@ -1,391 +0,0 @@ -#!/usr/bin/python - -import sys -import os -import os.path -import json -import traceback -import ssl -import base64 -import pyDes -import re -from urlparse import urlparse -from setup import * -from pylib import Properties - -cur_dir = os.path.dirname(os.path.realpath(__file__)) - -#TODO: add command line options with argprase - -def get_properties(prop_fn, current_properties=None): - if not current_properties: - p = Properties.Properties() - else: - p = current_properties - - with open(prop_fn) as file_object: - p.load(file_object) - - for k in p.keys(): - if p[k].lower() == 'true': - p[k] == True - elif p[k].lower() == 'false': - p[k] == False - - return p - - -with open("/etc/gluu/conf/salt") as f: - salt_property = f.read().strip() - key = salt_property.split("=")[1].strip() - -def unobscure(s): - cipher = pyDes.triple_des(key) - decrypted = cipher.decrypt(base64.b64decode(s), padmode=pyDes.PAD_PKCS5) - return decrypted - -class SetupCasa(object): - - def __init__(self, install_dir): - self.install_dir = install_dir - self.setup_properties_fn = os.path.join(self.install_dir, 'setup_casa.properties') - self.savedProperties = os.path.join(self.install_dir, 'setup_casa.properties.last') - - # Change this to final version - self.twilio_version = '7.17.0' - self.casa_war_url = 'https://ox.gluu.org/maven/org/gluu/casa/4.1.0-Final/casa-4.1.0-Final.war' - self.twilio_jar_url = 'http://central.maven.org/maven2/com/twilio/sdk/twilio/{0}/twilio-{0}.jar'.format(self.twilio_version) - - self.application_max_ram = 1024 # in MB - - # Gluu components installation status - self.install_oxd = False - self.oxd_server_https = "" - self.distFolder = '/opt/dist' - self.casa = '/etc/gluu/conf/casa.json' - self.jetty_app_configuration = { - 'casa': {'name': 'casa', - 'jetty': {'modules': 'server,deploy,resources,http,http-forwarded,console-capture,jsp'}, - 'memory': {'ratio': 1, "jvm_heap_ration": 0.7, "max_allowed_mb": 1024}, - 'installed': False - } - } - - self.detectedHostname = setupObject.detect_hostname() - self.ldif_scripts_casa = '%s/scripts_casa.ldif' % setupObject.outputFolder - self.casa_config = '%s/casa.json' % setupObject.outputFolder - - - - def check_installed(self): - - if setupObject.check_installed(): - return os.path.exists('%s/casa.json' % setupObject.configFolder) - else: - print "\nPlease run './setup.py' to configure Gluu Server first!\n" - sys.exit() - - def download_files(self): - setupObject.logIt("Downloading files") - - # Casa is not part of CE package. We need to download it if needed - for download_url in (self.casa_war_url, self.twilio_jar_url): - fname = os.path.basename(download_url) - if fname.startswith('casa'): - fname = 'casa.war' - dest_path = os.path.join(setupObject.distGluuFolder, fname) - if not os.path.exists(dest_path): - print "Downloading:", fname - setupObject.run(['/usr/bin/wget', download_url, '--no-verbose', '--retry-connrefused', '--tries=10', '-O', dest_path]) - - def promptForProperties(self): - - promptForCasa = setupObject.getPrompt("Install Gluu Casa? (Y/n)", "Y")[0].lower() - - if promptForCasa == 'y': - self.application_max_ram = setupObject.getPrompt("Enter maximum RAM for applications in MB", '1024') - - have_oxd = setupObject.getPrompt( - "Do you have an existing oxd-server-4.0 installation?\nNote: oxd is used to integrate this product with the Gluu Server OP. (Y/n) ?", - "n")[0].lower() - - if have_oxd == 'y': - oxd_server_https = 'https://localhost:8443' - self.oxd_server_https = setupObject.getPrompt("Enter the URL + port of your oxd-server", oxd_server_https).lower() - - o = urlparse(self.oxd_server_https) - oxd_hostname = o.hostname - - if oxd_hostname in (self.detectedHostname, 'localhost'): - self.start_oxd_server('restart') - - else: - install_oxd = setupObject.getPrompt("Install oxd-server on this host now?", "Y")[0].lower() - if install_oxd == 'n': - print "An oxd server instance is required when installing this product via Linux packages" - sys.exit(0) - else: - self.install_oxd = True - self.oxd_server_https = 'https://localhost:8443' - - promptForLicense = setupObject.getPrompt("\n\nDo you acknowledge that Casa is commercial software, and use of Casa is only permitted\nunder the Gluu License Agreement for Gluu Casa? [Y/n]", "n")[0].lower() - if promptForLicense == 'n': - print("You must accept the Gluu License Agreement to continue. Exiting.\n") - sys.exit() - - - def casa_json_config(self): - data = setupObject.readFile(self.casa_config) - datastore = json.loads(data) - - o = urlparse(self.oxd_server_https) - self.oxd_hostname = o.hostname - self.oxd_port = o.port if o.port else 8443 - - datastore['oxd_config']['host'] = self.oxd_hostname - datastore['oxd_config']['port'] = self.oxd_port - - datastore_str = json.dumps(datastore, indent=2) - setupObject.writeFile(self.casa, datastore_str) - - - def import_ldif(self): - - p = get_properties(setupObject.gluu_properties_fn) - - if os.path.exists(setupObject.gluu_hybrid_roperties): - get_properties(setupObject.gluu_hybrid_roperties, p) - - if p["persistence.type"] == 'couchbase' or p.get('storage.default') == 'couchbase': - self.import_ldif_couchbase() - elif p["persistence.type"] == 'ldap' or p.get('storage.default') == 'ldap': - self.import_ldif_ldap() - - - def import_ldif_couchbase(self): - setupObject.logIt("Importing LDIF files into Couchbase") - p = get_properties(setupObject.gluuCouchebaseProperties) - attribDataTypes.startup(setupObject.install_dir) - - setupObject.prepare_multivalued_list() - setupObject.cbm = CBM(p['servers'].split(',')[0], p['auth.userName'], unobscure(p['auth.userPassword'])) - setupObject.import_ldif_couchebase([os.path.join('.','output/scripts_casa.ldif')],'gluu') - - - def import_ldif_ldap(self): - setupObject.logIt("Importing LDIF files into LDAP") - - if not os.path.exists(setupObject.gluu_properties_fn): - sys.exit("ldap properties file does not exist on this server. Terminating installation.") - - p = get_properties(setupObject.ox_ldap_properties) - - setupObject.ldapPass = unobscure(p['bindPassword']) - setupObject.ldap_hostname = p['servers'].split(',')[0].split(':')[0] - - setupObject.createLdapPw() - setupObject.ldap_binddn = "cn=directory manager" - setupObject.import_ldif_template_opendj(self.ldif_scripts_casa) - setupObject.deleteLdapPw() - - - def install_oxd_server(self): - - print "\nInstalling oxd from package..." - packageRpm = True - packageExtension = ".rpm" - if setupObject.os_type in ['debian', 'ubuntu']: - packageRpm = False - packageExtension = ".deb" - - oxdDistFolder = "%s/%s" % (self.distFolder, "oxd") - - if not os.path.exists(oxdDistFolder): - setupObject.logIt(oxdDistFolder+" Directory is not found") - print oxdDistFolder+" Directory is not found" - sys.exit(0) - - packageName = None - for file in os.listdir(oxdDistFolder): - if file.endswith(packageExtension): - packageName = "%s/%s" % ( oxdDistFolder, file ) - - if packageName == None: - setupObject.logIt('Failed to find oxd package in folder %s !' % oxdDistFolder) - sys.exit(0) - - setupObject.logIt("Found package '%s' for install" % packageName) - - if packageRpm: - setupObject.run([setupObject.cmd_rpm, '--install', '--verbose', '--hash', packageName]) - else: - setupObject.run([setupObject.cmd_dpkg, '--install', packageName]) - - #set trust_all_certs: true in oxd-server.yml - oxd_yaml_fn ='/opt/oxd-server/conf/oxd-server.yml' - oxd_yaml = setupObject.readFile(oxd_yaml_fn).split('\n') - - for i, l in enumerate(oxd_yaml[:]): - if l.strip().startswith('trust_all_certs'): - oxd_yaml[i] = 'trust_all_certs: true' - - setupObject.writeFile(oxd_yaml_fn, '\n'.join(oxd_yaml)) - - # Enable service autoload on Gluu-Server startup - setupObject.enable_service_at_start('oxd-server') - # change start.sh permission - setupObject.run(['chmod', '+x', '/opt/oxd-server/bin/oxd-start.sh']) - - # Start oxd-server - self.start_oxd_server() - - def start_oxd_server(self, cmd='start'): - print "Starting oxd-server..." - setupObject.run_service_command('oxd-server', cmd) - - - def install_casa(self): - print "Installing Casa" - setupObject.logIt("Configuring Casa...") - - setupObject.calculate_aplications_memory(self.application_max_ram, - self.jetty_app_configuration, - [self.jetty_app_configuration['casa']], - ) - - setupObject.copyFile('%s/casa.json' % setupObject.outputFolder, setupObject.configFolder) - setupObject.run(['chmod', 'g+w', '/opt/gluu/python/libs']) - - setupObject.logIt("Copying casa.war into jetty webapps folder...") - - jettyServiceName = 'casa' - setupObject.installJettyService(self.jetty_app_configuration[jettyServiceName]) - - jettyServiceWebapps = '%s/%s/webapps' % (setupObject.jetty_base, jettyServiceName) - setupObject.copyFile('%s/casa.war' % setupObject.distGluuFolder, jettyServiceWebapps) - - jettyServiceOxAuthCustomLibsPath = '%s/%s/%s' % (setupObject.jetty_base, "oxauth", "custom/libs") - setupObject.copyFile(os.path.join(setupObject.distGluuFolder, os.path.basename(self.twilio_jar_url)), jettyServiceOxAuthCustomLibsPath) - - setupObject.run([setupObject.cmd_chown, '-R', 'jetty:jetty', jettyServiceOxAuthCustomLibsPath]) - - # Make necessary Directories for Casa - for path in ('/opt/gluu/jetty/casa/static/', '/opt/gluu/jetty/casa/plugins'): - if not os.path.exists(path): - setupObject.run(['mkdir', '-p', path]) - setupObject.run(['chown', '-R', 'jetty:jetty', path]) - - #Adding twilio jar path to oxauth.xml - oxauth_xml_fn = '/opt/gluu/jetty/oxauth/webapps/oxauth.xml' - if os.path.exists(oxauth_xml_fn): - oxauth_xml = setupObject.readFile(oxauth_xml_fn) - oxauth_xml = oxauth_xml.splitlines() - - n = -1 - - for i, l in enumerate(oxauth_xml[:]): - ls = l.strip() - if ls == '': - n = i - if re.search('twilio-(.*)\.jar', ls): - oxauth_xml.remove(l) - n -= 1 - - oxauth_xml.insert(n, '\t./custom/libs/twilio-{}.jar'.format(self.twilio_version)) - oxauth_xml = '\n'.join(oxauth_xml) - setupObject.writeFile(oxauth_xml_fn, oxauth_xml) - - setupObject.run(['chown', '-R', 'jetty:jetty', '%s/casa.json' % setupObject.configFolder]) - setupObject.run(['chmod', 'g+w', '%s/casa.json' % setupObject.configFolder]) - self.casa_json_config() - - - def start_services(self): - - # Restart oxAuth service to load new custom libs - print "\nRestarting oxAuth" - try: - setupObject.run_service_command('oxauth', 'restart') - print "oxAuth restarted!\n" - except: - print "Error starting oxAuth! Please review setup_casa_error.log." - setupObject.logIt("Error starting oxAuth", True) - setupObject.logIt(traceback.format_exc(), True) - - # Start Casa - print "Starting Casa..." - try: - setupObject.run_service_command('casa', 'start') - print "Casa started!\n" - except: - print "Error starting Casa! Please review setup_casa_error.log." - setupObject.logIt("Error starting Casa", True) - setupObject.logIt(traceback.format_exc(), True) - - def import_oxd_certificate2javatruststore(self): - setupObject.logIt("Importing oxd certificate") - oxd_cert = ssl.get_server_certificate((self.oxd_hostname, self.oxd_port)) - oxd_alias = 'oxd_' + self.oxd_hostname.replace('.','_') - oxd_cert_tmp_fn = '/tmp/{}.crt'.format(oxd_alias) - - with open(oxd_cert_tmp_fn,'w') as w: - w.write(oxd_cert) - - setupObject.run(['/opt/jre/jre/bin/keytool', '-import', '-trustcacerts', '-keystore', - '/opt/jre/jre/lib/security/cacerts', '-storepass', 'changeit', - '-noprompt', '-alias', oxd_alias, '-file', oxd_cert_tmp_fn]) - - def load_properties(self, fn): - setupObject.logIt('Loading Properties %s' % fn) - p = get_properties(fn) - - for k in p.keys(): - setattr(self, k, p[k]) - -if __name__ == '__main__': - - - setupObject = Setup(cur_dir) - setupObject.log = os.path.join(cur_dir, 'setup_casa.log') - setupObject.logError = os.path.join(cur_dir, 'setup_casa_error.log') - - installObject = SetupCasa(cur_dir) - - if installObject.check_installed(): - print "\033[91m\nThis instance has already been configured. If you need to install new one you should reinstall package first.\033[0m" - sys.exit(2) - - # Get the OS and init type - (setupObject.os_type, setupObject.os_version) = setupObject.detect_os_type() - setupObject.os_initdaemon = setupObject.detect_initd() - - print "\nInstalling Gluu Casa...\n" - print "Detected OS : %s %s" % (setupObject.os_type, setupObject.os_version) - print "Detected init: %s" % setupObject.os_initdaemon - - setupObject.logIt("Installing Gluu Casa") - - if os.path.exists(installObject.setup_properties_fn): - installObject.load_properties(installObject.setup_properties_fn) - else: - installObject.promptForProperties() - - try: - installObject.download_files() - - if installObject.install_oxd: - installObject.install_oxd_server() - - installObject.install_casa() - installObject.import_ldif() - installObject.import_oxd_certificate2javatruststore() - installObject.start_services() - setupObject.save_properties(installObject.savedProperties, installObject) - except: - setupObject.logIt("***** Error caught in main loop *****", True) - setupObject.logIt(traceback.format_exc(), True) - - - print "Gluu Casa installation successful!\nPoint your browser to https://%s/casa\n" % installObject.detectedHostname - print "Recall admin capabilities are disabled by default.\nCheck casa docs to learn how to unlock admin features\n" - From f6be736fc97c284f94a3e4d0593fb2e19bd22942 Mon Sep 17 00:00:00 2001 From: Jose Date: Sun, 6 Mar 2022 10:23:39 -0500 Subject: [PATCH 018/101] chore: update custom scripts #125 --- casa/app/src/main/webapp/enrollment-api.yaml | 158 +------------ casa/extras/Casa.py | 6 +- casa/extras/casa-external_u2f.py | 225 ------------------- 3 files changed, 2 insertions(+), 387 deletions(-) delete mode 100644 casa/extras/casa-external_u2f.py diff --git a/casa/app/src/main/webapp/enrollment-api.yaml b/casa/app/src/main/webapp/enrollment-api.yaml index 5b0f62802..18967351c 100644 --- a/casa/app/src/main/webapp/enrollment-api.yaml +++ b/casa/app/src/main/webapp/enrollment-api.yaml @@ -698,162 +698,6 @@ paths: type: string description: Possible values for code: NO_MATCH_OR_EXPIRED, FAILED -# /enrollment/u2f/registration-message: -# get: -# tags: -# - fido -# -# description: | -# Gets a U2F registration message to be used in the browser using the U2F client API -# -# security: -# - casa_auth: [] -# parameters: -# - -# name: userid -# in: query -# description: Identifier (inum) of the user attempting the enrollment -# required: true -# type: string -# -# responses: -# 200: -# description: | -# A json containing an object useful to be passed to `u2f.register` javascript method. -# See: https://developers.yubico.com/U2F/Protocol_details/Specification.html -# 400: -# description: A parameter is missing or inconsistent -# schema: -# type: object -# required: -# - code -# properties: -# code: -# type: string -# example: NO_USER_ID -# 404: -# description: The user passed does not exist -# schema: -# type: object -# required: -# - code -# properties: -# code: -# type: string -# example: UNKNOWN_USER_ID -# 500: -# description: An unexpected error occurred when processing the request -# schema: -# type: object -# required: -# - code -# properties: -# code: -# type: string -# example: FAILED -# -# /enrollment/u2f/registration/{userid}: -# post: -# tags: -# - fido -# description: | -# Sends back to server the result obtained after the call to `u2f.register` was -# made. This occurs after the user touches the u2f key button -# -# parameters: -# - -# name: registrationResult -# in: body -# required: true -# schema: -# type: object -# - -# name: userid -# in: path -# description: Identifier (inum) of the user owning the device -# required: true -# type: string -# -# responses: -# 201: -# description: Successful enrollment of device -# schema: -# $ref: '#/definitions/SecurityKey' -# -# 404: -# description: The user supplied does not exist or is not recognized -# to have a recent call to `registration-message` -# schema: -# type: object -# required: -# - code -# properties: -# code: -# type: string -# example: NO_MATCH_OR_EXPIRED|UNKNOWN_USER_ID -# 500: -# description: The operation of enrollment failed -# schema: -# type: object -# required: -# - code -# properties: -# code: -# type: string -# example: FAILED -# -# /enrollment/u2f/creds/{userid}: -# post: -# tags: -# - fido -# description: | -# Assigns the nick name for a **recently** enrolled security key device whose -# associated user ID is passed in path parameter. Details of the credential -# are passed in credential parameter. This is supposed to be called **after** -# a call to `registration/{userid}` having received the data of the credential -# -# parameters: -# - -# name: credential -# in: body -# description: | -# Contains a nickname for this credential and the identifier (key) obtained in -# a previous `registration` call. -# required: true -# schema: -# $ref: '#/definitions/NamedCredential' -# - -# name: userid -# in: path -# description: Identifier (inum) of the user owning the device -# required: true -# type: string -# -# responses: -# 200: -# description: Update successfully applied. Empty body -# 400: -# description: A parameter is missing -# schema: -# type: object -# required: -# - code -# properties: -# code: -# type: string -# example: MISSING_PARAMS -# 500: -# description: The provided key in credential parameter is not -# recognized as a recent enrollment or the update failed -# schema: -# type: object -# required: -# - code -# properties: -# code: -# type: string -# example: NO_MATCH_OR_EXPIRED|FAILED - /enrollment/fido2/attestation: get: tags: @@ -1057,7 +901,7 @@ paths: type: array items: type: string - example: ["otp", "u2f", "fido2"] + example: ["otp", "fido2"] total_creds: type: integer example: 3 diff --git a/casa/extras/Casa.py b/casa/extras/Casa.py index 4d2453344..cefaa6106 100644 --- a/casa/extras/Casa.py +++ b/casa/extras/Casa.py @@ -32,7 +32,6 @@ class PersonAuthentication(PersonAuthenticationType): def __init__(self, currentTimeMillis): self.currentTimeMillis = currentTimeMillis self.ACR_SG = "super_gluu" - self.ACR_U2F = "u2f" self.modulePrefix = "casa-external_" @@ -58,10 +57,7 @@ def init(self, customScript, configurationAttributes): print "Casa. init. Got dynamic module for acr %s" % acr configAttrs = self.getConfigurationAttributes(acr, self.scriptsList) - if acr == self.ACR_U2F: - u2f_application_id = configurationAttributes.get("u2f_app_id").getValue2() - configAttrs.put("u2f_application_id", SimpleCustomProperty("u2f_application_id", u2f_application_id)) - elif acr == self.ACR_SG: + if acr == self.ACR_SG: application_id = configurationAttributes.get("supergluu_app_id").getValue2() configAttrs.put("application_id", SimpleCustomProperty("application_id", application_id)) diff --git a/casa/extras/casa-external_u2f.py b/casa/extras/casa-external_u2f.py deleted file mode 100644 index e1ecb56c7..000000000 --- a/casa/extras/casa-external_u2f.py +++ /dev/null @@ -1,225 +0,0 @@ -from io.jans.model.custom.script.type.auth import PersonAuthenticationType -from io.jans.as.client.fido.u2f import FidoU2fClientFactory -from io.jans.as.server.model.config import Constants -from io.jans.as.server.security import Identity -from io.jans.as.server.service import AuthenticationService -from io.jans.as.server.service import SessionIdService -from io.jans.as.server.service import UserService -from io.jans.as.server.service.fido.u2f import DeviceRegistrationService -from io.jans.as.server.util import ServerUtil -from io.jans.service.cdi.util import CdiUtil -from io.jans.util import StringHelper - -from javax.ws.rs import ClientErrorException, WebApplicationException -from javax.ws.rs.core import Response - -import java -import sys - - -class PersonAuthentication(PersonAuthenticationType): - def __init__(self, currentTimeMillis): - self.currentTimeMillis = currentTimeMillis - - def init(self, customScript, configurationAttributes): - print "U2F. Initialization" - - print "U2F. Initialization. Downloading U2F metadata" - u2f_server_uri = configurationAttributes.get("u2f_server_uri").getValue2() - u2f_server_metadata_uri = u2f_server_uri + "/.well-known/fido-configuration" - #u2f_server_metadata_uri = u2f_server_uri + "/oxauth/restv1/fido-configuration" - - metaDataConfigurationService = FidoU2fClientFactory.instance().createMetaDataConfigurationService(u2f_server_metadata_uri) - - max_attempts = 20 - for attempt in range(1, max_attempts + 1): - try: - self.metaDataConfiguration = metaDataConfigurationService.getMetadataConfiguration() - break - except WebApplicationException, ex: - # Detect if last try or we still get Service Unavailable HTTP error - if (attempt == max_attempts) or (ex.getResponse().getStatus() != Response.Status.SERVICE_UNAVAILABLE.getStatusCode()): - raise ex - - java.lang.Thread.sleep(3000) - print "Attempting to load metadata: %d" % attempt - - print "U2F. Initialized successfully" - return True - - def destroy(self, configurationAttributes): - print "U2F. Destroy" - print "U2F. Destroyed successfully" - return True - - def getApiVersion(self): - return 11 - - def getAuthenticationMethodClaims(self, configurationAttributes): - return None - - def isValidAuthenticationMethod(self, usageType, configurationAttributes): - return True - - def getAlternativeAuthenticationMethod(self, usageType, configurationAttributes): - return None - - def authenticate(self, configurationAttributes, requestParameters, step): - authenticationService = CdiUtil.bean(AuthenticationService) - - identity = CdiUtil.bean(Identity) - credentials = identity.getCredentials() - - user_name = credentials.getUsername() - - if (step == 1): - print "U2F. Authenticate for step 1" - - if authenticationService.getAuthenticatedUser() != None: - return True - - user_password = credentials.getPassword() - logged_in = False - if (StringHelper.isNotEmptyString(user_name) and StringHelper.isNotEmptyString(user_password)): - userService = CdiUtil.bean(UserService) - logged_in = authenticationService.authenticate(user_name, user_password) - - if (not logged_in): - return False - - return True - elif (step == 2): - print "U2F. Authenticate for step 2" - - token_response = ServerUtil.getFirstValue(requestParameters, "tokenResponse") - if token_response == None: - print "U2F. Authenticate for step 2. tokenResponse is empty" - return False - - auth_method = ServerUtil.getFirstValue(requestParameters, "authMethod") - if auth_method == None: - print "U2F. Authenticate for step 2. authMethod is empty" - return False - - authenticationService = CdiUtil.bean(AuthenticationService) - user = authenticationService.getAuthenticatedUser() - if (user == None): - print "U2F. Prepare for step 2. Failed to determine user name" - return False - - if (auth_method == 'authenticate'): - print "U2F. Prepare for step 2. Call FIDO U2F in order to finish authentication workflow" - authenticationRequestService = FidoU2fClientFactory.instance().createAuthenticationRequestService(self.metaDataConfiguration) - authenticationStatus = authenticationRequestService.finishAuthentication(user.getUserId(), token_response) - - if (authenticationStatus.getStatus() != Constants.RESULT_SUCCESS): - print "U2F. Authenticate for step 2. Get invalid authentication status from FIDO U2F server" - return False - - return True - elif (auth_method == 'enroll'): - print "U2F. Prepare for step 2. Call FIDO U2F in order to finish registration workflow" - registrationRequestService = FidoU2fClientFactory.instance().createRegistrationRequestService(self.metaDataConfiguration) - registrationStatus = registrationRequestService.finishRegistration(user.getUserId(), token_response) - - if (registrationStatus.getStatus() != Constants.RESULT_SUCCESS): - print "U2F. Authenticate for step 2. Get invalid registration status from FIDO U2F server" - return False - - return True - else: - print "U2F. Prepare for step 2. Authenticatiod method is invalid" - return False - - return False - else: - return False - - def prepareForStep(self, configurationAttributes, requestParameters, step): - identity = CdiUtil.bean(Identity) - - if (step == 1): - return True - elif (step == 2): - print "U2F. Prepare for step 2" - - session_id = CdiUtil.bean(SessionIdService).getSessionId() - if session_id == None: - print "U2F. Prepare for step 2. Failed to determine session_id" - return False - - authenticationService = CdiUtil.bean(AuthenticationService) - user = authenticationService.getAuthenticatedUser() - if (user == None): - print "U2F. Prepare for step 2. Failed to determine user name" - return False - - u2f_application_id = configurationAttributes.get("u2f_application_id").getValue2() - - # Check if user have registered devices - deviceRegistrationService = CdiUtil.bean(DeviceRegistrationService) - - userInum = user.getAttribute("inum") - - registrationRequest = None - authenticationRequest = None - - deviceRegistrations = deviceRegistrationService.findUserDeviceRegistrations(userInum, u2f_application_id) - if (deviceRegistrations.size() > 0): - print "U2F. Prepare for step 2. Call FIDO U2F in order to start authentication workflow" - - try: - authenticationRequestService = FidoU2fClientFactory.instance().createAuthenticationRequestService(self.metaDataConfiguration) - authenticationRequest = authenticationRequestService.startAuthentication(user.getUserId(), None, u2f_application_id, session_id.getId()) - except ClientErrorException, ex: - if (ex.getResponse().getResponseStatus() != Response.Status.NOT_FOUND): - print "U2F. Prepare for step 2. Failed to start authentication workflow. Exception:", sys.exc_info()[1] - return False - else: - print "U2F. Prepare for step 2. Call FIDO U2F in order to start registration workflow" - registrationRequestService = FidoU2fClientFactory.instance().createRegistrationRequestService(self.metaDataConfiguration) - registrationRequest = registrationRequestService.startRegistration(user.getUserId(), u2f_application_id, session_id.getId()) - - identity.setWorkingParameter("fido_u2f_authentication_request", ServerUtil.asJson(authenticationRequest)) - identity.setWorkingParameter("fido_u2f_registration_request", ServerUtil.asJson(registrationRequest)) - - return True - elif (step == 3): - print "U2F. Prepare for step 3" - - return True - else: - return False - - def getExtraParametersForStep(self, configurationAttributes, step): - return None - - def getCountAuthenticationSteps(self, configurationAttributes): - return 2 - - def getPageForStep(self, configurationAttributes, step): - if (step == 2): - #Modified for Casa compliance - return "/casa/u2f.xhtml" - - return "" - - def logout(self, configurationAttributes, requestParameters): - return True - - # Added for Casa compliance - - def hasEnrollments(self, configurationAttributes, user): - - inum = user.getAttribute("inum") - devRegService = CdiUtil.bean(DeviceRegistrationService) - app_id = configurationAttributes.get("u2f_application_id").getValue2() - userDevices = devRegService.findUserDeviceRegistrations(inum, app_id, "jansStatus") - - hasDevices = False - for device in userDevices: - if device.getStatus() != None and device.getStatus().getValue() == "active": - hasDevices=True - break - - return hasDevices From ae455c5940fad46ddb80617aa2716d9de80c9023 Mon Sep 17 00:00:00 2001 From: Arnab Dutta Date: Mon, 7 Mar 2022 13:22:01 +0530 Subject: [PATCH 019/101] feat: move health menu under Home menu #116 --- admin-ui/app/redux/actions/HealthAction.js | 11 + .../redux/actions/LicenseDetailsActions.js | 26 +++ admin-ui/app/redux/actions/types.js | 10 +- admin-ui/app/redux/api/HealthApi.js | 18 ++ admin-ui/app/redux/api/LicenseDetailsApi.js | 31 +++ admin-ui/app/redux/reducers/HealthReducer.js | 40 ++++ .../redux/reducers/LicenseDetailsReducer.js | 54 +++++ admin-ui/app/redux/reducers/index.js | 4 + admin-ui/app/redux/sagas/HealthSaga.js | 49 +++++ .../app/redux/sagas/LicenseDetailsSaga.js | 73 +++++++ admin-ui/app/redux/sagas/index.js | 4 + .../app/routes/Apps/Gluu/GluuAppSidebar.js | 12 ++ admin-ui/app/routes/Health/HealthPage.js | 97 +++++++++ .../app/routes/License/LicenseDetailsForm.js | 120 +++++++++++ .../routes/License/LicenseDetailsForm.test.js | 24 +++ .../app/routes/License/LicenseDetailsPage.js | 192 ++++++++++++++++++ .../routes/License/LicenseDetailsPage.test.js | 40 ++++ admin-ui/app/routes/License/license.js | 15 ++ admin-ui/app/routes/index.js | 4 + 19 files changed, 823 insertions(+), 1 deletion(-) create mode 100644 admin-ui/app/redux/actions/HealthAction.js create mode 100644 admin-ui/app/redux/actions/LicenseDetailsActions.js create mode 100644 admin-ui/app/redux/api/HealthApi.js create mode 100644 admin-ui/app/redux/api/LicenseDetailsApi.js create mode 100644 admin-ui/app/redux/reducers/HealthReducer.js create mode 100644 admin-ui/app/redux/reducers/LicenseDetailsReducer.js create mode 100644 admin-ui/app/redux/sagas/HealthSaga.js create mode 100644 admin-ui/app/redux/sagas/LicenseDetailsSaga.js create mode 100644 admin-ui/app/routes/Health/HealthPage.js create mode 100644 admin-ui/app/routes/License/LicenseDetailsForm.js create mode 100644 admin-ui/app/routes/License/LicenseDetailsForm.test.js create mode 100644 admin-ui/app/routes/License/LicenseDetailsPage.js create mode 100644 admin-ui/app/routes/License/LicenseDetailsPage.test.js create mode 100644 admin-ui/app/routes/License/license.js diff --git a/admin-ui/app/redux/actions/HealthAction.js b/admin-ui/app/redux/actions/HealthAction.js new file mode 100644 index 000000000..87a88c879 --- /dev/null +++ b/admin-ui/app/redux/actions/HealthAction.js @@ -0,0 +1,11 @@ +import { GET_HEALTH, GET_HEALTH_RESPONSE } from './types' + +export const getHealthStatus = (action) => ({ + type: GET_HEALTH, + payload: { action }, +}) + +export const getHealthStatusResponse = (data) => ({ + type: GET_HEALTH_RESPONSE, + payload: { data }, +}) diff --git a/admin-ui/app/redux/actions/LicenseDetailsActions.js b/admin-ui/app/redux/actions/LicenseDetailsActions.js new file mode 100644 index 000000000..487377212 --- /dev/null +++ b/admin-ui/app/redux/actions/LicenseDetailsActions.js @@ -0,0 +1,26 @@ +import { + GET_LICENSE_DETAILS, + GET_LICENSE_DETAILS_RESPONSE, + UPDATE_LICENSE_DETAILS, + UPDATE_LICENSE_DETAILS_RESPONSE, +} from './types' + +export const getLicenseDetails = (action) => ({ + type: GET_LICENSE_DETAILS, + payload: { action }, +}) + +export const getLicenseDetailsResponse = (data) => ({ + type: GET_LICENSE_DETAILS_RESPONSE, + payload: { data }, +}) + +export const updateLicenseDetails = (action) => ({ + type: UPDATE_LICENSE_DETAILS, + payload: { action }, +}) + +export const updateLicenseDetailsResponse = (data) => ({ + type: UPDATE_LICENSE_DETAILS_RESPONSE, + payload: { data }, +}) \ No newline at end of file diff --git a/admin-ui/app/redux/actions/types.js b/admin-ui/app/redux/actions/types.js index ef5efa84b..aeea155fc 100644 --- a/admin-ui/app/redux/actions/types.js +++ b/admin-ui/app/redux/actions/types.js @@ -68,4 +68,12 @@ export const ACTIVATE_LICENSE = 'ACTIVATE_LICENSE' export const ACTIVATE_LICENSE_RESPONSE = 'ACTIVATE_LICENSE_RESPONSE' //OIDC DISCOVERY export const GET_OIDC_DISCOVERY = 'GET_OIDC_DISCOVERY' -export const GET_OIDC_DISCOVERY_RESPONSE = 'GET_OIDC_DISCOVERY_RESPONSE' \ No newline at end of file +export const GET_OIDC_DISCOVERY_RESPONSE = 'GET_OIDC_DISCOVERY_RESPONSE' +// Health +export const GET_HEALTH = 'GET_HEALTH' +export const GET_HEALTH_RESPONSE = 'GET_HEALTH_RESPONSE' +//License Details +export const GET_LICENSE_DETAILS = 'GET_LICENSE_DETAILS' +export const GET_LICENSE_DETAILS_RESPONSE = 'GET_LICENSE_DETAILS_RESPONSE' +export const UPDATE_LICENSE_DETAILS = 'UPDATE_LICENSE_DETAILS' +export const UPDATE_LICENSE_DETAILS_RESPONSE = 'UPDATE_LICENSE_DETAILS_RESPONSE' \ No newline at end of file diff --git a/admin-ui/app/redux/api/HealthApi.js b/admin-ui/app/redux/api/HealthApi.js new file mode 100644 index 000000000..193b96c93 --- /dev/null +++ b/admin-ui/app/redux/api/HealthApi.js @@ -0,0 +1,18 @@ +export default class HealthApi { + constructor(api) { + this.api = api + } + + getHealthStatus = () => { + return new Promise((resolve, reject) => { + this.api.getAuthServerHealth((error, data) => { + if (error) { + console.log(e); + reject(error) + } else { + resolve(data) + } + }) + }) + } +} diff --git a/admin-ui/app/redux/api/LicenseDetailsApi.js b/admin-ui/app/redux/api/LicenseDetailsApi.js new file mode 100644 index 000000000..3c8f604eb --- /dev/null +++ b/admin-ui/app/redux/api/LicenseDetailsApi.js @@ -0,0 +1,31 @@ +export default class LicenseDetailsApi { + constructor(api) { + this.api = api + } + + getLicenseDetails = () => { + return new Promise((resolve, reject) => { + this.api.getAdminuiLicense((error, data) => { + this.handleResponse(error, reject, resolve, data) + }) + }) + } + + updateLicenseDetails = (data) => { + const options = {} + options['licenseRequest'] = data + return new Promise((resolve, reject) => { + this.api.editAdminuiLicense(options, (error, options) => { + this.handleResponse(error, reject, resolve, data) + }) + }) + } + + handleResponse(error, reject, resolve, data) { + if (error) { + reject(error) + } else { + resolve(data) + } + } +} \ No newline at end of file diff --git a/admin-ui/app/redux/reducers/HealthReducer.js b/admin-ui/app/redux/reducers/HealthReducer.js new file mode 100644 index 000000000..c86284473 --- /dev/null +++ b/admin-ui/app/redux/reducers/HealthReducer.js @@ -0,0 +1,40 @@ +import { GET_HEALTH, GET_HEALTH_RESPONSE } from '../actions/types' +import reducerRegistry from './ReducerRegistry' +const INIT_STATE = { + serverStatus: null, + dbStatus: null, + loading: false, +} + +const reducerName = 'healthReducer' + +export default function healthReducer(state = INIT_STATE, action) { + switch (action.type) { + case GET_HEALTH: + return { + ...state, + loading: true, + } + case GET_HEALTH_RESPONSE: + if (action.payload.data) { + return { + ...state, + serverStatus: action.payload.data.status, + dbStatus: action.payload.data.db_status, + loading: false, + } + } else { + return handleDefault() + } + default: + return handleDefault() + } + + function handleDefault() { + return { + ...state, + loading: false, + } + } +} +reducerRegistry.register(reducerName, healthReducer) diff --git a/admin-ui/app/redux/reducers/LicenseDetailsReducer.js b/admin-ui/app/redux/reducers/LicenseDetailsReducer.js new file mode 100644 index 000000000..4c1c0ebc0 --- /dev/null +++ b/admin-ui/app/redux/reducers/LicenseDetailsReducer.js @@ -0,0 +1,54 @@ +import { GET_LICENSE_DETAILS, GET_LICENSE_DETAILS_RESPONSE, UPDATE_LICENSE_DETAILS, UPDATE_LICENSE_DETAILS_RESPONSE } from '../actions/types' +import reducerRegistry from '../../redux/reducers/ReducerRegistry' +const INIT_STATE = { + item: {}, + loading: true, +} + +const reducerName = 'licenseDetailsReducer' + +export default function licenseDetailsReducer(state = INIT_STATE, action) { + switch (action.type) { + case GET_LICENSE_DETAILS: + return { + ...state, + loading: true, + } + case GET_LICENSE_DETAILS_RESPONSE: + if (action.payload.data) { + return { + ...state, + item: action.payload.data, + loading: false, + } + } else { + return { + ...state, + loading: false, + } + } + case UPDATE_LICENSE_DETAILS: + return { + ...state, + loading: true, + } + case UPDATE_LICENSE_DETAILS_RESPONSE: + if (action.payload.data) { + return { + ...state, + items: action.payload.data, + loading: false, + } + } else { + return { + ...state, + loading: false, + } + } + default: + return { + ...state, + } + } +} +reducerRegistry.register(reducerName, licenseDetailsReducer) diff --git a/admin-ui/app/redux/reducers/index.js b/admin-ui/app/redux/reducers/index.js index 97b5a3081..873acdfc5 100644 --- a/admin-ui/app/redux/reducers/index.js +++ b/admin-ui/app/redux/reducers/index.js @@ -2,11 +2,13 @@ * App Reducers */ import mauReducer from './MauReducer' +import healthReducer from './HealthReducer' import authReducer from './AuthReducer' import fidoReducer from './FidoReducer' import initReducer from './InitReducer' import logoutReducer from './LogoutReducer' import licenseReducer from './LicenseReducer' +import licenseDetailsReducer from './LicenseDetailsReducer' import oidcDiscoveryReducer from './OidcDiscoveryReducer' const appReducers = { @@ -17,6 +19,8 @@ const appReducers = { licenseReducer, oidcDiscoveryReducer, mauReducer, + healthReducer, + licenseDetailsReducer, } export default appReducers diff --git a/admin-ui/app/redux/sagas/HealthSaga.js b/admin-ui/app/redux/sagas/HealthSaga.js new file mode 100644 index 000000000..28bad31e5 --- /dev/null +++ b/admin-ui/app/redux/sagas/HealthSaga.js @@ -0,0 +1,49 @@ +import { call, all, put, fork, takeLatest, select } from 'redux-saga/effects' +import { + isFourZeroOneError, + addAdditionalData, +} from '../../utils/TokenController' +import { getHealthStatusResponse } from '../actions/HealthAction' +import { getAPIAccessToken } from '../actions/AuthActions' +import { postUserAction} from '../api/backend-api' +import { GET_HEALTH } from '../actions/types' +import HealthApi from '../api/HealthApi' +import { getClient } from '../api/base' +import { initAudit } from '../sagas/SagaUtils' +const JansConfigApi = require('jans_config_api') + +function* newFunction() { + const token = yield select((state) => state.authReducer.token.access_token) + const issuer = yield select((state) => state.authReducer.issuer) + const api = new JansConfigApi.AuthServerHealthCheckApi( + getClient(JansConfigApi, token, issuer), + ) + return new HealthApi(api) +} + +export function* getHealthStatus({ payload }) { + const audit = yield* initAudit() + try { + payload = payload ? payload : { action: {} } + addAdditionalData(audit, 'FETCH', 'Health', payload) + const healthApi = yield* newFunction() + const data = yield call(healthApi.getHealthStatus, payload.action.action_data) + yield put(getHealthStatusResponse(data)) + yield call(postUserAction, audit) + } catch (e) { + + yield put(getHealthStatusResponse(null)) + if (isFourZeroOneError(e)) { + const jwt = yield select((state) => state.authReducer.userinfo_jwt) + yield put(getAPIAccessToken(jwt)) + } + } +} + +export function* watchGetHealthStatus() { + yield takeLatest(GET_HEALTH, getHealthStatus) +} + +export default function* rootSaga() { + yield all([fork(watchGetHealthStatus)]) +} diff --git a/admin-ui/app/redux/sagas/LicenseDetailsSaga.js b/admin-ui/app/redux/sagas/LicenseDetailsSaga.js new file mode 100644 index 000000000..3d4b48399 --- /dev/null +++ b/admin-ui/app/redux/sagas/LicenseDetailsSaga.js @@ -0,0 +1,73 @@ +import { all, call, fork, put, takeEvery, select } from 'redux-saga/effects' +import { GET_LICENSE_DETAILS, UPDATE_LICENSE_DETAILS } from '../actions/types' +import { + getLicenseDetailsResponse, + updateLicenseDetailsResponse, +} from '../actions/LicenseDetailsActions' +import { getClient } from '../../redux/api/base' +import LicenseDetailsApi from '../api/LicenseDetailsApi' +const JansConfigApi = require('jans_config_api') +import { initAudit } from '../../redux/sagas/SagaUtils' +import { + isFourZeroOneError, + addAdditionalData, +} from '../../utils/TokenController' +import { getAPIAccessToken } from '../../redux/actions/AuthActions' + +function* newFunction() { + const token = yield select((state) => state.authReducer.token.access_token) + const issuer = yield select((state) => state.authReducer.issuer) + const api = new JansConfigApi.AdminUILicenseApi( + getClient(JansConfigApi, token, issuer), + ) + return new LicenseDetailsApi(api) +} + + +export function* getLicenseDetailsWorker({ payload }) { + const audit = yield* initAudit() + try { + //addAdditionalData(audit, FETCH, GET_LICENSE_DETAILS, payload) + const licenseApi = yield* newFunction() + const data = yield call(licenseApi.getLicenseDetails) + yield put(getLicenseDetailsResponse(data)) + yield call(postUserAction, audit) + } catch (e) { + yield put(getLicenseDetailsResponse(null)) + if (isFourZeroOneError(e)) { + const jwt = yield select((state) => state.authReducer.userinfo_jwt) + yield put(getAPIAccessToken(jwt)) + } + } +} + +export function* updateLicenseDetailsWorker({ payload }) { + const audit = yield* initAudit() + try { + //addAdditionalData(audit, UPDATE, UPDATE_LICENSE_DETAILS, payload) + const roleApi = yield* newFunction() + const data = yield call(roleApi.updateLicenseDetails, payload.action.action_data) + yield put(updateLicenseDetailsResponse(data)) + yield call(postUserAction, audit) + } catch (e) { + yield put(updateLicenseDetailsResponse(null)) + if (isFourZeroOneError(e)) { + const jwt = yield select((state) => state.authReducer.userinfo_jwt) + yield put(getAPIAccessToken(jwt)) + } + } +} + + +export function* getLicenseWatcher() { + + yield takeEvery(GET_LICENSE_DETAILS, getLicenseDetailsWorker) +} + +export function* updateLicenseWatcher() { + yield takeEvery(UPDATE_LICENSE_DETAILS, updateLicenseDetailsWorker) +} + +export default function* rootSaga() { + yield all([fork(getLicenseWatcher), fork(updateLicenseWatcher)]) +} \ No newline at end of file diff --git a/admin-ui/app/redux/sagas/index.js b/admin-ui/app/redux/sagas/index.js index ab60195a3..e48540a5f 100644 --- a/admin-ui/app/redux/sagas/index.js +++ b/admin-ui/app/redux/sagas/index.js @@ -5,10 +5,12 @@ import { all } from 'redux-saga/effects' // sagas import mauSaga from './MauSaga' +import healthSaga from './HealthSaga' import authSagas from './AuthSaga' import fidoSaga from './FidoSaga' import initSaga from './InitSaga' import licenseSaga from './LicenseSaga' +import licenseDetailsSaga from './LicenseDetailsSaga' import oidcDiscoverySaga from './OidcDiscoverySaga' import process from '../../../plugins/PluginSagasResolver' @@ -23,6 +25,8 @@ export default function* rootSaga() { licenseSaga(), oidcDiscoverySaga(), mauSaga(), + healthSaga(), + licenseDetailsSaga(), ], pluginSagaArr, ), diff --git a/admin-ui/app/routes/Apps/Gluu/GluuAppSidebar.js b/admin-ui/app/routes/Apps/Gluu/GluuAppSidebar.js index 3e2b70b3a..3fd11fc83 100644 --- a/admin-ui/app/routes/Apps/Gluu/GluuAppSidebar.js +++ b/admin-ui/app/routes/Apps/Gluu/GluuAppSidebar.js @@ -49,6 +49,18 @@ function GluuAppSidebar({ scopes }) { textStyle={{ fontSize: '18px', fontWeight: '600' }} exact /> + + {/* -------- Plugins ---------*/} diff --git a/admin-ui/app/routes/Health/HealthPage.js b/admin-ui/app/routes/Health/HealthPage.js new file mode 100644 index 000000000..731f71389 --- /dev/null +++ b/admin-ui/app/routes/Health/HealthPage.js @@ -0,0 +1,97 @@ +import React, { useEffect } from 'react' +import { + Container, + CardBody, + Card, + Badge, + CardHeader, + FormGroup, +} from '../../components' +import { useTranslation } from 'react-i18next' +import applicationStyle from '../../routes/Apps/Gluu/styles/applicationstyle' +import { buildPayload } from '../../utils/PermChecker' +import { connect } from 'react-redux' +import { getHealthStatus } from '../../redux/actions/HealthAction' +import GluuRibbon from '../../routes/Apps/Gluu/GluuRibbon' + +function HealthPage({ serverStatus, dbStatus, dispatch }) { + const { t } = useTranslation() + const userAction = {} + const options = {} + + useEffect(() => { + fetchHealthInfo(userAction, options, dispatch) + }, []) + + function fetchHealthInfo() { + buildPayload(userAction, 'GET Health Status', options) + dispatch(getHealthStatus(userAction)) + } + + function getColor(status) { + return isUp(status) ? 'primary' : 'danger' + } + + function isUp(status) { + if (status) { + return ( + status.toUpperCase() === 'ONLINE'.toUpperCase() || + status.toUpperCase() === 'RUNNING'.toUpperCase() + ) + } + return false + } + + return ( + + + + + + + + + + {t('titles.oauth_server_status_title')} + + + {serverStatus && ( + {serverStatus} + )} + + + + + {t('titles.database_status_title')} + + + {dbStatus && {dbStatus}} + + + + + + ) +} + +const mapStateToProps = (state) => { + return { + serverStatus: state.healthReducer.serverStatus, + dbStatus: state.healthReducer.dbStatus, + loading: state.healthReducer.loading, + permissions: state.authReducer.permissions, + } +} +export default connect(mapStateToProps)(HealthPage) diff --git a/admin-ui/app/routes/License/LicenseDetailsForm.js b/admin-ui/app/routes/License/LicenseDetailsForm.js new file mode 100644 index 000000000..a1543419d --- /dev/null +++ b/admin-ui/app/routes/License/LicenseDetailsForm.js @@ -0,0 +1,120 @@ +import React, { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import DatePicker from 'react-datepicker' +import GluuLabel from '../../routes/Apps/Gluu/GluuLabel' +import GluuCommitFooter from '../../routes/Apps/Gluu/GluuCommitFooter' +import GluuCommitDialog from '../../routes/Apps/Gluu/GluuCommitDialog' +import GluuToogle from '../../routes/Apps/Gluu/GluuToogle' +import { LICENSE } from '../../utils/ApiResources' +import GluuTooltip from '../../routes/Apps/Gluu/GluuTooltip' +import { + Col, + Form, + FormGroup, + CustomInput, + Accordion, +} from '../../components' +import { Formik } from 'formik' + +function LicenseDetailsForm({ item, handleSubmit }) { + const { t } = useTranslation() + const [validityPeriod, setValidityPeriod] = useState( + !!item.validityPeriod ? new Date(item.validityPeriod) : new Date(), + ) + const [modal, setModal] = useState(false) + + useEffect(() => { + setValidityPeriod(new Date(item.validityPeriod)) + }, [item.validityPeriod]) + + function toggle() { + setModal(!modal) + } + + function submitForm() { + toggle() + document.getElementsByClassName('UserActionSubmitButton')[0].click() + } + + const initialValues = { + maxActivations: item.maxActivations, + licenseActive: item.licenseActive, + validityPeriod: item.validityPeriod, + } + return ( + <> + + + {t('fields.licenseDetails').toUpperCase()} + + + { + values.validityPeriod = validityPeriod + handleSubmit(values) + }} + > + {(formik) => ( +
+ + + + + setValidityPeriod(date)} + /> + + + + + + + + + + + + + + + + + + + + + + + + )} +
+
+
+ + ) +} + +export default LicenseDetailsForm diff --git a/admin-ui/app/routes/License/LicenseDetailsForm.test.js b/admin-ui/app/routes/License/LicenseDetailsForm.test.js new file mode 100644 index 000000000..3b12924f5 --- /dev/null +++ b/admin-ui/app/routes/License/LicenseDetailsForm.test.js @@ -0,0 +1,24 @@ +import React from 'react' +import { render, screen } from '@testing-library/react' +import LicenseDetailsForm from './LicenseDetailsForm' +import license from "./license" +import i18n from '../../i18n' +import { I18nextProvider } from 'react-i18next' + +const Wrapper = ({ children }) => ( + {children} +) +const permissions = [ + 'https://jans.io/oauth/config/attributes.readonly', + 'https://jans.io/oauth/config/attributes.write', + 'https://jans.io/oauth/config/attributes.delete', +] +const item = license +it('Should render the license detail page properly', () => { + render(, { + wrapper: Wrapper, + }) + screen.getByText(/License Valid Upto/) + screen.getByText(/Maximum Activations/) + screen.getByText(/Is License Active/) +}) diff --git a/admin-ui/app/routes/License/LicenseDetailsPage.js b/admin-ui/app/routes/License/LicenseDetailsPage.js new file mode 100644 index 000000000..ccb5e5fb3 --- /dev/null +++ b/admin-ui/app/routes/License/LicenseDetailsPage.js @@ -0,0 +1,192 @@ +import React, { useEffect } from 'react' +import { connect } from 'react-redux' +import LicenseDetailsForm from './LicenseDetailsForm' +import GluuRibbon from '../../routes/Apps/Gluu/GluuRibbon' +import GluuFormDetailRow from '../../routes/Apps/Gluu/GluuFormDetailRow' +import { LICENSE } from '../../utils/ApiResources' +import { + getLicenseDetails, + updateLicenseDetails, +} from '../../redux/actions/LicenseDetailsActions' +import { + Card, + CardBody, + CardTitle, + Container, + FormGroup, + Row, + Col, +} from '../../components' +import { + buildPayload, +} from '../../utils/PermChecker' +import GluuLoader from '../../routes/Apps/Gluu/GluuLoader' +import Alert from '@material-ui/lab/Alert' + +const FETCHING_LICENSE_DETAILS = 'Fetch license details' + +function LicenseDetailsPage({ item, loading, dispatch }) { + const userAction = {} + const options = {} + + useEffect(() => { + buildPayload(userAction, FETCHING_LICENSE_DETAILS, options) + dispatch(getLicenseDetails({})) + }, []) + + function formatDate(date) { + if(date == undefined) { + return ''; + } + if(date.length > 10) { + return date.substring(0, 10); + } + return ''; + + } + return ( + + + + + + + + + + {item.licenseEnabled ? ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) : ( + + {!loading && + 'The License API is not enabled for this application.'} + + )} + + + + + ) +} + +const mapStateToProps = (state) => { + return { + item: state.licenseDetailsReducer.item, + loading: state.licenseDetailsReducer.loading, + } +} +export default connect(mapStateToProps)(LicenseDetailsPage) diff --git a/admin-ui/app/routes/License/LicenseDetailsPage.test.js b/admin-ui/app/routes/License/LicenseDetailsPage.test.js new file mode 100644 index 000000000..73624d875 --- /dev/null +++ b/admin-ui/app/routes/License/LicenseDetailsPage.test.js @@ -0,0 +1,40 @@ +import React from 'react' +import { render, screen } from '@testing-library/react' +import LicenseDetailsPage from './LicenseDetailsPage' +import { createStore, combineReducers } from 'redux' +import { Provider } from 'react-redux' +import i18n from '../../i18n' +import { I18nextProvider } from 'react-i18next' +import license from "./license" + +const permissions = [ + 'https://jans.io/oauth/config/attributes.readonly', + 'https://jans.io/oauth/config/attributes.write', + 'https://jans.io/oauth/config/attributes.delete', +] +const INIT_LICENSE_DETAIL_STATE = { + item: license, + loading: false, +} +const store = createStore( + combineReducers({ + licenseDetailsReducer: (state = INIT_LICENSE_DETAIL_STATE) => state, + noReducer: (state = {}) => state, + }), +) + +const Wrapper = ({ children }) => ( + + {children} + +) + +it('Should render the Custom Script add page properly', () => { + render(, { wrapper: Wrapper }) + const key = license.licenseKey + screen.getByText(/Product Name/) + screen.getByText(/Product Code/) + screen.getByText(/License Type/) + screen.getByText(/License Key/) + screen.getByText(key) +}) diff --git a/admin-ui/app/routes/License/license.js b/admin-ui/app/routes/License/license.js new file mode 100644 index 000000000..571745aa1 --- /dev/null +++ b/admin-ui/app/routes/License/license.js @@ -0,0 +1,15 @@ +export const license = { + companyName: "Gluu", + customerEmail: "arnab@gluu.org", + customerFirstName: "Arnab", + customerLastName: "Dutta", + licenseActive: true, + licenseEnable: true, + licenseKey: "GFXJ-AB8B-YDJK-L4AD", + licenseType: "TIME_LIMITED", + maxActivations: 14, + productCode: "adminui001", + productName: "Gluu Admin UI", + validityPeriod: "2022-10-01T00:00Z", +} +export default license \ No newline at end of file diff --git a/admin-ui/app/routes/index.js b/admin-ui/app/routes/index.js index 5672042a5..9ae0d7e04 100755 --- a/admin-ui/app/routes/index.js +++ b/admin-ui/app/routes/index.js @@ -5,6 +5,8 @@ import { useSelector } from 'react-redux' // ----------- Pages Imports --------------- import Reports from './Dashboards/Reports' import DashboardPage from './Dashboards/DashboardPage' +import HealthPage from './Health/HealthPage' +import LicenseDetailsPage from './License/LicenseDetailsPage' import NavbarOnly from './Layouts/NavbarOnly' import SidebarDefault from './Layouts/SidebarDefault' import SidebarA from './Layouts/SidebarA' @@ -35,6 +37,8 @@ export const RoutedContent = () => { + + {/* Layouts */} From 94ea401c5b17f394a947a9ca668494d2b110123b Mon Sep 17 00:00:00 2001 From: Arnab Dutta Date: Mon, 7 Mar 2022 16:25:06 +0530 Subject: [PATCH 020/101] feat: move health menu under Home menu #116 --- admin-ui/app/redux/api/HealthApi.js | 1 - admin-ui/app/redux/sagas/LicenseDetailsSaga.js | 1 + admin-ui/app/routes/License/LicenseDetailsForm.test.js | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/admin-ui/app/redux/api/HealthApi.js b/admin-ui/app/redux/api/HealthApi.js index 193b96c93..89533c606 100644 --- a/admin-ui/app/redux/api/HealthApi.js +++ b/admin-ui/app/redux/api/HealthApi.js @@ -7,7 +7,6 @@ export default class HealthApi { return new Promise((resolve, reject) => { this.api.getAuthServerHealth((error, data) => { if (error) { - console.log(e); reject(error) } else { resolve(data) diff --git a/admin-ui/app/redux/sagas/LicenseDetailsSaga.js b/admin-ui/app/redux/sagas/LicenseDetailsSaga.js index 3d4b48399..baf8393c7 100644 --- a/admin-ui/app/redux/sagas/LicenseDetailsSaga.js +++ b/admin-ui/app/redux/sagas/LicenseDetailsSaga.js @@ -8,6 +8,7 @@ import { getClient } from '../../redux/api/base' import LicenseDetailsApi from '../api/LicenseDetailsApi' const JansConfigApi = require('jans_config_api') import { initAudit } from '../../redux/sagas/SagaUtils' +import { postUserAction } from '../../redux/api/backend-api' import { isFourZeroOneError, addAdditionalData, diff --git a/admin-ui/app/routes/License/LicenseDetailsForm.test.js b/admin-ui/app/routes/License/LicenseDetailsForm.test.js index 3b12924f5..a69fcd9ca 100644 --- a/admin-ui/app/routes/License/LicenseDetailsForm.test.js +++ b/admin-ui/app/routes/License/LicenseDetailsForm.test.js @@ -18,6 +18,7 @@ it('Should render the license detail page properly', () => { render(, { wrapper: Wrapper, }) + expect(screen.getByText(/License Valid Upto/)).toBeInTheDocument() screen.getByText(/License Valid Upto/) screen.getByText(/Maximum Activations/) screen.getByText(/Is License Active/) From 75d013879cc4d5d9b95e6fbdbb57dc1b591cb1bb Mon Sep 17 00:00:00 2001 From: Arnab Dutta Date: Mon, 7 Mar 2022 16:44:23 +0530 Subject: [PATCH 021/101] feat: move health menu under Home menu #116 --- .../Configuration/LicenseDetailsForm.js | 120 ----------- .../Configuration/LicenseDetailsForm.test.js | 24 --- .../Configuration/LicenseDetailsPage.js | 193 ------------------ .../Configuration/LicenseDetailsPage.test.js | 40 ---- .../admin/components/Configuration/license.js | 15 -- admin-ui/plugins/admin/plugin-metadata.js | 15 -- .../redux/actions/LicenseDetailsActions.js | 26 --- .../admin/redux/api/LicenseDetailsApi.js | 31 --- .../redux/reducers/LicenseDetailsReducer.js | 54 ----- .../admin/redux/sagas/LicenseDetailsSaga.js | 73 ------- 10 files changed, 591 deletions(-) delete mode 100644 admin-ui/plugins/admin/components/Configuration/LicenseDetailsForm.js delete mode 100644 admin-ui/plugins/admin/components/Configuration/LicenseDetailsForm.test.js delete mode 100644 admin-ui/plugins/admin/components/Configuration/LicenseDetailsPage.js delete mode 100644 admin-ui/plugins/admin/components/Configuration/LicenseDetailsPage.test.js delete mode 100644 admin-ui/plugins/admin/components/Configuration/license.js delete mode 100644 admin-ui/plugins/admin/redux/actions/LicenseDetailsActions.js delete mode 100644 admin-ui/plugins/admin/redux/api/LicenseDetailsApi.js delete mode 100644 admin-ui/plugins/admin/redux/reducers/LicenseDetailsReducer.js delete mode 100644 admin-ui/plugins/admin/redux/sagas/LicenseDetailsSaga.js diff --git a/admin-ui/plugins/admin/components/Configuration/LicenseDetailsForm.js b/admin-ui/plugins/admin/components/Configuration/LicenseDetailsForm.js deleted file mode 100644 index 37465f020..000000000 --- a/admin-ui/plugins/admin/components/Configuration/LicenseDetailsForm.js +++ /dev/null @@ -1,120 +0,0 @@ -import React, { useEffect, useState } from 'react' -import { useTranslation } from 'react-i18next' -import DatePicker from 'react-datepicker' -import GluuLabel from '../../../../app/routes/Apps/Gluu/GluuLabel' -import GluuCommitFooter from '../../../../app/routes/Apps/Gluu/GluuCommitFooter' -import GluuCommitDialog from '../../../../app/routes/Apps/Gluu/GluuCommitDialog' -import GluuToogle from '../../../../app/routes/Apps/Gluu/GluuToogle' -import { LICENSE } from '../../../../app/utils/ApiResources' -import GluuTooltip from '../../../../app/routes/Apps/Gluu/GluuTooltip' -import { - Col, - Form, - FormGroup, - CustomInput, - Accordion, -} from '../../../../app/components' -import { Formik } from 'formik' - -function LicenseDetailsForm({ item, handleSubmit }) { - const { t } = useTranslation() - const [validityPeriod, setValidityPeriod] = useState( - !!item.validityPeriod ? new Date(item.validityPeriod) : new Date(), - ) - const [modal, setModal] = useState(false) - - useEffect(() => { - setValidityPeriod(new Date(item.validityPeriod)) - }, [item.validityPeriod]) - - function toggle() { - setModal(!modal) - } - - function submitForm() { - toggle() - document.getElementsByClassName('UserActionSubmitButton')[0].click() - } - - const initialValues = { - maxActivations: item.maxActivations, - licenseActive: item.licenseActive, - validityPeriod: item.validityPeriod, - } - return ( - <> - - - {t('fields.licenseDetails').toUpperCase()} - - - { - values.validityPeriod = validityPeriod - handleSubmit(values) - }} - > - {(formik) => ( -
- - - - - setValidityPeriod(date)} - /> - - - - - - - - - - - - - - - - - - - - - - - - )} -
-
-
- - ) -} - -export default LicenseDetailsForm diff --git a/admin-ui/plugins/admin/components/Configuration/LicenseDetailsForm.test.js b/admin-ui/plugins/admin/components/Configuration/LicenseDetailsForm.test.js deleted file mode 100644 index 76deb4b27..000000000 --- a/admin-ui/plugins/admin/components/Configuration/LicenseDetailsForm.test.js +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react' -import { render, screen } from '@testing-library/react' -import LicenseDetailsForm from './LicenseDetailsForm' -import license from "./license" -import i18n from '../../../../app/i18n' -import { I18nextProvider } from 'react-i18next' - -const Wrapper = ({ children }) => ( - {children} -) -const permissions = [ - 'https://jans.io/oauth/config/attributes.readonly', - 'https://jans.io/oauth/config/attributes.write', - 'https://jans.io/oauth/config/attributes.delete', -] -const item = license -it('Should render the license detail page properly', () => { - render(, { - wrapper: Wrapper, - }) - screen.getByText(/License Valid Upto/) - screen.getByText(/Maximum Activations/) - screen.getByText(/Is License Active/) -}) diff --git a/admin-ui/plugins/admin/components/Configuration/LicenseDetailsPage.js b/admin-ui/plugins/admin/components/Configuration/LicenseDetailsPage.js deleted file mode 100644 index 3199b0a20..000000000 --- a/admin-ui/plugins/admin/components/Configuration/LicenseDetailsPage.js +++ /dev/null @@ -1,193 +0,0 @@ -import React, { useEffect } from 'react' -import { connect } from 'react-redux' -import LicenseDetailsForm from './LicenseDetailsForm' -import GluuRibbon from '../../../../app/routes/Apps/Gluu/GluuRibbon' -import GluuFormDetailRow from '../../../../app/routes/Apps/Gluu/GluuFormDetailRow' -import { LICENSE } from '../../../../app/utils/ApiResources' -import { - getLicenseDetails, - updateLicenseDetails, -} from '../../redux/actions/LicenseDetailsActions' -import { - Card, - CardBody, - CardTitle, - Container, - FormGroup, - Row, - Col, -} from '../../../../app/components' -import { - buildPayload, -} from '../../../../app/utils/PermChecker' -import GluuLoader from '../../../../app/routes/Apps/Gluu/GluuLoader' -import Alert from '@material-ui/lab/Alert' -import { - FETCHING_LICENSE_DETAILS, -} from '../../common/Constants' - -function LicenseDetailsPage({ item, loading, dispatch }) { - const userAction = {} - const options = {} - - useEffect(() => { - buildPayload(userAction, FETCHING_LICENSE_DETAILS, options) - dispatch(getLicenseDetails({})) - }, []) - - function formatDate(date) { - if(date == undefined) { - return ''; - } - if(date.length > 10) { - return date.substring(0, 10); - } - return ''; - - } - return ( - - - - - - - - - - {item.licenseEnabled ? ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) : ( - - {!loading && - 'The License API is not enabled for this application.'} - - )} - - - - - ) -} - -const mapStateToProps = (state) => { - return { - item: state.licenseDetailsReducer.item, - loading: state.licenseDetailsReducer.loading, - } -} -export default connect(mapStateToProps)(LicenseDetailsPage) diff --git a/admin-ui/plugins/admin/components/Configuration/LicenseDetailsPage.test.js b/admin-ui/plugins/admin/components/Configuration/LicenseDetailsPage.test.js deleted file mode 100644 index e855c3c56..000000000 --- a/admin-ui/plugins/admin/components/Configuration/LicenseDetailsPage.test.js +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react' -import { render, screen } from '@testing-library/react' -import LicenseDetailsPage from './LicenseDetailsPage' -import { createStore, combineReducers } from 'redux' -import { Provider } from 'react-redux' -import i18n from '../../../../app/i18n' -import { I18nextProvider } from 'react-i18next' -import license from "./license" - -const permissions = [ - 'https://jans.io/oauth/config/attributes.readonly', - 'https://jans.io/oauth/config/attributes.write', - 'https://jans.io/oauth/config/attributes.delete', -] -const INIT_LICENSE_DETAIL_STATE = { - item: license, - loading: false, -} -const store = createStore( - combineReducers({ - licenseDetailsReducer: (state = INIT_LICENSE_DETAIL_STATE) => state, - noReducer: (state = {}) => state, - }), -) - -const Wrapper = ({ children }) => ( - - {children} - -) - -it('Should render the Custom Script add page properly', () => { - render(, { wrapper: Wrapper }) - const key = license.licenseKey - screen.getByText(/Product Name/) - screen.getByText(/Product Code/) - screen.getByText(/License Type/) - screen.getByText(/License Key/) - screen.getByText(key) -}) diff --git a/admin-ui/plugins/admin/components/Configuration/license.js b/admin-ui/plugins/admin/components/Configuration/license.js deleted file mode 100644 index 571745aa1..000000000 --- a/admin-ui/plugins/admin/components/Configuration/license.js +++ /dev/null @@ -1,15 +0,0 @@ -export const license = { - companyName: "Gluu", - customerEmail: "arnab@gluu.org", - customerFirstName: "Arnab", - customerLastName: "Dutta", - licenseActive: true, - licenseEnable: true, - licenseKey: "GFXJ-AB8B-YDJK-L4AD", - licenseType: "TIME_LIMITED", - maxActivations: 14, - productCode: "adminui001", - productName: "Gluu Admin UI", - validityPeriod: "2022-10-01T00:00Z", -} -export default license \ No newline at end of file diff --git a/admin-ui/plugins/admin/plugin-metadata.js b/admin-ui/plugins/admin/plugin-metadata.js index 270f7f258..47e1844a5 100644 --- a/admin-ui/plugins/admin/plugin-metadata.js +++ b/admin-ui/plugins/admin/plugin-metadata.js @@ -1,6 +1,5 @@ import HealthPage from './components/Health/HealthPage' import ReportPage from './components/Reports/ReportPage' -import LicenseDetailsPage from './components/Configuration/LicenseDetailsPage' import UiRoleListPage from './components/Roles/UiRoleListPage' import UiPermListPage from './components/Permissions/UiPermListPage' import MappingPage from './components/Mapping/MappingPage' @@ -10,7 +9,6 @@ import CustomScriptEditPage from './components/CustomScripts/CustomScriptEditPag import SettingsPage from './components/Settings/SettingsPage' import MauGraph from './components/MAU/MauGraph' import scriptSaga from './redux/sagas/CustomScriptSaga' -import licenseDetailsSaga from './redux/sagas/LicenseDetailsSaga' import apiRoleSaga from './redux/sagas/ApiRoleSaga' import apiPermissionSaga from './redux/sagas/ApiPermissionSaga' import mappingSaga from './redux/sagas/MappingSaga' @@ -18,7 +16,6 @@ import mappingSaga from './redux/sagas/MappingSaga' import scriptReducer from './redux/reducers/CustomScriptReducer' import apiRoleReducer from './redux/reducers/ApiRoleReducer' import apiPermissionReducer from './redux/reducers/ApiPermissionReducer' -import licenseDetailsReducer from './redux/reducers/LicenseDetailsReducer' import mappingReducer from './redux/reducers/MappingReducer' import { ACR_READ, @@ -37,11 +34,6 @@ const pluginMetadata = { title: 'menus.adminui', icon: 'fa-cubes', children: [ - { - title: 'menus.licenseDetails', - path: PLUGIN_BASE_APTH + '/licenseDetails', - permission: ACR_READ, - }, { title: 'menus.config-api', children: [ @@ -131,22 +123,15 @@ const pluginMetadata = { path: PLUGIN_BASE_APTH + '/settings', permission: ACR_READ, }, - { - component: LicenseDetailsPage, - path: PLUGIN_BASE_APTH + '/licenseDetails', - permission: ACR_READ, - }, ], reducers: [ { name: 'scriptReducer', reducer: scriptReducer }, { name: 'apiRoleReducer', reducer: apiRoleReducer }, { name: 'apiPermissionReducer', reducer: apiPermissionReducer }, - { name: 'licenseDetailsReducer', reducer: licenseDetailsReducer }, { name: 'mappingReducer', reducer: mappingReducer }, ], sagas: [ scriptSaga(), - licenseDetailsSaga(), apiRoleSaga(), apiPermissionSaga(), mappingSaga(), diff --git a/admin-ui/plugins/admin/redux/actions/LicenseDetailsActions.js b/admin-ui/plugins/admin/redux/actions/LicenseDetailsActions.js deleted file mode 100644 index 487377212..000000000 --- a/admin-ui/plugins/admin/redux/actions/LicenseDetailsActions.js +++ /dev/null @@ -1,26 +0,0 @@ -import { - GET_LICENSE_DETAILS, - GET_LICENSE_DETAILS_RESPONSE, - UPDATE_LICENSE_DETAILS, - UPDATE_LICENSE_DETAILS_RESPONSE, -} from './types' - -export const getLicenseDetails = (action) => ({ - type: GET_LICENSE_DETAILS, - payload: { action }, -}) - -export const getLicenseDetailsResponse = (data) => ({ - type: GET_LICENSE_DETAILS_RESPONSE, - payload: { data }, -}) - -export const updateLicenseDetails = (action) => ({ - type: UPDATE_LICENSE_DETAILS, - payload: { action }, -}) - -export const updateLicenseDetailsResponse = (data) => ({ - type: UPDATE_LICENSE_DETAILS_RESPONSE, - payload: { data }, -}) \ No newline at end of file diff --git a/admin-ui/plugins/admin/redux/api/LicenseDetailsApi.js b/admin-ui/plugins/admin/redux/api/LicenseDetailsApi.js deleted file mode 100644 index 3c8f604eb..000000000 --- a/admin-ui/plugins/admin/redux/api/LicenseDetailsApi.js +++ /dev/null @@ -1,31 +0,0 @@ -export default class LicenseDetailsApi { - constructor(api) { - this.api = api - } - - getLicenseDetails = () => { - return new Promise((resolve, reject) => { - this.api.getAdminuiLicense((error, data) => { - this.handleResponse(error, reject, resolve, data) - }) - }) - } - - updateLicenseDetails = (data) => { - const options = {} - options['licenseRequest'] = data - return new Promise((resolve, reject) => { - this.api.editAdminuiLicense(options, (error, options) => { - this.handleResponse(error, reject, resolve, data) - }) - }) - } - - handleResponse(error, reject, resolve, data) { - if (error) { - reject(error) - } else { - resolve(data) - } - } -} \ No newline at end of file diff --git a/admin-ui/plugins/admin/redux/reducers/LicenseDetailsReducer.js b/admin-ui/plugins/admin/redux/reducers/LicenseDetailsReducer.js deleted file mode 100644 index 4f40aae7f..000000000 --- a/admin-ui/plugins/admin/redux/reducers/LicenseDetailsReducer.js +++ /dev/null @@ -1,54 +0,0 @@ -import { GET_LICENSE_DETAILS, GET_LICENSE_DETAILS_RESPONSE, UPDATE_LICENSE_DETAILS, UPDATE_LICENSE_DETAILS_RESPONSE } from '../actions/types' -import reducerRegistry from '../../../../app/redux/reducers/ReducerRegistry' -const INIT_STATE = { - item: {}, - loading: true, -} - -const reducerName = 'licenseDetailsReducer' - -export default function licenseDetailsReducer(state = INIT_STATE, action) { - switch (action.type) { - case GET_LICENSE_DETAILS: - return { - ...state, - loading: true, - } - case GET_LICENSE_DETAILS_RESPONSE: - if (action.payload.data) { - return { - ...state, - item: action.payload.data, - loading: false, - } - } else { - return { - ...state, - loading: false, - } - } - case UPDATE_LICENSE_DETAILS: - return { - ...state, - loading: true, - } - case UPDATE_LICENSE_DETAILS_RESPONSE: - if (action.payload.data) { - return { - ...state, - items: action.payload.data, - loading: false, - } - } else { - return { - ...state, - loading: false, - } - } - default: - return { - ...state, - } - } -} -reducerRegistry.register(reducerName, licenseDetailsReducer) diff --git a/admin-ui/plugins/admin/redux/sagas/LicenseDetailsSaga.js b/admin-ui/plugins/admin/redux/sagas/LicenseDetailsSaga.js deleted file mode 100644 index 6f4f5a573..000000000 --- a/admin-ui/plugins/admin/redux/sagas/LicenseDetailsSaga.js +++ /dev/null @@ -1,73 +0,0 @@ -import { all, call, fork, put, takeEvery, select } from 'redux-saga/effects' -import { GET_LICENSE_DETAILS, UPDATE_LICENSE_DETAILS } from '../actions/types' -import { - getLicenseDetailsResponse, - updateLicenseDetailsResponse, -} from '../actions/LicenseDetailsActions' -import { getClient } from '../../../../app/redux/api/base' -import LicenseDetailsApi from '../api/LicenseDetailsApi' -const JansConfigApi = require('jans_config_api') -import { initAudit } from '../../../../app/redux/sagas/SagaUtils' -import { - isFourZeroOneError, - addAdditionalData, -} from '../../../../app/utils/TokenController' -import { getAPIAccessToken } from '../../../../app/redux/actions/AuthActions' - -function* newFunction() { - const token = yield select((state) => state.authReducer.token.access_token) - const issuer = yield select((state) => state.authReducer.issuer) - const api = new JansConfigApi.AdminUILicenseApi( - getClient(JansConfigApi, token, issuer), - ) - return new LicenseDetailsApi(api) -} - - -export function* getLicenseDetailsWorker({ payload }) { - const audit = yield* initAudit() - try { - //addAdditionalData(audit, FETCH, GET_LICENSE_DETAILS, payload) - const licenseApi = yield* newFunction() - const data = yield call(licenseApi.getLicenseDetails) - yield put(getLicenseDetailsResponse(data)) - yield call(postUserAction, audit) - } catch (e) { - yield put(getLicenseDetailsResponse(null)) - if (isFourZeroOneError(e)) { - const jwt = yield select((state) => state.authReducer.userinfo_jwt) - yield put(getAPIAccessToken(jwt)) - } - } -} - -export function* updateLicenseDetailsWorker({ payload }) { - const audit = yield* initAudit() - try { - //addAdditionalData(audit, UPDATE, UPDATE_LICENSE_DETAILS, payload) - const roleApi = yield* newFunction() - const data = yield call(roleApi.updateLicenseDetails, payload.action.action_data) - yield put(updateLicenseDetailsResponse(data)) - yield call(postUserAction, audit) - } catch (e) { - yield put(updateLicenseDetailsResponse(null)) - if (isFourZeroOneError(e)) { - const jwt = yield select((state) => state.authReducer.userinfo_jwt) - yield put(getAPIAccessToken(jwt)) - } - } -} - - -export function* getLicenseWatcher() { - - yield takeEvery(GET_LICENSE_DETAILS, getLicenseDetailsWorker) -} - -export function* updateLicenseWatcher() { - yield takeEvery(UPDATE_LICENSE_DETAILS, updateLicenseDetailsWorker) -} - -export default function* rootSaga() { - yield all([fork(getLicenseWatcher), fork(updateLicenseWatcher)]) -} \ No newline at end of file From 9f8008b6d05e5b387795044a8d4cd06026b5b606 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 7 Mar 2022 16:57:03 +0530 Subject: [PATCH 022/101] feat: relocating burger menu button --- .../SidebarTrigger/SidebarTrigger.js | 63 +++++++++++++------ .../app/layout/components/DefaultSidebar.js | 1 + 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/admin-ui/app/components/SidebarTrigger/SidebarTrigger.js b/admin-ui/app/components/SidebarTrigger/SidebarTrigger.js index 652d95f70..4235a93d6 100755 --- a/admin-ui/app/components/SidebarTrigger/SidebarTrigger.js +++ b/admin-ui/app/components/SidebarTrigger/SidebarTrigger.js @@ -1,32 +1,57 @@ -import React from 'react'; -import { NavLink } from 'reactstrap'; -import PropTypes from 'prop-types'; -import { withPageConfig } from './../Layout'; +import React, { useState, useEffect } from 'react' +import { NavLink } from 'reactstrap' +import PropTypes from 'prop-types' +import { withPageConfig } from './../Layout' const SidebarTrigger = (props) => { - const { tag: Tag, pageConfig, ...otherProps } = props; + const { tag: Tag, pageConfig, ...otherProps } = props + const [showCollapse, setShowCollapse] = useState( + window.matchMedia('(max-width: 992px)').matches, + ) + useEffect(() => { + window + .matchMedia('(max-width: 768px)') + .addEventListener('change', (e) => setShowCollapse(e.matches)) + }, []) return ( { props.pageConfig.toggleSidebar(); return false; } } - active={ Tag !== 'a' ? !pageConfig.sidebarCollapsed : undefined } - { ...otherProps } + onClick={() => { + props.pageConfig.toggleSidebar() + return false + }} + active={Tag !== 'a' ? !pageConfig.sidebarCollapsed : undefined} + {...otherProps} > - {pageConfig.sidebarCollapsed && } - {!pageConfig.sidebarCollapsed && } + {pageConfig.sidebarCollapsed && ( + + )} + {!pageConfig.sidebarCollapsed && ( + + )} - ); -}; + ) +} SidebarTrigger.propTypes = { tag: PropTypes.any, children: PropTypes.node, - pageConfig: PropTypes.object -}; + pageConfig: PropTypes.object, +} SidebarTrigger.defaultProps = { tag: NavLink, -}; +} -const cfgSidebarTrigger = withPageConfig(SidebarTrigger); +const cfgSidebarTrigger = withPageConfig(SidebarTrigger) -export { - cfgSidebarTrigger as SidebarTrigger -}; +export { cfgSidebarTrigger as SidebarTrigger } diff --git a/admin-ui/app/layout/components/DefaultSidebar.js b/admin-ui/app/layout/components/DefaultSidebar.js index 09a5de432..46fc066d8 100755 --- a/admin-ui/app/layout/components/DefaultSidebar.js +++ b/admin-ui/app/layout/components/DefaultSidebar.js @@ -16,6 +16,7 @@ export const DefaultSidebar = () => ( {/* START SIDEBAR: Only for Desktop */} + From 40d02769f673cb658c87b365e900011027e475f1 Mon Sep 17 00:00:00 2001 From: Jose Date: Mon, 7 Mar 2022 07:33:59 -0500 Subject: [PATCH 023/101] chore: minor refactoring --- .../java/org/gluu/casa/core/pojo/FidoDevice.java | 3 +-- .../java/org/gluu/casa/core/pojo/SecurityKey.java | 8 -------- .../authnmethod/service/U2fClientCodes.java | 14 -------------- 3 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/service/U2fClientCodes.java diff --git a/casa/app/src/main/java/org/gluu/casa/core/pojo/FidoDevice.java b/casa/app/src/main/java/org/gluu/casa/core/pojo/FidoDevice.java index b5b6e9ce1..c283133f4 100644 --- a/casa/app/src/main/java/org/gluu/casa/core/pojo/FidoDevice.java +++ b/casa/app/src/main/java/org/gluu/casa/core/pojo/FidoDevice.java @@ -3,8 +3,7 @@ import java.util.Date; /** - * Represents a registered credential of u2f type. - * @author jgomer + * Represents a fido registered credential */ public class FidoDevice extends RegisteredCredential implements Comparable { diff --git a/casa/app/src/main/java/org/gluu/casa/core/pojo/SecurityKey.java b/casa/app/src/main/java/org/gluu/casa/core/pojo/SecurityKey.java index cd812214e..783affcbb 100644 --- a/casa/app/src/main/java/org/gluu/casa/core/pojo/SecurityKey.java +++ b/casa/app/src/main/java/org/gluu/casa/core/pojo/SecurityKey.java @@ -1,12 +1,4 @@ package org.gluu.casa.core.pojo; - -/** - * Created by jgomer on 2017-07-25. - * Represents a registered credential corresponding to a fido u2f security key - */ public class SecurityKey extends FidoDevice { - - public SecurityKey() { } - } diff --git a/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/service/U2fClientCodes.java b/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/service/U2fClientCodes.java deleted file mode 100644 index ee4b29204..000000000 --- a/casa/app/src/main/java/org/gluu/casa/plugins/authnmethod/service/U2fClientCodes.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.gluu.casa.plugins.authnmethod.service; - -/** - * Created by jgomer on 2017-07-24. - * An enumeration that holds the response codes that a u2f device registration request may output - */ -public enum U2fClientCodes { - OTHER_ERROR, BAD_REQUEST, CONFIGURATION_UNSUPPORTED, DEVICE_INELIGIBLE, TIMEOUT; - - public static U2fClientCodes get(int ord) { - return U2fClientCodes.values()[ord-1]; - } - -} From 2bad15129bc0443a0731c206f3c69a9d57c064ea Mon Sep 17 00:00:00 2001 From: Mohammad Abudayyeh <47318409+moabu@users.noreply.github.com> Date: Mon, 7 Mar 2022 12:38:30 +0000 Subject: [PATCH 024/101] chore: update admin ui version --- docker-admin-ui/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-admin-ui/Dockerfile b/docker-admin-ui/Dockerfile index 9684dfe4d..584f80f8a 100644 --- a/docker-admin-ui/Dockerfile +++ b/docker-admin-ui/Dockerfile @@ -28,7 +28,7 @@ RUN echo "daemon off;" >> /etc/nginx/nginx.conf # TODO: # - use NODE_ENV=production # - download build package (not git clone) -ENV ADMIN_UI_VERSION=51fe9e12269a9882a63f1a4d4dc1a3f537b204b6 +ENV ADMIN_UI_VERSION=11927ed4f036d7199d5340cf230100234c6aaaab # note that as we're pulling from a monorepo (with multiple project in it) # we are using partial-clone and sparse-checkout to get the admin-ui code From f780e0425ca36060f9dfbee4189a2b851e76b2b7 Mon Sep 17 00:00:00 2001 From: iromli Date: Tue, 8 Mar 2022 12:40:21 +0700 Subject: [PATCH 025/101] chore: update admin-ui source --- docker-admin-ui/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-admin-ui/Dockerfile b/docker-admin-ui/Dockerfile index 584f80f8a..fe5dc690a 100644 --- a/docker-admin-ui/Dockerfile +++ b/docker-admin-ui/Dockerfile @@ -28,7 +28,7 @@ RUN echo "daemon off;" >> /etc/nginx/nginx.conf # TODO: # - use NODE_ENV=production # - download build package (not git clone) -ENV ADMIN_UI_VERSION=11927ed4f036d7199d5340cf230100234c6aaaab +ENV ADMIN_UI_VERSION=303e707061773ba33bec74ecb3a78f5de0539e24 # note that as we're pulling from a monorepo (with multiple project in it) # we are using partial-clone and sparse-checkout to get the admin-ui code From 5f7a3934a57f08f363075b5c5c8cb9d98f4e372d Mon Sep 17 00:00:00 2001 From: iromli Date: Tue, 8 Mar 2022 12:40:34 +0700 Subject: [PATCH 026/101] chore: update dependencies - base image pinned to liberica-openjre-alpine:11.0.13-8 - updated casa source --- docker-casa/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-casa/Dockerfile b/docker-casa/Dockerfile index c96fb8abe..6a6efd22a 100644 --- a/docker-casa/Dockerfile +++ b/docker-casa/Dockerfile @@ -1,4 +1,4 @@ -FROM bellsoft/liberica-openjre-alpine:11 +FROM bellsoft/liberica-openjre-alpine:11.0.13-8 # =============== # Alpine packages @@ -35,7 +35,7 @@ EXPOSE 8080 # ==== ENV GLUU_VERSION=5.0.0-SNAPSHOT -ENV GLUU_BUILD_DATE='2022-02-28 21:45' +ENV GLUU_BUILD_DATE='2022-03-02 11:52' ENV GLUU_SOURCE_URL=https://jenkins.gluu.org/maven/org/gluu/casa/${GLUU_VERSION}/casa-${GLUU_VERSION}.war # Install Casa From 62ab2fa0db473bad52888bb7a062f1aba801ca50 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 8 Mar 2022 12:12:11 +0530 Subject: [PATCH 027/101] feat: change collapse option location --- .../SidebarTrigger/SidebarTrigger.js | 13 +++---------- .../app/layout/components/DefaultSidebar.js | 7 ++++++- admin-ui/app/routes/Apps/Gluu/GluuNavBar.js | 18 ++++++++++++++---- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/admin-ui/app/components/SidebarTrigger/SidebarTrigger.js b/admin-ui/app/components/SidebarTrigger/SidebarTrigger.js index 4235a93d6..7c7df3692 100755 --- a/admin-ui/app/components/SidebarTrigger/SidebarTrigger.js +++ b/admin-ui/app/components/SidebarTrigger/SidebarTrigger.js @@ -1,18 +1,11 @@ -import React, { useState, useEffect } from 'react' +import React from 'react' import { NavLink } from 'reactstrap' import PropTypes from 'prop-types' import { withPageConfig } from './../Layout' const SidebarTrigger = (props) => { const { tag: Tag, pageConfig, ...otherProps } = props - const [showCollapse, setShowCollapse] = useState( - window.matchMedia('(max-width: 992px)').matches, - ) - useEffect(() => { - window - .matchMedia('(max-width: 768px)') - .addEventListener('change', (e) => setShowCollapse(e.matches)) - }, []) + return ( { @@ -31,7 +24,7 @@ const SidebarTrigger = (props) => { }} >
)} - {!pageConfig.sidebarCollapsed && ( + {!pageConfig.sidebarCollapsed && !props.showCollapseonly && ( ( {/* START SIDEBAR: Only for Desktop */} - + @@ -31,6 +31,11 @@ export const DefaultSidebar = () => ( {/* */} + {/* SIDEBAR: Menu */} diff --git a/admin-ui/app/routes/Apps/Gluu/GluuNavBar.js b/admin-ui/app/routes/Apps/Gluu/GluuNavBar.js index fb4b3ed92..609d2c4a3 100755 --- a/admin-ui/app/routes/Apps/Gluu/GluuNavBar.js +++ b/admin-ui/app/routes/Apps/Gluu/GluuNavBar.js @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useState, useEffect } from 'react' import PropTypes from 'prop-types' import { Avatar, @@ -20,6 +20,14 @@ import GluuErrorFallBack from './GluuErrorFallBack' function GluuNavBar({ themeColor, themeStyle, userinfo }) { const userInfo = userinfo ? userinfo : {} + const [showCollapse, setShowCollapse] = useState( + window.matchMedia('(max-width: 992px)').matches, + ) + useEffect(() => { + window + .matchMedia('(max-width: 992px)') + .addEventListener('change', (e) => setShowCollapse(e.matches)) + }, []) return (