[PATCHv1 0/4] LDAP authentication

From: Royce Lv <lvroyce@linux.vnet.ibm.com> How to test: 1. set an LDAP based on PAM: http://www.debuntu.org/how-to-set-up-a-ldap-server-and-its-clients/ 2. uncommented ldap section in config Tested: 1. authentication with valid user/password 2. wrong password 3. non-existent user 4. wrong ldap settings Royce Lv (4): Add configuration of LDAP Split PAM and LDAP authentication Add LDAP authentication Fix test cases for authentication contrib/DEBIAN/control.in | 1 + contrib/kimchi.spec.fedora.in | 1 + contrib/kimchi.spec.suse.in | 1 + src/kimchi.conf.in | 9 +++ src/kimchi/auth.py | 153 +++++++++++++++++++++++++++++++----------- src/kimchi/config.py.in | 5 ++ tests/utils.py | 43 ++++++++---- 7 files changed, 159 insertions(+), 54 deletions(-) -- 1.8.3.2

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Add LDAP configuration to specify LDAP server, search base and filter for query user. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi.conf.in | 9 +++++++++ src/kimchi/config.py.in | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/src/kimchi.conf.in b/src/kimchi.conf.in index ea39292..5c54d4e 100644 --- a/src/kimchi.conf.in +++ b/src/kimchi.conf.in @@ -43,3 +43,12 @@ [display] # Port for websocket proxy to listen on #display_proxy_port = 64667 + +[authentication] +# Authentication method, available option: pam, ldap. +# method = pam + +# Following fields need to be specified if using LDAP. +# ldap_server = "localhost" +# ldap_search_base = "ou=People, dc=kimchi, dc=org" +# ldap_search_filter = "uid=%(username)s" diff --git a/src/kimchi/config.py.in b/src/kimchi/config.py.in index 097c017..887fe63 100644 --- a/src/kimchi/config.py.in +++ b/src/kimchi/config.py.in @@ -259,6 +259,11 @@ def _get_config(): config.set("server", "environment", "production") config.set("server", "federation", "off") config.set('server', 'max_body_size', '4*1024*1024') + config.add_section("authentication") + config.set("authentication", "method", "pam") + config.set("authentication", "ldap_server", "") + config.set("authentication", "ldap_search_base", "") + config.set("authentication", "ldap_search_filter", "") config.add_section("logging") config.set("logging", "log_dir", paths.log_dir) config.set("logging", "log_level", DEFAULT_LOG_LEVEL) -- 1.8.3.2

On 10/20/2014 11:52 AM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add LDAP configuration to specify LDAP server, search base and filter for query user.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi.conf.in | 9 +++++++++ src/kimchi/config.py.in | 5 +++++ 2 files changed, 14 insertions(+)
diff --git a/src/kimchi.conf.in b/src/kimchi.conf.in index ea39292..5c54d4e 100644 --- a/src/kimchi.conf.in +++ b/src/kimchi.conf.in @@ -43,3 +43,12 @@ [display] # Port for websocket proxy to listen on #display_proxy_port = 64667 + +[authentication] +# Authentication method, available option: pam, ldap. +# method = pam +
+# Following fields need to be specified if using LDAP. +# ldap_server = "localhost" +# ldap_search_base = "ou=People, dc=kimchi, dc=org" +# ldap_search_filter = "uid=%(username)s"
We should keep the default values in blank as the default authentication method is PAM. And to guide user to input the right values we can add some samples: # Following fields need to be specified if using LDAP # LDAP server. Specify an IP or hostname. # Example: localhost # ldap_server = "" # LDAP search base. Specify the search base value. # Example: "ou=People, dc=kimchi, dc=org" # ldap_search_base = "" # LDAP search filter. Specify the search filter in a python format. # Example: "uid=%(username)s" # ldap_search_filter = ""
diff --git a/src/kimchi/config.py.in b/src/kimchi/config.py.in index 097c017..887fe63 100644 --- a/src/kimchi/config.py.in +++ b/src/kimchi/config.py.in @@ -259,6 +259,11 @@ def _get_config(): config.set("server", "environment", "production") config.set("server", "federation", "off") config.set('server', 'max_body_size', '4*1024*1024') + config.add_section("authentication") + config.set("authentication", "method", "pam") + config.set("authentication", "ldap_server", "") + config.set("authentication", "ldap_search_base", "") + config.set("authentication", "ldap_search_filter", "") config.add_section("logging") config.set("logging", "log_dir", paths.log_dir) config.set("logging", "log_level", DEFAULT_LOG_LEVEL)

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Split PAM authentication implementation and abstract a common class. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/auth.py | 111 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 40 deletions(-) diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index c8801a5..10c7c1f 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -31,6 +31,7 @@ import urllib2 from kimchi import template +from kimchi.config import config from kimchi.exception import InvalidOperation, OperationFailed from kimchi.utils import get_all_tabs, run_command @@ -59,6 +60,22 @@ def debug(msg): class User(object): + @classmethod + def create(cls, auth_args): + auth_type = auth_args.pop('auth_type') + for klass in cls.__subclasses__(): + if auth_type == klass.auth_type: + try: + if not klass.authenticate(**auth_args): + debug("cannot verify user with the given password") + return None + except OperationFailed: + raise + return klass(auth_args['username']) + + +class PAMUser(User): + auth_type = "pam" def __init__(self, username): self.user = {} @@ -124,40 +141,54 @@ class User(object): def get_user(self): return self.user + @staticmethod + def authenticate(username, password, service="passwd"): + '''Returns True if authenticate is OK via PAM.''' + def _pam_conv(auth, query_list, userData=None): + resp = [] + for i in range(len(query_list)): + query, qtype = query_list[i] + if qtype == PAM.PAM_PROMPT_ECHO_ON: + resp.append((username, 0)) + elif qtype == PAM.PAM_PROMPT_ECHO_OFF: + resp.append((password, 0)) + elif qtype == PAM.PAM_PROMPT_ERROR_MSG: + cherrypy.log.error_log.error( + "PAM authenticate prompt error: %s" % query) + resp.append(('', 0)) + elif qtype == PAM.PAM_PROMPT_TEXT_INFO: + resp.append(('', 0)) + else: + return None + return resp + + auth = PAM.pam() + auth.start(service) + auth.set_item(PAM.PAM_USER, username) + auth.set_item(PAM.PAM_CONV, _pam_conv) + try: + auth.authenticate() + except PAM.error, (resp, code): + msg_args = {'username': username, 'code': code} + raise OperationFailed("KCHAUTH0001E", msg_args) -def authenticate(username, password, service="passwd"): - '''Returns True if authenticate is OK via PAM.''' - def _pam_conv(auth, query_list, userData=None): - resp = [] - for i in range(len(query_list)): - query, qtype = query_list[i] - if qtype == PAM.PAM_PROMPT_ECHO_ON: - resp.append((username, 0)) - elif qtype == PAM.PAM_PROMPT_ECHO_OFF: - resp.append((password, 0)) - elif qtype == PAM.PAM_PROMPT_ERROR_MSG: - cherrypy.log.error_log.error("PAM authenticate prompt error " - "message: %s" % query) - resp.append(('', 0)) - elif qtype == PAM.PAM_PROMPT_TEXT_INFO: - resp.append(('', 0)) - else: - return None - return resp - - auth = PAM.pam() - auth.start(service) - auth.set_item(PAM.PAM_USER, username) - auth.set_item(PAM.PAM_CONV, _pam_conv) - - try: - auth.authenticate() - except PAM.error: - raise - - return True + return True +class LDAPUser(User): + auth_type = "ldap" + def __init__(self, username): + self.user = {} + self.user[USER_NAME] = username + self.user[USER_GROUPS] = None + # FIXME: user roles will be changed according roles assignment after + # objstore is integrated + self.user[USER_ROLES] = dict.fromkeys(tabs, 'user') + + @staticmethod + def authenticate(username, password): + return False + def from_browser(): # Enable Basic Authentication for REST tools. # Ajax request sent from jQuery in browser will have "X-Requested-With" @@ -216,15 +247,15 @@ def check_auth_httpba(): def login(username, password, **kwargs): - try: - if not authenticate(username, password): - debug("User cannot be verified with the supplied password") - return None - except PAM.error, (resp, code): - msg_args = {'username': username, 'code': code} - raise OperationFailed("KCHAUTH0001E", msg_args) - - user = User(username) + auth_args = {'auth_type': config.get("authentication", "method"), + 'username': username, + 'password': password} + + user = User.create(auth_args) + if not user: + debug("User cannot be verified with the supplied password") + return None + debug("User verified, establishing session") cherrypy.session.acquire_lock() cherrypy.session.regenerate() -- 1.8.3.2

On 10/20/2014 11:52 AM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Split PAM authentication implementation and abstract a common class.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/auth.py | 111 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 40 deletions(-)
diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index c8801a5..10c7c1f 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -31,6 +31,7 @@ import urllib2
from kimchi import template +from kimchi.config import config from kimchi.exception import InvalidOperation, OperationFailed from kimchi.utils import get_all_tabs, run_command
@@ -59,6 +60,22 @@ def debug(msg):
class User(object):
As it is related to authentication, I suggest renaming it to "Authentication"
+ @classmethod + def create(cls, auth_args): + auth_type = auth_args.pop('auth_type') + for klass in cls.__subclasses__(): + if auth_type == klass.auth_type: + try: + if not klass.authenticate(**auth_args): + debug("cannot verify user with the given password") + return None + except OperationFailed: + raise + return klass(auth_args['username']) + +
We could do it in the __init__() function and return the right instance according to the authentication method. Example: class Authentication(object): def __init__(self, auth_type): + for klass in cls.__subclasses__(): + if auth_type == klass.auth_type: + return klass()
+class PAMUser(User):
class PAMAuthentication(Authentication);
+ auth_type = "pam"
def __init__(self, username):
You can get the username/password only when doing the authentication
self.user = {} @@ -124,40 +141,54 @@ class User(object): def get_user(self): return self.user
+ @staticmethod
It will not be static with you have a instance according to type as I suggested above.
+ def authenticate(username, password, service="passwd"): + '''Returns True if authenticate is OK via PAM.''' + def _pam_conv(auth, query_list, userData=None): + resp = [] + for i in range(len(query_list)): + query, qtype = query_list[i] + if qtype == PAM.PAM_PROMPT_ECHO_ON: + resp.append((username, 0)) + elif qtype == PAM.PAM_PROMPT_ECHO_OFF: + resp.append((password, 0)) + elif qtype == PAM.PAM_PROMPT_ERROR_MSG: + cherrypy.log.error_log.error( + "PAM authenticate prompt error: %s" % query) + resp.append(('', 0)) + elif qtype == PAM.PAM_PROMPT_TEXT_INFO: + resp.append(('', 0)) + else: + return None + return resp + + auth = PAM.pam() + auth.start(service) + auth.set_item(PAM.PAM_USER, username) + auth.set_item(PAM.PAM_CONV, _pam_conv) + try: + auth.authenticate() + except PAM.error, (resp, code): + msg_args = {'username': username, 'code': code} + raise OperationFailed("KCHAUTH0001E", msg_args)
-def authenticate(username, password, service="passwd"): - '''Returns True if authenticate is OK via PAM.''' - def _pam_conv(auth, query_list, userData=None): - resp = [] - for i in range(len(query_list)): - query, qtype = query_list[i] - if qtype == PAM.PAM_PROMPT_ECHO_ON: - resp.append((username, 0)) - elif qtype == PAM.PAM_PROMPT_ECHO_OFF: - resp.append((password, 0)) - elif qtype == PAM.PAM_PROMPT_ERROR_MSG: - cherrypy.log.error_log.error("PAM authenticate prompt error " - "message: %s" % query) - resp.append(('', 0)) - elif qtype == PAM.PAM_PROMPT_TEXT_INFO: - resp.append(('', 0)) - else: - return None - return resp - - auth = PAM.pam() - auth.start(service) - auth.set_item(PAM.PAM_USER, username) - auth.set_item(PAM.PAM_CONV, _pam_conv) - - try: - auth.authenticate() - except PAM.error: - raise - - return True + return True
+class LDAPUser(User):
class LDAPAuthentication(Authentication):
+ auth_type = "ldap" + def __init__(self, username):
Same I commented above. You can receive the username/password only when doing the authentication itself.
+ self.user = {} + self.user[USER_NAME] = username + self.user[USER_GROUPS] = None + # FIXME: user roles will be changed according roles assignment after + # objstore is integrated + self.user[USER_ROLES] = dict.fromkeys(tabs, 'user') + + @staticmethod + def authenticate(username, password): + return False + def from_browser(): # Enable Basic Authentication for REST tools. # Ajax request sent from jQuery in browser will have "X-Requested-With" @@ -216,15 +247,15 @@ def check_auth_httpba():
def login(username, password, **kwargs): - try: - if not authenticate(username, password): - debug("User cannot be verified with the supplied password") - return None - except PAM.error, (resp, code): - msg_args = {'username': username, 'code': code} - raise OperationFailed("KCHAUTH0001E", msg_args) - - user = User(username)
+ auth_args = {'auth_type': config.get("authentication", "method"), + 'username': username, + 'password': password} + + user = User.create(auth_args) + if not user: + debug("User cannot be verified with the supplied password") + return None +
auth = Authentication(config.get("authentication", "method")) user = auth.authentication(username, password)
debug("User verified, establishing session") cherrypy.session.acquire_lock() cherrypy.session.regenerate()

On 2014年10月22日 02:39, Aline Manera wrote:
On 10/20/2014 11:52 AM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Split PAM authentication implementation and abstract a common class.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/auth.py | 111 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 40 deletions(-)
diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index c8801a5..10c7c1f 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -31,6 +31,7 @@ import urllib2
from kimchi import template +from kimchi.config import config from kimchi.exception import InvalidOperation, OperationFailed from kimchi.utils import get_all_tabs, run_command
@@ -59,6 +60,22 @@ def debug(msg):
class User(object):
As it is related to authentication, I suggest renaming it to "Authentication"
It is not just authentication, it is related to user authentication, authorization, user role/group get. (see things already there in PAMUser) See below: In my refactor, Users only allows to be created when it has right auth_type as well as pass the authentication of this type. Otherwise we don't have the need to create such type of user.
+ @classmethod + def create(cls, auth_args): + auth_type = auth_args.pop('auth_type') + for klass in cls.__subclasses__(): + if auth_type == klass.auth_type: + try: + if not klass.authenticate(**auth_args): + debug("cannot verify user with the given password") + return None + except OperationFailed: + raise + return klass(auth_args['username']) + +
We could do it in the __init__() function and return the right instance according to the authentication method. Example:
class Authentication(object): def __init__(self, auth_type):
+ for klass in cls.__subclasses__(): + if auth_type == klass.auth_type: + return klass()
+class PAMUser(User):
class PAMAuthentication(Authentication);
+ auth_type = "pam"
def __init__(self, username):
You can get the username/password only when doing the authentication
self.user = {} @@ -124,40 +141,54 @@ class User(object): def get_user(self): return self.user
+ @staticmethod
It will not be static with you have a instance according to type as I suggested above.
+ def authenticate(username, password, service="passwd"): + '''Returns True if authenticate is OK via PAM.''' + def _pam_conv(auth, query_list, userData=None): + resp = [] + for i in range(len(query_list)): + query, qtype = query_list[i] + if qtype == PAM.PAM_PROMPT_ECHO_ON: + resp.append((username, 0)) + elif qtype == PAM.PAM_PROMPT_ECHO_OFF: + resp.append((password, 0)) + elif qtype == PAM.PAM_PROMPT_ERROR_MSG: + cherrypy.log.error_log.error( + "PAM authenticate prompt error: %s" % query) + resp.append(('', 0)) + elif qtype == PAM.PAM_PROMPT_TEXT_INFO: + resp.append(('', 0)) + else: + return None + return resp + + auth = PAM.pam() + auth.start(service) + auth.set_item(PAM.PAM_USER, username) + auth.set_item(PAM.PAM_CONV, _pam_conv) + try: + auth.authenticate() + except PAM.error, (resp, code): + msg_args = {'username': username, 'code': code} + raise OperationFailed("KCHAUTH0001E", msg_args)
-def authenticate(username, password, service="passwd"): - '''Returns True if authenticate is OK via PAM.''' - def _pam_conv(auth, query_list, userData=None): - resp = [] - for i in range(len(query_list)): - query, qtype = query_list[i] - if qtype == PAM.PAM_PROMPT_ECHO_ON: - resp.append((username, 0)) - elif qtype == PAM.PAM_PROMPT_ECHO_OFF: - resp.append((password, 0)) - elif qtype == PAM.PAM_PROMPT_ERROR_MSG: - cherrypy.log.error_log.error("PAM authenticate prompt error " - "message: %s" % query) - resp.append(('', 0)) - elif qtype == PAM.PAM_PROMPT_TEXT_INFO: - resp.append(('', 0)) - else: - return None - return resp - - auth = PAM.pam() - auth.start(service) - auth.set_item(PAM.PAM_USER, username) - auth.set_item(PAM.PAM_CONV, _pam_conv) - - try: - auth.authenticate() - except PAM.error: - raise - - return True + return True
+class LDAPUser(User):
class LDAPAuthentication(Authentication):
+ auth_type = "ldap" + def __init__(self, username):
Same I commented above. You can receive the username/password only when doing the authentication itself.
+ self.user = {} + self.user[USER_NAME] = username + self.user[USER_GROUPS] = None + # FIXME: user roles will be changed according roles assignment after + # objstore is integrated + self.user[USER_ROLES] = dict.fromkeys(tabs, 'user') + + @staticmethod + def authenticate(username, password): + return False + def from_browser(): # Enable Basic Authentication for REST tools. # Ajax request sent from jQuery in browser will have "X-Requested-With" @@ -216,15 +247,15 @@ def check_auth_httpba():
def login(username, password, **kwargs): - try: - if not authenticate(username, password): - debug("User cannot be verified with the supplied password") - return None - except PAM.error, (resp, code): - msg_args = {'username': username, 'code': code} - raise OperationFailed("KCHAUTH0001E", msg_args) - - user = User(username)
+ auth_args = {'auth_type': config.get("authentication", "method"), + 'username': username, + 'password': password} + + user = User.create(auth_args) + if not user: + debug("User cannot be verified with the supplied password") + return None +
auth = Authentication(config.get("authentication", "method")) user = auth.authentication(username, password)
Nope, this part already covered in factory method
debug("User verified, establishing session") cherrypy.session.acquire_lock() cherrypy.session.regenerate()
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

On 10/22/2014 04:02 AM, Royce Lv wrote:
On 2014年10月22日 02:39, Aline Manera wrote:
On 10/20/2014 11:52 AM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Split PAM authentication implementation and abstract a common class.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- src/kimchi/auth.py | 111 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 40 deletions(-)
diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index c8801a5..10c7c1f 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -31,6 +31,7 @@ import urllib2
from kimchi import template +from kimchi.config import config from kimchi.exception import InvalidOperation, OperationFailed from kimchi.utils import get_all_tabs, run_command
@@ -59,6 +60,22 @@ def debug(msg):
class User(object):
As it is related to authentication, I suggest renaming it to "Authentication"
It is not just authentication, it is related to user authentication, authorization, user role/group get. (see things already there in PAMUser)
OK.
See below: In my refactor, Users only allows to be created when it has right auth_type as well as pass the authentication of this type. Otherwise we don't have the need to create such type of user.
If you don't want to change it, please rename "create" by "get" or something as Kimchi does not create any user.
+ @classmethod + def create(cls, auth_args): + auth_type = auth_args.pop('auth_type') + for klass in cls.__subclasses__(): + if auth_type == klass.auth_type: + try: + if not klass.authenticate(**auth_args): + debug("cannot verify user with the given password") + return None + except OperationFailed: + raise + return klass(auth_args['username']) + +
We could do it in the __init__() function and return the right instance according to the authentication method. Example:
class Authentication(object): def __init__(self, auth_type):
+ for klass in cls.__subclasses__(): + if auth_type == klass.auth_type: + return klass()
+class PAMUser(User):
class PAMAuthentication(Authentication);
+ auth_type = "pam"
def __init__(self, username):
You can get the username/password only when doing the authentication
self.user = {} @@ -124,40 +141,54 @@ class User(object): def get_user(self): return self.user
+ @staticmethod
It will not be static with you have a instance according to type as I suggested above.
+ def authenticate(username, password, service="passwd"): + '''Returns True if authenticate is OK via PAM.''' + def _pam_conv(auth, query_list, userData=None): + resp = [] + for i in range(len(query_list)): + query, qtype = query_list[i] + if qtype == PAM.PAM_PROMPT_ECHO_ON: + resp.append((username, 0)) + elif qtype == PAM.PAM_PROMPT_ECHO_OFF: + resp.append((password, 0)) + elif qtype == PAM.PAM_PROMPT_ERROR_MSG: + cherrypy.log.error_log.error( + "PAM authenticate prompt error: %s" % query) + resp.append(('', 0)) + elif qtype == PAM.PAM_PROMPT_TEXT_INFO: + resp.append(('', 0)) + else: + return None + return resp + + auth = PAM.pam() + auth.start(service) + auth.set_item(PAM.PAM_USER, username) + auth.set_item(PAM.PAM_CONV, _pam_conv) + try: + auth.authenticate() + except PAM.error, (resp, code): + msg_args = {'username': username, 'code': code} + raise OperationFailed("KCHAUTH0001E", msg_args)
-def authenticate(username, password, service="passwd"): - '''Returns True if authenticate is OK via PAM.''' - def _pam_conv(auth, query_list, userData=None): - resp = [] - for i in range(len(query_list)): - query, qtype = query_list[i] - if qtype == PAM.PAM_PROMPT_ECHO_ON: - resp.append((username, 0)) - elif qtype == PAM.PAM_PROMPT_ECHO_OFF: - resp.append((password, 0)) - elif qtype == PAM.PAM_PROMPT_ERROR_MSG: - cherrypy.log.error_log.error("PAM authenticate prompt error " - "message: %s" % query) - resp.append(('', 0)) - elif qtype == PAM.PAM_PROMPT_TEXT_INFO: - resp.append(('', 0)) - else: - return None - return resp - - auth = PAM.pam() - auth.start(service) - auth.set_item(PAM.PAM_USER, username) - auth.set_item(PAM.PAM_CONV, _pam_conv) - - try: - auth.authenticate() - except PAM.error: - raise - - return True + return True
+class LDAPUser(User):
class LDAPAuthentication(Authentication):
+ auth_type = "ldap" + def __init__(self, username):
Same I commented above. You can receive the username/password only when doing the authentication itself.
+ self.user = {} + self.user[USER_NAME] = username + self.user[USER_GROUPS] = None + # FIXME: user roles will be changed according roles assignment after + # objstore is integrated + self.user[USER_ROLES] = dict.fromkeys(tabs, 'user') + + @staticmethod + def authenticate(username, password): + return False + def from_browser(): # Enable Basic Authentication for REST tools. # Ajax request sent from jQuery in browser will have "X-Requested-With" @@ -216,15 +247,15 @@ def check_auth_httpba():
def login(username, password, **kwargs): - try: - if not authenticate(username, password): - debug("User cannot be verified with the supplied password") - return None - except PAM.error, (resp, code): - msg_args = {'username': username, 'code': code} - raise OperationFailed("KCHAUTH0001E", msg_args) - - user = User(username)
+ auth_args = {'auth_type': config.get("authentication", "method"), + 'username': username, + 'password': password} + + user = User.create(auth_args) + if not user: + debug("User cannot be verified with the supplied password") + return None +
auth = Authentication(config.get("authentication", "method")) user = auth.authentication(username, password)
Nope, this part already covered in factory method
debug("User verified, establishing session") cherrypy.session.acquire_lock() cherrypy.session.regenerate()
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Add LDAP authentication, also deals with invalid user, LDAP search base configure error and other LDAP errors. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- contrib/DEBIAN/control.in | 1 + contrib/kimchi.spec.fedora.in | 1 + contrib/kimchi.spec.suse.in | 1 + src/kimchi/auth.py | 44 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/contrib/DEBIAN/control.in b/contrib/DEBIAN/control.in index 7372a58..0721960 100644 --- a/contrib/DEBIAN/control.in +++ b/contrib/DEBIAN/control.in @@ -27,6 +27,7 @@ Depends: python-cherrypy3 (>= 3.2.0), firewalld, nginx, python-guestfs, + python-ldap, libguestfs-tools Build-Depends: libxslt, python-libxml2, diff --git a/contrib/kimchi.spec.fedora.in b/contrib/kimchi.spec.fedora.in index 2ca3076..fcb8c11 100644 --- a/contrib/kimchi.spec.fedora.in +++ b/contrib/kimchi.spec.fedora.in @@ -29,6 +29,7 @@ Requires: nfs-utils Requires: nginx Requires: iscsi-initiator-utils Requires: policycoreutils-python +Requires: python-ldap Requires: python-libguestfs Requires: libguestfs-tools BuildRequires: libxslt diff --git a/contrib/kimchi.spec.suse.in b/contrib/kimchi.spec.suse.in index 9ea240c..b8f0531 100644 --- a/contrib/kimchi.spec.suse.in +++ b/contrib/kimchi.spec.suse.in @@ -23,6 +23,7 @@ Requires: python-psutil >= 0.6.0 Requires: python-jsonschema >= 1.3.0 Requires: python-ethtool Requires: python-ipaddr +Requires: python-ldap Requires: python-lxml Requires: python-xml Requires: nfs-client diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index 10c7c1f..162bbfd 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -20,6 +20,7 @@ import base64 import cherrypy import fcntl +import ldap import multiprocessing import os import PAM @@ -177,6 +178,7 @@ class PAMUser(User): class LDAPUser(User): auth_type = "ldap" + def __init__(self, username): self.user = {} self.user[USER_NAME] = username @@ -187,7 +189,47 @@ class LDAPUser(User): @staticmethod def authenticate(username, password): - return False + ldap_server = config.get("authentication", "ldap_server").strip('"') + ldap_search_base = config.get( + "authentication", "ldap_search_base").strip('"') + ldap_search_filter = config.get( + "authentication", "ldap_search_filter", + vars={"username": username.encode("utf-8")}).strip('"') + + connect = ldap.open(ldap_server) + try: + try: + result = connect.search_s( + ldap_search_base, ldap.SCOPE_SUBTREE, ldap_search_filter) + if len(result) == 0: + entity = ldap_search_filter % {'username': username} + raise ldap.LDAPError("Invalid ldap entity:%s" % entity) + except ldap.NO_SUCH_OBJECT: + # ldap search base specified wrongly. + raise ldap.LDAPError( + "invalid ldap search base %s" % ldap_search_base) + + try: + connect.bind_s(result[0][0], password) + except ldap.INVALID_CREDENTIALS: + # invalid user password + raise ldap.LDAPError("invalid user/passwd") + connect.unbind_s() + return True + except ldap.LDAPError, e: + arg = {"username": username, "code": e.message} + raise OperationFailed("KCHAUTH0001E", arg) + + def get_groups(self): + return self.user[USER_GROUPS] + + def get_roles(self): + self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin') + return self.user[USER_ROLES] + + def get_user(self): + return self.user + def from_browser(): # Enable Basic Authentication for REST tools. -- 1.8.3.2

On 10/20/2014 11:52 AM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add LDAP authentication, also deals with invalid user, LDAP search base configure error and other LDAP errors.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- contrib/DEBIAN/control.in | 1 + contrib/kimchi.spec.fedora.in | 1 + contrib/kimchi.spec.suse.in | 1 + src/kimchi/auth.py | 44 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 46 insertions(+), 1 deletion(-)
diff --git a/contrib/DEBIAN/control.in b/contrib/DEBIAN/control.in index 7372a58..0721960 100644 --- a/contrib/DEBIAN/control.in +++ b/contrib/DEBIAN/control.in @@ -27,6 +27,7 @@ Depends: python-cherrypy3 (>= 3.2.0), firewalld, nginx, python-guestfs, + python-ldap, libguestfs-tools Build-Depends: libxslt, python-libxml2, diff --git a/contrib/kimchi.spec.fedora.in b/contrib/kimchi.spec.fedora.in index 2ca3076..fcb8c11 100644 --- a/contrib/kimchi.spec.fedora.in +++ b/contrib/kimchi.spec.fedora.in @@ -29,6 +29,7 @@ Requires: nfs-utils Requires: nginx Requires: iscsi-initiator-utils Requires: policycoreutils-python +Requires: python-ldap Requires: python-libguestfs Requires: libguestfs-tools BuildRequires: libxslt diff --git a/contrib/kimchi.spec.suse.in b/contrib/kimchi.spec.suse.in index 9ea240c..b8f0531 100644 --- a/contrib/kimchi.spec.suse.in +++ b/contrib/kimchi.spec.suse.in @@ -23,6 +23,7 @@ Requires: python-psutil >= 0.6.0 Requires: python-jsonschema >= 1.3.0 Requires: python-ethtool Requires: python-ipaddr +Requires: python-ldap Requires: python-lxml Requires: python-xml Requires: nfs-client diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index 10c7c1f..162bbfd 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -20,6 +20,7 @@ import base64 import cherrypy import fcntl +import ldap import multiprocessing import os import PAM @@ -177,6 +178,7 @@ class PAMUser(User):
class LDAPUser(User): auth_type = "ldap" + def __init__(self, username): self.user = {} self.user[USER_NAME] = username @@ -187,7 +189,47 @@ class LDAPUser(User):
@staticmethod def authenticate(username, password): - return False + ldap_server = config.get("authentication", "ldap_server").strip('"') + ldap_search_base = config.get( + "authentication", "ldap_search_base").strip('"') + ldap_search_filter = config.get( + "authentication", "ldap_search_filter", + vars={"username": username.encode("utf-8")}).strip('"') + + connect = ldap.open(ldap_server) + try: + try: + result = connect.search_s( + ldap_search_base, ldap.SCOPE_SUBTREE, ldap_search_filter) + if len(result) == 0: + entity = ldap_search_filter % {'username': username} + raise ldap.LDAPError("Invalid ldap entity:%s" % entity) + except ldap.NO_SUCH_OBJECT: + # ldap search base specified wrongly. + raise ldap.LDAPError( + "invalid ldap search base %s" % ldap_search_base) + + try: + connect.bind_s(result[0][0], password) + except ldap.INVALID_CREDENTIALS: + # invalid user password + raise ldap.LDAPError("invalid user/passwd") + connect.unbind_s() + return True + except ldap.LDAPError, e: + arg = {"username": username, "code": e.message} + raise OperationFailed("KCHAUTH0001E", arg) + + def get_groups(self): + return self.user[USER_GROUPS] +
+ def get_roles(self): + self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin') + return self.user[USER_ROLES]
The admin ID's should be listed on Kimchi config file, instead of doing admin permissions to all users. So on __init__(): self.admin_users = config.get("authentication", "ldap_admin_users") self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin') if self.user[USERNAME] in self.admin_users else dict.fromkeys(tabs, 'user') And on get_roles(): def get_roles(self): return self.user[USER_ROLES]
+ + def get_user(self): + return self.user +
def from_browser(): # Enable Basic Authentication for REST tools.

On 2014年10月22日 02:43, Aline Manera wrote:
On 10/20/2014 11:52 AM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add LDAP authentication, also deals with invalid user, LDAP search base configure error and other LDAP errors.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- contrib/DEBIAN/control.in | 1 + contrib/kimchi.spec.fedora.in | 1 + contrib/kimchi.spec.suse.in | 1 + src/kimchi/auth.py | 44 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 46 insertions(+), 1 deletion(-)
diff --git a/contrib/DEBIAN/control.in b/contrib/DEBIAN/control.in index 7372a58..0721960 100644 --- a/contrib/DEBIAN/control.in +++ b/contrib/DEBIAN/control.in @@ -27,6 +27,7 @@ Depends: python-cherrypy3 (>= 3.2.0), firewalld, nginx, python-guestfs, + python-ldap, libguestfs-tools Build-Depends: libxslt, python-libxml2, diff --git a/contrib/kimchi.spec.fedora.in b/contrib/kimchi.spec.fedora.in index 2ca3076..fcb8c11 100644 --- a/contrib/kimchi.spec.fedora.in +++ b/contrib/kimchi.spec.fedora.in @@ -29,6 +29,7 @@ Requires: nfs-utils Requires: nginx Requires: iscsi-initiator-utils Requires: policycoreutils-python +Requires: python-ldap Requires: python-libguestfs Requires: libguestfs-tools BuildRequires: libxslt diff --git a/contrib/kimchi.spec.suse.in b/contrib/kimchi.spec.suse.in index 9ea240c..b8f0531 100644 --- a/contrib/kimchi.spec.suse.in +++ b/contrib/kimchi.spec.suse.in @@ -23,6 +23,7 @@ Requires: python-psutil >= 0.6.0 Requires: python-jsonschema >= 1.3.0 Requires: python-ethtool Requires: python-ipaddr +Requires: python-ldap Requires: python-lxml Requires: python-xml Requires: nfs-client diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index 10c7c1f..162bbfd 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -20,6 +20,7 @@ import base64 import cherrypy import fcntl +import ldap import multiprocessing import os import PAM @@ -177,6 +178,7 @@ class PAMUser(User):
class LDAPUser(User): auth_type = "ldap" + def __init__(self, username): self.user = {} self.user[USER_NAME] = username @@ -187,7 +189,47 @@ class LDAPUser(User):
@staticmethod def authenticate(username, password): - return False + ldap_server = config.get("authentication", "ldap_server").strip('"') + ldap_search_base = config.get( + "authentication", "ldap_search_base").strip('"') + ldap_search_filter = config.get( + "authentication", "ldap_search_filter", + vars={"username": username.encode("utf-8")}).strip('"') + + connect = ldap.open(ldap_server) + try: + try: + result = connect.search_s( + ldap_search_base, ldap.SCOPE_SUBTREE, ldap_search_filter) + if len(result) == 0: + entity = ldap_search_filter % {'username': username} + raise ldap.LDAPError("Invalid ldap entity:%s" % entity) + except ldap.NO_SUCH_OBJECT: + # ldap search base specified wrongly. + raise ldap.LDAPError( + "invalid ldap search base %s" % ldap_search_base) + + try: + connect.bind_s(result[0][0], password) + except ldap.INVALID_CREDENTIALS: + # invalid user password + raise ldap.LDAPError("invalid user/passwd") + connect.unbind_s() + return True + except ldap.LDAPError, e: + arg = {"username": username, "code": e.message} + raise OperationFailed("KCHAUTH0001E", arg) + + def get_groups(self): + return self.user[USER_GROUPS] +
+ def get_roles(self): + self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin') + return self.user[USER_ROLES]
The admin ID's should be listed on Kimchi config file, instead of doing admin permissions to all users.
So on __init__():
self.admin_users = config.get("authentication", "ldap_admin_users") self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin') if self.user[USERNAME] in self.admin_users else dict.fromkeys(tabs, 'user')
And on get_roles():
def get_roles(self): return self.user[USER_ROLES]
Aline, this patch just want to cover Authentication-- whether we let a person in. I will add authorization(what this person is allow to manipulate) after we settled down our opinion on how to implement it.
+ + def get_user(self): + return self.user +
def from_browser(): # Enable Basic Authentication for REST tools.
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

On 10/22/2014 04:04 AM, Royce Lv wrote:
On 2014年10月22日 02:43, Aline Manera wrote:
On 10/20/2014 11:52 AM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add LDAP authentication, also deals with invalid user, LDAP search base configure error and other LDAP errors.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- contrib/DEBIAN/control.in | 1 + contrib/kimchi.spec.fedora.in | 1 + contrib/kimchi.spec.suse.in | 1 + src/kimchi/auth.py | 44 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 46 insertions(+), 1 deletion(-)
diff --git a/contrib/DEBIAN/control.in b/contrib/DEBIAN/control.in index 7372a58..0721960 100644 --- a/contrib/DEBIAN/control.in +++ b/contrib/DEBIAN/control.in @@ -27,6 +27,7 @@ Depends: python-cherrypy3 (>= 3.2.0), firewalld, nginx, python-guestfs, + python-ldap, libguestfs-tools Build-Depends: libxslt, python-libxml2, diff --git a/contrib/kimchi.spec.fedora.in b/contrib/kimchi.spec.fedora.in index 2ca3076..fcb8c11 100644 --- a/contrib/kimchi.spec.fedora.in +++ b/contrib/kimchi.spec.fedora.in @@ -29,6 +29,7 @@ Requires: nfs-utils Requires: nginx Requires: iscsi-initiator-utils Requires: policycoreutils-python +Requires: python-ldap Requires: python-libguestfs Requires: libguestfs-tools BuildRequires: libxslt diff --git a/contrib/kimchi.spec.suse.in b/contrib/kimchi.spec.suse.in index 9ea240c..b8f0531 100644 --- a/contrib/kimchi.spec.suse.in +++ b/contrib/kimchi.spec.suse.in @@ -23,6 +23,7 @@ Requires: python-psutil >= 0.6.0 Requires: python-jsonschema >= 1.3.0 Requires: python-ethtool Requires: python-ipaddr +Requires: python-ldap Requires: python-lxml Requires: python-xml Requires: nfs-client diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index 10c7c1f..162bbfd 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -20,6 +20,7 @@ import base64 import cherrypy import fcntl +import ldap import multiprocessing import os import PAM @@ -177,6 +178,7 @@ class PAMUser(User):
class LDAPUser(User): auth_type = "ldap" + def __init__(self, username): self.user = {} self.user[USER_NAME] = username @@ -187,7 +189,47 @@ class LDAPUser(User):
@staticmethod def authenticate(username, password): - return False + ldap_server = config.get("authentication", "ldap_server").strip('"') + ldap_search_base = config.get( + "authentication", "ldap_search_base").strip('"') + ldap_search_filter = config.get( + "authentication", "ldap_search_filter", + vars={"username": username.encode("utf-8")}).strip('"') + + connect = ldap.open(ldap_server) + try: + try: + result = connect.search_s( + ldap_search_base, ldap.SCOPE_SUBTREE, ldap_search_filter) + if len(result) == 0: + entity = ldap_search_filter % {'username': username} + raise ldap.LDAPError("Invalid ldap entity:%s" % entity) + except ldap.NO_SUCH_OBJECT: + # ldap search base specified wrongly. + raise ldap.LDAPError( + "invalid ldap search base %s" % ldap_search_base) + + try: + connect.bind_s(result[0][0], password) + except ldap.INVALID_CREDENTIALS: + # invalid user password + raise ldap.LDAPError("invalid user/passwd") + connect.unbind_s() + return True + except ldap.LDAPError, e: + arg = {"username": username, "code": e.message} + raise OperationFailed("KCHAUTH0001E", arg) + + def get_groups(self): + return self.user[USER_GROUPS] +
+ def get_roles(self): + self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin') + return self.user[USER_ROLES]
The admin ID's should be listed on Kimchi config file, instead of doing admin permissions to all users.
So on __init__():
self.admin_users = config.get("authentication", "ldap_admin_users") self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin') if self.user[USERNAME] in self.admin_users else dict.fromkeys(tabs, 'user')
And on get_roles():
def get_roles(self): return self.user[USER_ROLES]
Aline, this patch just want to cover Authentication-- whether we let a person in. I will add authorization(what this person is allow to manipulate) after we settled down our opinion on how to implement it.
Well, we already had an agreement on how get the admin users, right? We will use Kimchi config file to handle that information and all the other users will have "user" role.
+ + def get_user(self): + return self.user +
def from_browser(): # Enable Basic Authentication for REST tools.
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

On 10/20/2014 11:52 AM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Add LDAP authentication, also deals with invalid user, LDAP search base configure error and other LDAP errors.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- contrib/DEBIAN/control.in | 1 + contrib/kimchi.spec.fedora.in | 1 + contrib/kimchi.spec.suse.in | 1 + src/kimchi/auth.py | 44 ++++++++++++++++++++++++++++++++++++++++++-
You also need to update README to add this new dependency.
4 files changed, 46 insertions(+), 1 deletion(-)
diff --git a/contrib/DEBIAN/control.in b/contrib/DEBIAN/control.in index 7372a58..0721960 100644 --- a/contrib/DEBIAN/control.in +++ b/contrib/DEBIAN/control.in @@ -27,6 +27,7 @@ Depends: python-cherrypy3 (>= 3.2.0), firewalld, nginx, python-guestfs, + python-ldap, libguestfs-tools Build-Depends: libxslt, python-libxml2, diff --git a/contrib/kimchi.spec.fedora.in b/contrib/kimchi.spec.fedora.in index 2ca3076..fcb8c11 100644 --- a/contrib/kimchi.spec.fedora.in +++ b/contrib/kimchi.spec.fedora.in @@ -29,6 +29,7 @@ Requires: nfs-utils Requires: nginx Requires: iscsi-initiator-utils Requires: policycoreutils-python +Requires: python-ldap Requires: python-libguestfs Requires: libguestfs-tools BuildRequires: libxslt diff --git a/contrib/kimchi.spec.suse.in b/contrib/kimchi.spec.suse.in index 9ea240c..b8f0531 100644 --- a/contrib/kimchi.spec.suse.in +++ b/contrib/kimchi.spec.suse.in @@ -23,6 +23,7 @@ Requires: python-psutil >= 0.6.0 Requires: python-jsonschema >= 1.3.0 Requires: python-ethtool Requires: python-ipaddr +Requires: python-ldap Requires: python-lxml Requires: python-xml Requires: nfs-client diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py index 10c7c1f..162bbfd 100644 --- a/src/kimchi/auth.py +++ b/src/kimchi/auth.py @@ -20,6 +20,7 @@ import base64 import cherrypy import fcntl +import ldap import multiprocessing import os import PAM @@ -177,6 +178,7 @@ class PAMUser(User):
class LDAPUser(User): auth_type = "ldap" + def __init__(self, username): self.user = {} self.user[USER_NAME] = username @@ -187,7 +189,47 @@ class LDAPUser(User):
@staticmethod def authenticate(username, password): - return False + ldap_server = config.get("authentication", "ldap_server").strip('"') + ldap_search_base = config.get( + "authentication", "ldap_search_base").strip('"') + ldap_search_filter = config.get( + "authentication", "ldap_search_filter", + vars={"username": username.encode("utf-8")}).strip('"') + + connect = ldap.open(ldap_server) + try: + try: + result = connect.search_s( + ldap_search_base, ldap.SCOPE_SUBTREE, ldap_search_filter) + if len(result) == 0: + entity = ldap_search_filter % {'username': username} + raise ldap.LDAPError("Invalid ldap entity:%s" % entity) + except ldap.NO_SUCH_OBJECT: + # ldap search base specified wrongly. + raise ldap.LDAPError( + "invalid ldap search base %s" % ldap_search_base) + + try: + connect.bind_s(result[0][0], password) + except ldap.INVALID_CREDENTIALS: + # invalid user password + raise ldap.LDAPError("invalid user/passwd") + connect.unbind_s() + return True + except ldap.LDAPError, e: + arg = {"username": username, "code": e.message} + raise OperationFailed("KCHAUTH0001E", arg) + + def get_groups(self): + return self.user[USER_GROUPS] + + def get_roles(self): + self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin') + return self.user[USER_ROLES] + + def get_user(self): + return self.user +
def from_browser(): # Enable Basic Authentication for REST tools.

From: Royce Lv <lvroyce@linux.vnet.ibm.com> Authentication function moved to class, so build a fake user class to cover test. Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- tests/utils.py | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/tests/utils.py b/tests/utils.py index 140bb1d..4215bce 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -34,7 +34,8 @@ from lxml import etree import kimchi.mockmodel import kimchi.server -from kimchi.config import paths +from kimchi.config import config, paths +from kimchi.auth import User, USER_NAME, USER_GROUPS, USER_ROLES, tabs from kimchi.exception import OperationFailed _ports = {} @@ -167,29 +168,43 @@ def get_remote_iso_path(): return remote_path -def patch_auth(sudo=True): - """ - Override the authenticate function with a simple test against an - internal dict of users and passwords. - """ +class FakeUser(User): + auth_type = "fake" + sudo = True + + def __init__(self, username): + self.user = {} + self.user[USER_NAME] = username + self.user[USER_GROUPS] = None + self.user[USER_ROLES] = dict.fromkeys(tabs, 'user') - def _get_groups(self): + def get_groups(self): return ['groupA', 'groupB', 'wheel'] - def _has_sudo(self, result): - result.value = sudo + def get_roles(self): + if self.sudo: + self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin') + return self.user[USER_ROLES] + + def get_user(self): + return self.user - def _authenticate(username, password, service="passwd"): + @staticmethod + def authenticate(username, password, service="passwd"): try: return kimchi.mockmodel.fake_user[username] == password except KeyError, e: raise OperationFailed("KCHAUTH0001E", {'username': 'username', 'code': e.message}) - import kimchi.auth - kimchi.auth.authenticate = _authenticate - kimchi.auth.User.get_groups = _get_groups - kimchi.auth.User._has_sudo = _has_sudo + +def patch_auth(sudo=True): + """ + Override the authenticate function with a simple test against an + internal dict of users and passwords. + """ + config.set("authentication", "method", "fake") + FakeUser.sudo = sudo def normalize_xml(xml_str): -- 1.8.3.2

Reviewed-by: Aline Manera <alinefm@linux.vnet.ibm.com> On 10/20/2014 11:52 AM, lvroyce0210@gmail.com wrote:
From: Royce Lv <lvroyce@linux.vnet.ibm.com>
Authentication function moved to class, so build a fake user class to cover test.
Signed-off-by: Royce Lv <lvroyce@linux.vnet.ibm.com> --- tests/utils.py | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-)
diff --git a/tests/utils.py b/tests/utils.py index 140bb1d..4215bce 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -34,7 +34,8 @@ from lxml import etree
import kimchi.mockmodel import kimchi.server -from kimchi.config import paths +from kimchi.config import config, paths +from kimchi.auth import User, USER_NAME, USER_GROUPS, USER_ROLES, tabs from kimchi.exception import OperationFailed
_ports = {} @@ -167,29 +168,43 @@ def get_remote_iso_path(): return remote_path
-def patch_auth(sudo=True): - """ - Override the authenticate function with a simple test against an - internal dict of users and passwords. - """ +class FakeUser(User): + auth_type = "fake" + sudo = True + + def __init__(self, username): + self.user = {} + self.user[USER_NAME] = username + self.user[USER_GROUPS] = None + self.user[USER_ROLES] = dict.fromkeys(tabs, 'user')
- def _get_groups(self): + def get_groups(self): return ['groupA', 'groupB', 'wheel']
- def _has_sudo(self, result): - result.value = sudo + def get_roles(self): + if self.sudo: + self.user[USER_ROLES] = dict.fromkeys(tabs, 'admin') + return self.user[USER_ROLES] + + def get_user(self): + return self.user
- def _authenticate(username, password, service="passwd"): + @staticmethod + def authenticate(username, password, service="passwd"): try: return kimchi.mockmodel.fake_user[username] == password except KeyError, e: raise OperationFailed("KCHAUTH0001E", {'username': 'username', 'code': e.message})
- import kimchi.auth - kimchi.auth.authenticate = _authenticate - kimchi.auth.User.get_groups = _get_groups - kimchi.auth.User._has_sudo = _has_sudo + +def patch_auth(sudo=True): + """ + Override the authenticate function with a simple test against an + internal dict of users and passwords. + """ + config.set("authentication", "method", "fake") + FakeUser.sudo = sudo
def normalize_xml(xml_str):
participants (3)
-
Aline Manera
-
lvroyce0210@gmail.com
-
Royce Lv