Skip to content

Commit

Permalink
Merge pull request #267 from Mirantis/get-auth-params
Browse files Browse the repository at this point in the history
Add configurations/auth API call
  • Loading branch information
Adam Tengler authored Apr 17, 2018
2 parents 65ef593 + 55330e1 commit 541df6b
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 9 deletions.
15 changes: 15 additions & 0 deletions kqueen/auth/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,18 @@ def verify(self, user, password):
"""

raise NotImplementedError

@classmethod
def get_parameter_schema(cls):
"""Return parameters specific for a auth method implementation.
These parameters are used to generate form for inviting user with fields,
specific for a particular authentication method.
Returns:
dict: Returns ``self.parameter_schema`` by default, but can be overridden.
"""
if not hasattr(cls, 'parameter_schema'):
raise NotImplementedError('"parameter_schema" attribute should be provided in the '
'auth method implementation')
return cls.parameter_schema
4 changes: 2 additions & 2 deletions kqueen/auth/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"parameters": {
"uri": config.get('LDAP_URI'),
"admin_dn": config.get('LDAP_DN'),
"password": config.get('LDAP_PASSWORD')
"_password": config.get('LDAP_PASSWORD')
}
},
"local": {
Expand All @@ -51,7 +51,7 @@ def generate_auth_options(auth_list):
if not auth_options:
auth_options['local'] = {'engine': 'LocalAuth', 'parameters': {}}

logger.debug('Auth config generated {}'.format(auth_options))
logger.debug('Auth configuration options are generated ')
return auth_options


Expand Down
22 changes: 19 additions & 3 deletions kqueen/auth/ldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,29 @@


class LDAPAuth(BaseAuth):
verbose_name = 'LDAP'
parameter_schema = {
'username': {
'type': 'text',
'label': 'User CN',
'description': 'Enter user common name, resisted in the configured LDAP',
'validators': {
'required': True
},
'generate_password': False,
# TODO: add checkbox for notify if email is provided
'notify': False,
# TODO: add optional email field for ldap user
},
}

def __init__(self, *args, **kwargs):
"""
Implementation of :func:`~kqueen.auth.base.__init__`
"""

super(LDAPAuth, self).__init__(*args, **kwargs)
if not all(hasattr(self, attr) for attr in ['uri', 'admin_dn', 'password']):
if not all(hasattr(self, attr) for attr in ['uri', 'admin_dn', '_password']):
msg = 'Failed to configure LDAP, please provide valid LDAP credentials'
logger.error(msg)
raise ImproperlyConfigured(msg)
Expand All @@ -27,9 +43,9 @@ def __init__(self, *args, **kwargs):
self.kqueen_dc = ','.join(dc_list)

# Bind connection for Kqueen Read-only user
if self._bind(self.admin_dn, self.password):
if self._bind(self.admin_dn, self._password):
self.connection = ldap.initialize(self.uri)
self.connection.simple_bind_s(self.admin_dn, self.password)
self.connection.simple_bind_s(self.admin_dn, self._password)
self.connection.protocol_version = ldap.VERSION3
else:
msg = 'Failed to bind connection for Kqueen Read-only user'
Expand Down
14 changes: 14 additions & 0 deletions kqueen/auth/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@


class LocalAuth(BaseAuth):
verbose_name = 'Local'
parameter_schema = {
'username': {
'type': 'email',
'label': 'User Email',
'description': 'Provide valid email of the user you want to invite to the organization',
'validators': {
'required': True
},
'active': False,
'notify': True
}
}

def verify(self, user, password):
"""Implementation of :func:`~kqueen.auth.base.__init__`
Expand Down
6 changes: 3 additions & 3 deletions kqueen/auth/test_ldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def setup(self, user):
self.user.password = ''
self.user.save()

self.auth_class = LDAPAuth(uri='ldap://127.0.0.1', admin_dn='cn=admin,dc=example,dc=org', password='heslo123')
self.auth_class = LDAPAuth(uri='ldap://127.0.0.1', admin_dn='cn=admin,dc=example,dc=org', _password='heslo123')

def test_raise_on_missing_creds(self):
with pytest.raises(Exception, msg='Failed to configure LDAP, please provide valid LDAP credentials'):
Expand All @@ -29,8 +29,8 @@ def test_login_pass(self):
assert error is None

def test_login_bad_pass(self):
password = 'abc'
user, error = self.auth_class.verify(self.user, password)
_password = 'abc'
user, error = self.auth_class.verify(self.user, _password)

assert not user
assert error == 'Failed to validate full-DN. Check CN name and defined password of invited user'
Expand Down
34 changes: 34 additions & 0 deletions kqueen/blueprints/api/api3_0.yml
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,24 @@ components:
$ref: "https://raw.githubusercontent.com/kubernetes/kubernetes/master/api/openapi-spec/swagger.json#/definitions/io.k8s.api.core.v1.ServiceList"
version:
$ref: "https://raw.githubusercontent.com/kubernetes/kubernetes/master/api/openapi-spec/swagger.json#/definitions/io.k8s.apimachinery.pkg.version.Info"
getAuthConfig:
type: "object"
auth_method_name:
type: "object":
properties:
engine:
type: "string"
description: "Name of the authentication class"
name:
type: "string"
description: "Verbose name of the authentication method"
ui_parameters:
type: "object"
description: "Auth-method specific parameters"
parameters:
type: "object"
description: "Authentication class initialization parameters"

responses:
Unauthorized:
description: "Authorization information is missing or invalid."
Expand Down Expand Up @@ -1041,3 +1059,19 @@ paths:
$ref: "#/components/schemas/Engine"
"401":
$ref: "#/components/responses/Unauthorized"
/configurations/auth:
get:
summary: "Get information about auth classes configuration"
tags:
- "Auth"
responses:
"200":
description: "Successful operation"
content:
application/json:
schema:
type: "array"
$ref: "#/components/schemas/getUser"
"401":
$ref: "#/components/responses/Unauthorized"

34 changes: 33 additions & 1 deletion kqueen/blueprints/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from flask_jwt import jwt_required
from importlib import import_module
from kqueen.auth import encrypt_password
from kqueen.auth.common import generate_auth_options
from kqueen.models import Cluster
from kqueen.models import Organization
from kqueen.models import Provisioner
Expand Down Expand Up @@ -301,6 +302,7 @@ def provisioner_engine_list():
'parameters': parameters
})
except NotImplementedError:
logger.exception('UI parameters is not set for engine: {}'.format(engine))
engine_cls.append({
'name': engine,
'verbose_name': engine,
Expand All @@ -310,7 +312,7 @@ def provisioner_engine_list():
}
})
except Exception:
logger.exception('Unable to read parameters for engine: ')
logger.exception('Unable to read parameters for engine: {}'.format(engine))

return jsonify(engine_cls)

Expand Down Expand Up @@ -470,3 +472,33 @@ def swagger_json():
abort(500)

return jsonify(data)


@api.route('/configurations/auth', methods=['GET'])
@jwt_required()
def auth_params_configuration():

auth_opts = generate_auth_options(config.get("AUTH_MODULES"))
try:
for name, configuration in auth_opts.items():
auth_cls_name = configuration['engine']
module = import_module('kqueen.auth')
_class = getattr(module, auth_cls_name)

# Add UI fields description, verbose name and hide secure parameters
secure_params = {}
for k, v in configuration['parameters'].items():
if k.startswith('_'):
v = '*****'
secure_params[k] = v

auth_opts[name].update(
{'ui_parameters': _class.get_parameter_schema(),
'name': getattr(_class, 'verbose_name', name),
'parameters': secure_params})

except NotImplementedError:
logger.exception('UI parameters is not specified for "{}" auth type'.format(name))
except Exception:
logger.exception('Unable to read UI parameters for "{}" auth type'.format(name))
return jsonify(auth_opts)
3 changes: 3 additions & 0 deletions kqueen/engines/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ def get_parameter_schema(cls):
Returns:
dict: Returns ``self.parameter_schema`` in default, but can be overridden.
"""
if not hasattr(cls, 'parameter_schema'):
raise NotImplementedError('"parameter_schema" attribute should be provided in the '
'Provisioner class implementation')
return cls.parameter_schema

def get_progress(self):
Expand Down

0 comments on commit 541df6b

Please sign in to comment.