diff options
author | 2018-10-24 02:15:50 -0600 | |
---|---|---|
committer | 2018-10-31 02:55:23 -0700 | |
commit | 17690987fe470a962164ec168dc805db8a511130 (patch) | |
tree | 6db42b71cdc09e609ce3f6e3bb79a7d2363aaf24 | |
parent | ed7c491c37db044b07ab97781a2d8cab63bcfe99 (diff) |
Enforce AAF permissions
Issue-ID: OPTFRA-331
Change-Id: I046ddef243f73ae90ca0a28184ee0decf73069ee
Signed-off-by: Frank Sandoval <frank.sandoval@oamtechnologies.com>
Signed-off-by: Dileep Ranganathan <dileep.ranganathan@intel.com>
-rwxr-xr-x | conductor.conf | 21 | ||||
-rw-r--r-- | conductor/conductor/api/adapters/aaf/aaf_authentication.py | 118 | ||||
-rw-r--r-- | conductor/conductor/api/controllers/v1/plans.py | 6 | ||||
-rw-r--r-- | conductor/conductor/common/sms.py | 3 | ||||
-rw-r--r-- | conductor/conductor/opts.py | 2 | ||||
-rwxr-xr-x | preload_secrets.yaml | 5 |
6 files changed, 107 insertions, 48 deletions
diff --git a/conductor.conf b/conductor.conf index 027335d..0c0ae2b 100755 --- a/conductor.conf +++ b/conductor.conf @@ -131,20 +131,30 @@ #fatal_deprecations = false -[aaf_authentication] +[aaf_api] # # From conductor # # is_aaf_enabled. (boolean value) -#is_aaf_enabled = true +#is_aaf_enabled = false # aaf_cache_expiry_hrs. (integer value) -#aaf_cache_expiry_hrs = 3 +aaf_cache_expiry_hrs = 3 # aaf_url. (string value) -aaf_url = http://aaf-service:8100/authz/perms/user/ +#aaf_url = https://aaf-service:8100/authz/perms/user/ + +# aaf_cert_file. (string value) +#aaf_cert_file = <None> + +# aaf_cert_key_file. (string value) +#aaf_cert_key_file = <None> + +# aaf_ca_bundle_file. (string value) +#aaf_ca_bundle_file = +aaf_ca_bundle_file = AAF_RootCA.cer # aaf_retries. (integer value) #aaf_retries = 3 @@ -153,7 +163,7 @@ aaf_url = http://aaf-service:8100/authz/perms/user/ #aaf_timeout = 100 # aaf_user_roles. (list value) -#aaf_user_roles = {"type": "org.onap.oof","instance": "plans","action": "GET"},{"type": "org.onap.oof","instance": "plans","action": "POST"} +#aaf_permissions = {"type": "org.onap.oof.access","instance": "*","action": "*"} [aaf_sms] @@ -594,3 +604,4 @@ concurrent = true # Extensions list to use (list value) #extensions = multicloud + diff --git a/conductor/conductor/api/adapters/aaf/aaf_authentication.py b/conductor/conductor/api/adapters/aaf/aaf_authentication.py index e6b79d2..f669ba4 100644 --- a/conductor/conductor/api/adapters/aaf/aaf_authentication.py +++ b/conductor/conductor/api/adapters/aaf/aaf_authentication.py @@ -20,9 +20,11 @@ import base64 from datetime import datetime, timedelta import json +import os from conductor.common import rest from conductor.i18n import _LE, _LI +from conductor import __file__ as conductor_root from oslo_log import log LOG = log.getLogger(__name__) @@ -30,32 +32,46 @@ LOG = log.getLogger(__name__) from oslo_config import cfg CONF = cfg.CONF -# TBD - read values from conductor.conf AAF_OPTS = [ cfg.BoolOpt('is_aaf_enabled', - default=True, + default=False, help='is_aaf_enabled.'), cfg.IntOpt('aaf_cache_expiry_hrs', - default='3', + default='24', help='aaf_cache_expiry_hrs.'), cfg.StrOpt('aaf_url', - default='http://aaf-service:8100/authz/perms/user/', + default='https://aaf-service:8100/authz/perms/user/', help='aaf_url.'), + cfg.StrOpt('username', + default=None, + help='username.'), + cfg.StrOpt('password', + default=None, + help='pasword.'), + cfg.StrOpt('aaf_cert_file', + default=None, + help='aaf_cert_file.'), + cfg.StrOpt('aaf_cert_key_file', + default=None, + help='aaf_cert_key_file.'), + cfg.StrOpt('aaf_ca_bundle_file', + default="", + help='aaf_ca_bundle_file.'), cfg.IntOpt('aaf_retries', default='3', help='aaf_retries.'), cfg.IntOpt('aaf_timeout', default='100', help='aaf_timeout.'), - cfg.ListOpt('aaf_user_roles', - default=['{"type": "org.onap.oof","instance": "plans","action": "GET"}', - '{"type": "org.onap.oof","instance": "plans","action": "POST"}'], + cfg.StrOpt('aaf_conductor_user', + default=None, + help='aaf_conductor_user.'), + cfg.ListOpt('aaf_permissions', + default=['{"type": "org.onap.oof.access","instance": "*","action": "*"}'], help='aaf_user_roles.') ] -CONF.register_opts(AAF_OPTS, group='aaf_authentication') - -AUTHZ_PERMS_USER = '{}/authz/perms/user/{}' +CONF.register_opts(AAF_OPTS, group='aaf_api') EXPIRE_TIME = 'expire_time' @@ -64,11 +80,22 @@ perm_cache = {} def clear_cache(): perm_cache.clear() - def authenticate(uid, passwd): + # FS - trace + LOG.info("Authenticating username:password {} : {}: ".format(uid, passwd)) + + aafUser = None + username = CONF.conductor_api.username + password = CONF.conductor_api.password + if username == uid and password == passwd: + aafUser = CONF.aaf_api.aaf_conductor_user + else: + LOG.debug("Error Authenticating the user {} : {}: ".format(uid, passwd)) + return False + try: - perms = get_aaf_permissions(uid, passwd) - return has_valid_role(perms) + perms = get_aaf_permissions(aafUser) + return has_valid_permissions(perms) except Exception as exp: LOG.error("Error Authenticating the user {} : {}: ".format(uid, exp)) pass @@ -80,22 +107,27 @@ return True if the user has valid permissions else return false """ -def has_valid_role(perms): - aaf_user_roles = CONF.aaf_authentication.aaf_user_roles - - permObj = json.loads(perms) - permList = permObj["perm"] - for user_role in aaf_user_roles: - role = json.loads(user_role) - userType = role["type"] - userInstance = role["instance"] - userAction = role["action"] - for perm in permList: - permType = perm["type"] - permInstance = perm["instance"] - permAction = perm["action"] +def has_valid_permissions(userPerms): + permissions = CONF.aaf_api.aaf_permissions + + LOG.info("Validate permisions: acquired permissions {} ".format(userPerms)) + LOG.info("Validate permisions: allowed permissions {} ".format(permissions)) + + userPermObj = json.loads(userPerms) + userPermList = userPermObj["perm"] + for perm in permissions: + permObj = json.loads(perm) + permType = permObj["type"] + permInstance = permObj["instance"] + permAction = permObj["action"] + for userPerm in userPermList: + userType = userPerm["type"] + userInstance = userPerm["instance"] + userAction = userPerm["action"] if userType == permType and userInstance == permInstance and \ (userAction == permAction or userAction == "*"): + # FS - trace + LOG.info("User has valid permissions ") return True return False @@ -104,35 +136,43 @@ Make the remote aaf api call if user is not in the cache. Return the perms """ -def get_aaf_permissions(uid, passwd): - key = base64.b64encode("{}_{}".format(uid, passwd), "ascii") - time_delta = timedelta(hours = CONF.aaf_authentication.aaf_cache_expiry_hrs) +def get_aaf_permissions(aafUser): + key = base64.b64encode("{}".format(aafUser), "ascii") + time_delta = timedelta(hours = CONF.aaf_api.aaf_cache_expiry_hrs) -# TBD - test cache logic perms = perm_cache.get(key) if perms and datetime.now() < perms.get(EXPIRE_TIME): LOG.debug("Returning cached value") return perms['roles'] LOG.debug("Invoking AAF authentication API") - response = remote_api(passwd, uid) + response = remote_api(aafUser) perms = {EXPIRE_TIME: datetime.now() + time_delta, 'roles': response} perm_cache[key] = perms return response -def remote_api(passwd, uid): - server_url = CONF.aaf_authentication.aaf_url.rstrip('/') + +""" +The remote api is the AAF service + +""" +def remote_api(aafUser): + server_url = CONF.aaf_api.aaf_url+aafUser + kwargs = { "server_url": server_url, - "retries": CONF.aaf_authentication.aaf_retries, - "username": uid, - "password": passwd, + "retries": CONF.aaf_api.aaf_retries, + "username": CONF.aaf_api.username, + "password": CONF.aaf_api.password, "log_debug": LOG.debug, - "read_timeout": CONF.aaf_authentication.aaf_timeout, + "read_timeout": CONF.aaf_api.aaf_timeout, + "cert_file": CONF.aaf_api.aaf_cert_file, + "cert_key_file": CONF.aaf_api.aaf_cert_key_file, + "ca_bundle_file": CONF.aaf_api.aaf_ca_bundle_file, } restReq = rest.REST(**kwargs) - headers = {"Accept": "application/json"} + headers = {"Accept": "application/Perms+json;q=1.0;charset=utf-8;version=2.1,application/json;q=1.0;version=2.1,*/*;q=1.0"} rkwargs = { "method": 'GET', "path": '', diff --git a/conductor/conductor/api/controllers/v1/plans.py b/conductor/conductor/api/controllers/v1/plans.py index b32caef..411686b 100644 --- a/conductor/conductor/api/controllers/v1/plans.py +++ b/conductor/conductor/api/controllers/v1/plans.py @@ -85,7 +85,7 @@ class PlansBaseController(object): def plans_get(self, plan_id=None): - auth_flag = CONF.conductor_api.basic_auth_secure or CONF.aaf_authentication.is_aaf_enabled + auth_flag = CONF.conductor_api.basic_auth_secure or CONF.aaf_api.is_aaf_enabled # TBD - is healthcheck properly supported? if plan_id == 'healthcheck' or \ @@ -291,7 +291,7 @@ class PlansController(PlansBaseController): if args and args['name']: LOG.info('Plan name: {}'.format(args['name'])) - auth_flag = CONF.conductor_api.basic_auth_secure or CONF.aaf_authentication.is_aaf_enabled + auth_flag = CONF.conductor_api.basic_auth_secure or CONF.aaf_api.is_aaf_enabled # Create the plan only when the basic authentication is disabled or pass the authenticaiton check if not auth_flag or \ @@ -360,7 +360,7 @@ def verify_user(authstr): retVal = False - if CONF.aaf_authentication.is_aaf_enabled: + if CONF.aaf_api.is_aaf_enabled: retVal = aaf_auth.authenticate(user_dict['username'], user_dict['password']) else: if username == user_dict['username'] and password == user_dict['password']: diff --git a/conductor/conductor/common/sms.py b/conductor/conductor/common/sms.py index c5eee3a..6e21392 100644 --- a/conductor/conductor/common/sms.py +++ b/conductor/conductor/common/sms.py @@ -111,6 +111,9 @@ def load_secrets(): config.set_override('aafns', secret_dict['music_api']['aafns'], 'music_api') config.set_override('username', secret_dict['sdnc']['username'], 'sdnc') config.set_override('password', secret_dict['sdnc']['password'], 'sdnc') + config.set_override('username', secret_dict['aaf_api']['username'], 'aaf_api') + config.set_override('password', secret_dict['aaf_api']['password'], 'aaf_api') + config.set_override('aaf_conductor_user', secret_dict['aaf_api']['aaf_conductor_user'], 'aaf_api') def delete_secrets(): diff --git a/conductor/conductor/opts.py b/conductor/conductor/opts.py index 106de2d..d711a4e 100644 --- a/conductor/conductor/opts.py +++ b/conductor/conductor/opts.py @@ -75,7 +75,7 @@ def list_opts(): ('solver', conductor.solver.service.SOLVER_OPTS), ('reservation', conductor.reservation.service.reservation_OPTS), ('aaf_sms', conductor.common.sms.AAF_SMS_OPTS), - ('aaf_authentication', + ('aaf_api', conductor.api.adapters.aaf.aaf_authentication.AAF_OPTS), ('prometheus', conductor.common.prometheus_metrics.METRICS_OPTS), ] diff --git a/preload_secrets.yaml b/preload_secrets.yaml index 98e5197..3ec1b92 100755 --- a/preload_secrets.yaml +++ b/preload_secrets.yaml @@ -20,3 +20,8 @@ secrets: aafuser: conductor aafpass: c0nduct0r aafns: conductor +- name: aaf_api + values: + username: aaf_admin@people.osaaf.org + password: demo123456! + aaf_conductor_user: oof@oof.onap.org |