diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..f4961dc --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,61 @@ +pipeline { + agent any + options { + checkoutToSubdirectory('argo-probe-eudat-b2access') + } + environment { + PROJECT_DIR="argo-probe-eudat-b2access" + GIT_COMMIT=sh(script: "cd ${WORKSPACE}/$PROJECT_DIR && git log -1 --format=\"%H\"",returnStdout: true).trim() + GIT_COMMIT_HASH=sh(script: "cd ${WORKSPACE}/$PROJECT_DIR && git log -1 --format=\"%H\" | cut -c1-9",returnStdout: true).trim() + GIT_COMMIT_DATE=sh(script: "date -d \"\$(cd ${WORKSPACE}/$PROJECT_DIR && git show -s --format=%ci ${GIT_COMMIT_HASH})\" \"+%Y%m%d%H%M%S\"",returnStdout: true).trim() + + } + stages { + stage ('Build'){ + parallel { + + stage ('Build Rocky 9') { + agent { + docker { + image 'argo.registry:5000/epel-9-ams' + args '-u jenkins:jenkins' + } + } + steps { + echo 'Building Rocky 9 RPM...' + withCredentials(bindings: [sshUserPrivateKey(credentialsId: 'jenkins-rpm-repo', usernameVariable: 'REPOUSER', \ + keyFileVariable: 'REPOKEY')]) { + sh "/home/jenkins/build-rpm.sh -w ${WORKSPACE} -b ${BRANCH_NAME} -d rocky9 -p ${PROJECT_DIR} -s ${REPOKEY}" + + } + archiveArtifacts artifacts: '**/*.rpm', fingerprint: true + } + post { + always { + cleanWs() + } + } + } + } + } + } + post { + always { + cleanWs() + } + success { + script{ + if ( env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'devel' ) { + slackSend( message: ":rocket: New version for <$BUILD_URL|$PROJECT_DIR>:$BRANCH_NAME Job: $JOB_NAME !") + } + } + } + failure { + script{ + if ( env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'devel' ) { + slackSend( message: ":rain_cloud: Build Failed for <$BUILD_URL|$PROJECT_DIR>:$BRANCH_NAME Job: $JOB_NAME") + } + } + } + } +} diff --git a/Makefile b/Makefile index 1f6b265..6bbcc9e 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,9 @@ -PKGNAME=nagios-plugins-eudat-b2access +PKGNAME=argo-probe-eudat-b2access SPECFILE=${PKGNAME}.spec -FILES=check_b2access.py ${SPECFILE} +FILES=check_b2access.py check_b2access_simple.py ${SPECFILE} PKGVERSION=$(shell grep -s '^Version:' $(SPECFILE) | sed -e 's/Version:\s*//') -srpm: dist - rpmbuild -ts --define='dist .el6' ${PKGNAME}-${PKGVERSION}.tar.gz - rpm: dist rpmbuild -ta ${PKGNAME}-${PKGVERSION}.tar.gz @@ -22,4 +19,4 @@ sources: dist clean: rm -rf ${PKGNAME}-${PKGVERSION}.tar.gz - rm -rf dist \ No newline at end of file + rm -rf dist diff --git a/nagios-plugins-eudat-b2access.spec b/argo-probe-eudat-b2access.spec similarity index 59% rename from nagios-plugins-eudat-b2access.spec rename to argo-probe-eudat-b2access.spec index 99f2678..28ca47e 100644 --- a/nagios-plugins-eudat-b2access.spec +++ b/argo-probe-eudat-b2access.spec @@ -13,10 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -Name: nagios-plugins-eudat-b2access -Version: 0.3 +Name: argo-probe-eudat-b2access +Version: 0.5 Release: 1%{?dist} -Summary: Nagios B2ACCESS probes +Summary: Monitoring metrics for B2ACCESS License: Apache License, Version 2.0 Packager: Shiraz Memon @@ -24,17 +24,14 @@ Source: %{name}-%{version}.tar.gz BuildArch: noarch BuildRoot: %{_tmppath}/%{name}-%{version} -Requires: python -Requires: python-argparse -Requires: python-lxml -Requires: python-simplejson -Requires: python-defusedxml -Requires: python-httplib2 -Requires: python-requests - +Requires: python3 +Requires: python3-requests +Requires: python3-oauthlib +Requires: python3-requests-oauthlib +Requires: python3-urllib3 %description -Nagios probes to check functionality of B2ACCESS Service +Monitoring metrics to check functionality of B2ACCESS Service %prep %setup -q @@ -44,17 +41,25 @@ Nagios probes to check functionality of B2ACCESS Service %install -install -d %{buildroot}/%{_libexecdir}/argo-monitoring/probes/%{probe_namespace} -install -m 755 check_b2access.py %{buildroot}/%{_libexecdir}/argo-monitoring/probes/%{probe_namespace}/check_b2access.py +install -d %{buildroot}/%{_libexecdir}/argo/probes/%{probe_namespace} +install -m 755 check_b2access.py %{buildroot}/%{_libexecdir}/argo/probes/%{probe_namespace}/check_b2access.py +install -m 755 check_b2access_simple.py %{buildroot}/%{_libexecdir}/argo/probes/%{probe_namespace}/check_b2access_simple.py %files -%dir /%{_libexecdir}/argo-monitoring -%dir /%{_libexecdir}/argo-monitoring/probes/ -%dir /%{_libexecdir}/argo-monitoring/probes/%{probe_namespace} +%dir /%{_libexecdir}/argo +%dir /%{_libexecdir}/argo/probes/ +%dir /%{_libexecdir}/argo/probes/%{probe_namespace} -%attr(0755,root,root) /%{_libexecdir}/argo-monitoring/probes/%{probe_namespace}/check_b2access.py +%attr(0755,root,root) /%{_libexecdir}/argo/probes/%{probe_namespace}/check_b2access.py +%attr(0755,root,root) /%{_libexecdir}/argo/probes/%{probe_namespace}/check_b2access_simple.py %changelog +* Mon Jul 14 2024 Marvin Winkens - 1.0-1 +- Updated to python 3 +* Tue Mar 15 2022 Themis Zamani - 0.4-2 +- Update the spec file requirements + * Mon Jan 24 2022 Themis Zamani - 0.4-2 +- Update the spec file requirements * Tue Jun 05 2018 Shiraz Memon - 0.4-1 - Adapted to Unity v2.x.x REST API - More details in verbose mode @@ -64,4 +69,4 @@ install -m 755 check_b2access.py %{buildroot}/%{_libexecdir}/argo-monitoring/pro * Thu Sep 15 2016 Shiraz Memon - 0.2-1 - Updated namespace and license information * Thu Jul 28 2016 Shiraz Memon - 0.1-1 -- Initial version of the package \ No newline at end of file +- Initial version of the package diff --git a/check_b2access.py b/check_b2access.py index f418c91..09e6683 100755 --- a/check_b2access.py +++ b/check_b2access.py @@ -1,307 +1,318 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import argparse import sys import signal import json -from time import strftime,gmtime +from functools import wraps +from time import strftime, gmtime + +import urllib3 from oauthlib.oauth2 import BackendApplicationClient from requests_oauthlib import OAuth2Session -import requests.packages.urllib3 +import requests import subprocess import datetime -from oauthlib.oauth2.rfc6749.errors import OAuth2Error, MissingTokenError +from oauthlib.oauth2.rfc6749.errors import MissingTokenError from requests.exceptions import ConnectionError, HTTPError import os.path -import validators -from validators.utils import ValidationFailure -TEST_SUFFIX='NAGIOS-' + strftime("%Y%m%d-%H%M%S",gmtime()) -VALUE_ORIG='http://www.' + TEST_SUFFIX + '.com/1' -VALUE_AFTER='http://www.' + TEST_SUFFIX + '.com/2' -TOKEN_URI='/oauth2/token' +TOKEN_URI = '/oauth2/token' + -def handler(signum, stack): - print "UNKNOWN: Timeout reached, exiting." +def handler(*args): + print("UNKNOWN: Timeout reached, exiting.") sys.exit(3) + +def exceptionHandler(message: str): + def handleExceptions(func): + @wraps(func) + def wrapper(*args, **kwargs): + try: + ret = func(*args, **kwargs) + except BaseException: + print(message, sys.exc_info()[0]) + sys.exit(2) + return ret + + return wrapper + + return handleExceptions + + def getAccessToken(param): """Fetch access token from B2ACCESS""" - if param.verbose == True: - print "\nFetching access token from B2ACCESS" - """ Pre-req: Create a user 'argo' with password 'test' in group 'oauth-clients' and 'eudat:b2share' or any other """ - + if param.verbose: + print("\nFetching access token from B2ACCESS") + """ Pre-req: Create a user 'argo' with password 'test' in group 'oauth-clients' and 'eudat:b2share' or any other """ + try: client = BackendApplicationClient(client_id=username) - client.prepare_request_body(scope=['profile','email','GENERATE_USER_CERTIFICATE']) + client.prepare_request_body(scope=['profile', 'email', 'GENERATE_USER_CERTIFICATE']) oauth = OAuth2Session(client=client) - token = oauth.fetch_token(token_url=str(param.url)+TOKEN_URI, verify=False,client_id=str(param.username),client_secret=str(param.password),scope=['USER_PROFILE','GENERATE_USER_CERTIFICATE']) + token = oauth.fetch_token(token_url=str(param.url) + TOKEN_URI, verify=False, client_id=str(param.username), + client_secret=str(param.password), + scope=['USER_PROFILE', 'GENERATE_USER_CERTIFICATE']) j = json.dumps(token, indent=4) k = json.loads(j) if param.verbose: - print "Access token: "+k['access_token'] - - getTokenInfo(str(param.url)+'/oauth2/tokeninfo', str(k['access_token']), param.verbose) - getUserInfo(str(param.url)+'/oauth2/userinfo', str(k['access_token']), param.verbose) + print("Access token: " + k['access_token']) + + getTokenInfo(str(param.url)+'/oauth2/tokeninfo',str(k['access_token']), param.verbose) + getUserInfo(str(param.url)+'/oauth2/userinfo',str(k['access_token']), param.verbose) except ConnectionError as e: - print "CRITICAL: Invalid Unity URL: {0}".format(e) + print("CRITICAL: Invalid Unity URL: {0}".format(e)) sys.exit(2) except MissingTokenError as e: - print "CRITICAL: Invalid client Id and/or secret: {0}".format(e.description) + print("CRITICAL: Invalid client Id and/or secret: {0}".format(e.description)) sys.exit(2) except TypeError as e: - print e + print(e) sys.exit(2) except: print("CRITICAL: Error fetching OAuth 2.0 access token:", sys.exc_info()[0]) sys.exit(2) raise - - + + def getTokenInfo(url, token, verbose): """ Fetch access token details """ try: if verbose: - print "Fetching access token information from URL: "+url - - entity = requests.get(url,verify=False, headers = {'Authorization': 'Bearer '+token}) + print(f"\nFetching access token information from URL: {url}") + + entity = requests.get(url, verify=False, headers={'Authorization': 'Bearer ' + token}) + if entity.status_code != 200: + raise ConnectionError(f"Fetching access token from {url} returned {entity.status_code}") j = entity.json() expire = datetime.datetime.fromtimestamp(int(j['exp'])).strftime('%Y-%m-%d %H:%M:%S') if verbose: - print "Expires on: "+expire - print 'Detailed token info: '+entity.text + print(f"Expires on: {expire}\nDetailed token info: {entity.text}") except KeyError as e: - print "WARNING: Invalid key(s): {0}".format(e) + print("WARNING: Invalid key(s): {0}".format(e)) sys.exit(1) except ValueError as e: - print "CRITICAL: Invalid access token: {0}".format(e) + print("CRITICAL: Invalid access token: {0}".format(e)) sys.exit(2) except ConnectionError as e: - print "CRITICAL: Invalid token endpoint URL: {0}".format(e) + print("CRITICAL: Invalid token endpoint URL: {0}".format(e)) sys.exit(2) except: - print("CRITICAL: Error retrieving access token information:", sys.exc_info()[0]) + print("CRITICAL: Error retrieving user information:", sys.exc_info()[0]) sys.exit(2) raise - def getUserInfo(url, token, verbose): """ Fetch user information using access token """ try: - if param.verbose: - print "\n" - print "Fetching user information based on access token, endpoint URL: "+url - entity = requests.get(url,verify=False, headers = {'Authorization': 'Bearer '+token}) + if verbose: + print(f"\nFetching user information based on access token, endpoint URL: {url}") + entity = requests.get(url, verify=False, headers={'Authorization': 'Bearer ' + token}) + if entity.status_code != 200: + raise ConnectionError(f"Fetching access token from {url} returned {entity.status_code}") j = entity.json() - if param.verbose: - print "Subject: "+j['sub'] - print "Persistent Id: "+j['unity:persistent'] - print 'Detailed user information: '+entity.text + if verbose: + print( + f"Subject: {j['sub']}\nPersistent Id: {j['unity:persistent']}\n\ + Detailed user information: {entity.text}") except KeyError as e: - print "WARNING: Invalid key(s): {0}".format(e) + print("WARNING: Invalid key(s): {0}".format(e)) sys.exit(1) except ValueError as e: - print "CRITICAL: Invalid access token: {0}".format(e) + print("CRITICAL: Invalid access token: {0}".format(e)) sys.exit(2) except ConnectionError as e: - print "CRITICAL: Invalid UserInfo endpoint URL: {0}".format(e) - sys.exit(2) + print("CRITICAL: Invalid UserInfo endpoint URL: {0}".format(e)) + sys.exit(2) except: - print("CRITICAL: Error retrieving user information:", sys.exc_info()[0]) + print("CRITICAL: Error retrieving user information with the username/password:", sys.exc_info()[0]) sys.exit(2) raise - + def getInfoUsernamePassword(param): """ Query user information with username and password """ - - url = param.url+"/rest-admin/v1/resolve/userName/"+str(param.username) - + + url = param.url + "/rest-admin/v1/resolve/userName/" + str(param.username) + if param.verbose: - print "\nQuery with username and password, endpoint URL: "+url - - try: + print(f"\nQuery with username and password, endpoint URL: {url}") + try: uname = param.username pwd = param.password - entity = requests.get(str(url),verify=False,auth=(uname, pwd)) - if entity.status_code == 403: - raise HTTPError("CRITICAL: Error retrieving the user information with username {0}: invalid username/password".format(uname)) - sys.exit(2) + entity = requests.get(str(url), verify=False, auth=(uname, pwd)) + j = entity.json() + json_formatted_str = json.dumps(j, indent=2) + if entity.status_code == 403: + print("CRITICAL: Error retrieving the user information with username {0}: invalid username/password".format( + uname)) + sys.exit(2) + if param.verbose: - print "Credential requirement: "+j['credentialInfo']['credentialRequirementId'] - print "Entity Id: "+str(j['id']) - print "Username: "+j['identities'][0]['value'] - print "Detailed user information: "+entity.text + print(f"\nCredential requirement: {j['credentialInfo']['credentialRequirementId']}\n\ + Entity Id: {str(j['entityInformation']['entityId'])}\n\ + Username: {j['identities'][0]['value']}\n\ + Detailed user information: {entity.text}") + except ConnectionError as e: - print "CRITICAL: Invalid Unity endpoint URL: {0}".format(e) + print("CRITICAL: Invalid Unity endpoint URL: {0}".format(e)) sys.exit(2) except HTTPError as e: - print e + print(e) sys.exit(2) except KeyError as e: - print "CRITICAL: Invalid key(s): {0}".format(e) - sys.exit(2) + print("CRITICAL: Invalid key(s): {0}".format(e)) + sys.exit(2) except: print("CRITICAL: Error retrieving user information with the username/password:", sys.exc_info()[0]) sys.exit(2) raise - + def getInfoCert(param): """ Query user information with X509 Certificate Authentication """ try: - cert_txt = subprocess.check_output(["openssl", "x509", "-subject", "-noout","-in", param.certificate]) - + cert_txt = subprocess.check_output(["openssl", "x509", "-subject", "-noout", "-in", param.certificate]) sub = str(cert_txt).replace("subject= ", "") - dn = getLdapName(sub) - - """ url = param.url+"/rest-admin/v1/resolve/x500Name/CN=Ahmed Shiraz Memon,OU=IAS-JSC,OU=Forschungszentrum Juelich GmbH,O=GridGermany,C=DE" """ - - url = param.url+"/rest-admin/v1/resolve/x500Name/"+dn - - print "url: "+url - + """ + url = param.url+"/rest-admin/v1/resolve/x500Name/CN=Ahmed Shiraz Memon,OU=IAS-JSC,\ + OU=Forschungszentrum Juelich GmbH,O=GridGermany,C=DE" + """ + url = f"{param.url}/rest-admin/v1/resolve/x500Name/{dn}" + + print(f"url: {url}") + if param.verbose: - print "\nQuery user information with X509 Certificate Authentication, endpoint URL:" + url - - entity = requests.get(str(url),verify=False,cert=(str(param.certificate), str(param.key))) - + print(f"\nQuery user information with X509 Certificate Authentication, endpoint URL: {url}") + + entity = requests.get(str(url), verify=False, cert=(str(param.certificate), str(param.key))) + if (entity.status_code == 400) or (entity.status_code == 403): - raise HTTPError("CRITICAL: Error retrieving the user information with X500Name {0}: invalid certificate".format(dn)) + print("CRITICAL: Error retrieving the user information with X500Name {0}: invalid certificate".format(dn)) sys.exit(2) - + j = entity.json() - + if param.verbose: - print "Credential requirement: "+j['credentialInfo']['credentialRequirementId'] + print(f"Credential requirement: {j['credentialInfo']['credentialRequirementId']}") """print "Entity Id: "+str(j['entityId'])""" - print "Entity Id: "+str(j['entityInformation']['entityId']) - print "X500Name: "+j['identities'][0]['value'] - - if param.verbose: - print "Detailed user information: \n"+json.dumps(j, indent=4) + print(f"Entity Id: {str(j['entityInformation']['entityId'])}") + print(f"X500Name: {j['identities'][0]['value']}") + print(f"Detailed user information: \n{json.dumps(j, indent=4)}") except HTTPError as e: - print e + print(e) sys.exit(2) except KeyError as e: - print "CRITICAL: Invalid key(s): {0}".format(e) + print("CRITICAL: Invalid key(s): {0}".format(e)) sys.exit(2) except: print("CRITICAL: Error retrieving user information by X509 certificate:", sys.exc_info()) sys.exit(2) raise + def getLdapName(openssl_name): name = str(openssl_name) strs = name.split("/") - strs.reverse() - + strs[0] = str(strs[0]).rstrip() - strs.pop() - - print strs - + + # print(strs) why? str1 = ','.join(strs) - return str1 - + if __name__ == '__main__': - #disable ssl warnings and trust the unity server - requests.packages.urllib3.disable_warnings() + # disable ssl warnings and trust the unity server + urllib3.disable_warnings(category=urllib3.exceptions.InsecureRequestWarning) parser = argparse.ArgumentParser(description='B2ACCESS login, query probe') - + # req = parser.add_argument_group('required arguments') - + subParsers = parser.add_subparsers() - + parser.add_argument('-u', '--url', action='store', dest='url', required=True, - help='baseuri of B2ACCESS-UNITY to test') + help='baseuri of B2ACCESS-UNITY to test') parser.add_argument('-t', '--timeout', action='store', dest='timeout', - help='timeout') + help='timeout') parser.add_argument('-v', '--version', action='store', dest='version', - help='version') + help='version') parser.add_argument('-V', '--verbose', action='store_true', dest='verbose', - help='increase output verbosity', default=False) + help='increase output verbosity', default=False) parser.add_argument('-d', '--debug', action='store_true', dest='debug', - help='debug mode') - - u_parser = subParsers.add_parser('1',help='Username/Password based authentication') + help='debug mode') + u_parser = subParsers.add_parser('1', help='Username/Password based authentication') u_parser.add_argument('-U', '--username', action='store', dest='username', required=True, - help='B2ACCESS user') + help='B2ACCESS user') u_parser.add_argument('-P', '--password', action='store', dest='password', required=True, - help='B2ACCESS password') + help='B2ACCESS password') u_parser.set_defaults(action='1') - - c_parser = subParsers.add_parser('2',help='X.509 Certificate based authentication') + + c_parser = subParsers.add_parser('2', help='X.509 Certificate based authentication') c_parser.add_argument('-C', '--cert', action='store', dest='certificate', - help='Path to public key certificate', required=True) + help='Path to public key certificate', required=True) c_parser.add_argument('-K', '--key', action='store', dest='key', - help='Path to private key', required=True) + help='Path to private key', required=True) c_parser.set_defaults(action='2') - - param = parser.parse_args() - base_url = param.url - timeout = param.timeout - - if param.action == "1": - username = param.username - password = param.password - - - if param.verbose == True: - print "verbosity is turned ON" - - if param.timeout and int(param.timeout) > 0 : - print "Timeout: "+timeout + + parser_args = parser.parse_args() + base_url = parser_args.url + timeout = parser_args.timeout + username = "" + + if parser_args.action == "1": + username = parser_args.username + password = parser_args.password + + if parser_args.verbose: + print("verbosity is turned ON") + + if parser_args.timeout and int(parser_args.timeout) > 0: signal.signal(signal.SIGALRM, handler) - signal.alarm(int(param.timeout)) - - - - if param.verbose: - print "Starting B2ACCESS Probe...\n---------------------------\n" - print "B2ACCESS url: "+str(base_url) - if param.action == "1": - print "B2ACCESS username: "+username - if param.action == "2": - print "Public key: "+param.certificate - - try: - if param.action == "2": - if not os.path.exists(param.certificate): - raise IOError("CRITICAL: Public key certificate file does not exist: {0}".format(param.certificate)) - if not os.path.exists(param.key): - raise IOError("CRITICAL: Private key file does not exist: : {0}".format(param.key)) - if not validators.url(param.url): - raise SyntaxError("CRITICAL: Invalid URL syntax {0}".format(param.url)) + signal.alarm(int(parser_args.timeout)) + + if parser_args.verbose: + print(f"Starting B2ACCESS Probe...\n---------------------------\n\ + B2ACCESS url: {str(base_url)}") + if parser_args.action == "1": + print(f"B2ACCESS username: {username}") + elif parser_args.action == "2": + print(f"Public key: {parser_args.certificate}") + try: + if parser_args.action == "2": + if not os.path.exists(parser_args.certificate): + raise IOError( + "CRITICAL: Public key certificate file does not exist: {0}".format(parser_args.certificate)) + if not os.path.exists(parser_args.key): + raise IOError("CRITICAL: Private key file does not exist: : {0}".format(parser_args.key)) + except IOError as e: - print e + print(e) sys.exit(2) except SyntaxError as e: - print e + print(e) sys.exit(2) - except: + except BaseException: print(sys.exc_info()[0]) sys.exit(2) - raise - - if param.action == "1": - getAccessToken(param) - getInfoUsernamePassword(param) - - if param.action == "2": - getInfoCert(param) - - if param.verbose: - if param.action == "1": - print "\nOK, User access token retrieval and login with username/password was successful" - if param.action == "2": - print "\nOK, User login with X.509 Certificate was successful" + + if parser_args.action == "1": + getAccessToken(parser_args) + getInfoUsernamePassword(parser_args) + + if parser_args.action == "2": + getInfoCert(parser_args) + + if parser_args.verbose: + if parser_args.action == "1": + print("\nOK, User access token retrieval and login with username/password was successful") + elif parser_args.action == "2": + print("\nOK, User login with X.509 Certificate was successful") else: - print "OK" - sys.exit(0) \ No newline at end of file + print("OK") + sys.exit(0) diff --git a/check_b2access_simple.py b/check_b2access_simple.py new file mode 100755 index 0000000..43ad629 --- /dev/null +++ b/check_b2access_simple.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 + +import argparse +import sys + +import signal +import json +from time import strftime,gmtime +from oauthlib.oauth2 import BackendApplicationClient +from requests_oauthlib import OAuth2Session +import requests.packages.urllib3 +import subprocess +import datetime +from oauthlib.oauth2.rfc6749.errors import OAuth2Error, MissingTokenError +from requests.exceptions import ConnectionError, HTTPError +import os.path +import validators +from validators.utils import ValidationFailure + +TOKEN_URI='/oauth2/token' + +def handler(signum, stack): + print "UNKNOWN: Timeout reached, exiting." + sys.exit(3) + +def getAccessToken(param): + """Fetch access token from B2ACCESS""" + if param.verbose == True: + print "\nFetching access token from B2ACCESS" + """ Pre-req: Create a user 'argo' with password 'test' in group 'oauth-clients' and 'eudat:b2share' or any other """ + + try: + client = BackendApplicationClient(client_id=username) + client.prepare_request_body(scope=['USER_PROFILE','GENERATE_USER_CERTIFICATE']) + oauth = OAuth2Session(client=client) + token = oauth.fetch_token(token_url=str(param.url)+TOKEN_URI, verify=False,client_id=str(param.username),client_secret=str(param.password),scope=['USER_PROFILE','GENERATE_USER_CERTIFICATE']) + j = json.dumps(token, indent=4) + k = json.loads(j) + if param.verbose: + print "Access token: "+k['access_token'] + + getTokenInfo(str(param.url)+'/oauth2/tokeninfo', str(k['access_token']), param.verbose) + getUserInfo(str(param.url)+'/oauth2/userinfo', str(k['access_token']), param.verbose) + except ConnectionError as e: + print "CRITICAL: Invalid Unity URL: {0}".format(e) + sys.exit(2) + except MissingTokenError as e: + print "CRITICAL: Invalid client Id and/or secret: {0}".format(e.description) + sys.exit(2) + except TypeError as e: + print e + sys.exit(2) + except: + print("CRITICAL: Error fetching OAuth 2.0 access token:", sys.exc_info()[0]) + sys.exit(2) + raise + + +def getTokenInfo(url, token, verbose): + """ Fetch access token details """ + try: + if verbose: + print "Fetching access token information from URL: "+url + + entity = requests.get(url,verify=False, headers = {'Authorization': 'Bearer '+token}) + j = entity.json() + expire = datetime.datetime.fromtimestamp(int(j['exp'])).strftime('%Y-%m-%d %H:%M:%S') + if verbose: + print "Expires on: "+expire + print 'Detailed token info: '+entity.text + except KeyError as e: + print "WARNING: Invalid key(s): {0}".format(e) + sys.exit(1) + except ValueError as e: + print "CRITICAL: Invalid access token: {0}".format(e) + sys.exit(2) + except ConnectionError as e: + print "CRITICAL: Invalid token endpoint URL: {0}".format(e) + sys.exit(2) + except: + print("CRITICAL: Error retrieving access token information:", sys.exc_info()[0]) + sys.exit(2) + raise + + +def getUserInfo(url, token, verbose): + """ Fetch user information using access token """ + try: + if param.verbose: + print "\n" + print "Fetching user information based on access token, endpoint URL: "+url + entity = requests.get(url,verify=False, headers = {'Authorization': 'Bearer '+token}) + j = entity.json() + if param.verbose: + print "Subject: "+j['sub'] + print "Persistent Id: "+j['unity:persistent'] + print 'Detailed user information: '+entity.text + except KeyError as e: + print "WARNING: Invalid key(s): {0}".format(e) + sys.exit(1) + except ValueError as e: + print "CRITICAL: Invalid access token: {0}".format(e) + sys.exit(2) + except ConnectionError as e: + print "CRITICAL: Invalid UserInfo endpoint URL: {0}".format(e) + sys.exit(2) + except: + print("CRITICAL: Error retrieving user information:", sys.exc_info()[0]) + sys.exit(2) + raise + + +def getInfoUsernamePassword(param): + """ Query user information with username and password """ + + url = param.url+"/rest-admin/v1/resolve/userName/"+str(param.username) + + if param.verbose: + print "\nQuery with username and password, endpoint URL: "+url + + try: + uname = param.username + pwd = param.password + entity = requests.get(str(url),verify=False,auth=(uname, pwd)) + if entity.status_code == 403: + raise HTTPError("CRITICAL: Error retrieving the user information with username {0}: invalid username/password".format(uname)) + sys.exit(2) + j = entity.json() + if param.verbose: + print "Credential requirement: "+j['credentialInfo']['credentialRequirementId'] + print "Entity Id: "+str(j['id']) + print "Username: "+j['identities'][0]['value'] + print "Detailed user information: "+entity.text + except ConnectionError as e: + print "CRITICAL: Invalid Unity endpoint URL: {0}".format(e) + sys.exit(2) + except HTTPError as e: + print e + sys.exit(2) + except KeyError as e: + print "CRITICAL: Invalid key(s): {0}".format(e) + sys.exit(2) + except: + print("CRITICAL: Error retrieving user information with the username/password:", sys.exc_info()[0]) + sys.exit(2) + raise + +def getInfoCert(param): + """ Query user information with X509 Certificate Authentication """ + try: + cert_txt = subprocess.check_output(["openssl", "x509", "-subject", "-noout","-in", param.certificate]) + + sub = str(cert_txt).replace("subject= ", "") + + dn = getLdapName(sub) + + """ url = param.url+"/rest-admin/v1/resolve/x500Name/CN=Ahmed Shiraz Memon,OU=IAS-JSC,OU=Forschungszentrum Juelich GmbH,O=GridGermany,C=DE" """ + + url = param.url+"/rest-admin/v1/resolve/x500Name/"+dn + + if param.verbose: + print "\nQuery user information with X509 Certificate Authentication, endpoint URL:" + url + + entity = requests.get(str(url),verify=False,cert=(str(param.certificate), str(param.key))) + + if (entity.status_code == 400) or (entity.status_code == 403): + raise HTTPError("CRITICAL: Error retrieving the user information with X500Name {0}: invalid certificate".format(dn)) + sys.exit(2) + + j = entity.json() + if param.verbose: + print "Credential requirement: "+j['credentialInfo']['credentialRequirementId'] + print "Entity Id: "+str(j['id']) + print "X500Name: "+j['identities'][0]['value'] + + if param.verbose: + print "Detailed user information: "+entity.text + except HTTPError as e: + print e + sys.exit(2) + except KeyError as e: + print "CRITICAL: Invalid key(s): {0}".format(e) + sys.exit(2) + except: + print("CRITICAL: Error retrieving user information by X509 certificate:", sys.exc_info()[0]) + sys.exit(2) + raise + +def getLdapName(openssl_name): + name = str(openssl_name) + strs = name.split("/") + + strs.reverse() + + strs[0] = str(strs[0]).rstrip() + + strs.pop() + + print strs + + str1 = ','.join(strs) + + return str1 + + +if __name__ == '__main__': + #disable ssl warnings and trust the unity server + requests.packages.urllib3.disable_warnings() + parser = argparse.ArgumentParser(description='B2ACCESS login, query probe') + + # req = parser.add_argument_group('required arguments') + + parser.add_argument('-u', '--url', action='store', dest='url', required=True, + help='baseuri of B2ACCESS-UNITY to test') + parser.add_argument('-t', '--timeout', action='store', dest='timeout', + help='timeout') + parser.add_argument('-v', '--version', action='store', dest='version', + help='version') + parser.add_argument('-V', '--verbose', action='store_true', dest='verbose', + help='increase output verbosity', default=False) + parser.add_argument('-d', '--debug', action='store_true', dest='debug', + help='debug mode') + + parser.add_argument('-U', '--username', action='store', dest='username', + help='B2ACCESS user') + parser.add_argument('-P', '--password', action='store', dest='password', + help='B2ACCESS password') + + parser.add_argument('-C', '--cert', action='store', dest='certificate', + help='Path to public key certificate') + parser.add_argument('-K', '--key', action='store', dest='key', + help='Path to private key') + + param = parser.parse_args() + base_url = param.url + timeout = param.timeout + + + if param.username is not None and param.password is not None and param.certificate is None and param.key is None: + action = "1" + elif param.username is None and param.password is None and param.certificate is not None and param.key is not None: + action = "2" + else: + print "CRITICAL: Either username/password or certificate/key must be provided" + sys.exit(2) + + + if action == "1": + username = param.username + password = param.password + + + if param.verbose == True: + print "verbosity is turned ON" + + if param.timeout and int(param.timeout) > 0 : + print "Timeout: "+timeout + signal.signal(signal.SIGALRM, handler) + signal.alarm(int(param.timeout)) + + + + if param.verbose: + print "Starting B2ACCESS Probe...\n---------------------------\n" + print "B2ACCESS url: "+str(base_url) + if action == "1": + print "B2ACCESS username: "+username + if action == "2": + print "Public key: "+param.certificate + + try: + if action == "2": + if not os.path.exists(param.certificate): + raise IOError("CRITICAL: Public key certificate file does not exist: {0}".format(param.certificate)) + if not os.path.exists(param.key): + raise IOError("CRITICAL: Private key file does not exist: : {0}".format(param.key)) + if not validators.url(param.url): + raise SyntaxError("CRITICAL: Invalid URL syntax {0}".format(param.url)) + except IOError as e: + print e + sys.exit(2) + except SyntaxError as e: + print e + sys.exit(2) + except: + print(sys.exc_info()[0]) + sys.exit(2) + raise + + if action == "1": + getAccessToken(param) + getInfoUsernamePassword(param) + + if action == "2": + getInfoCert(param) + + if param.verbose: + if action == "1": + print "\nOK, User access token retrieval and login with username/password was successful" + if action == "2": + print "\nOK, User login with X.509 Certificate was successful" + else: + print "OK" + sys.exit(0)