leverage LFID as Authentication 53/40653/7
authorSerenaFeng <feng.xiaowei@zte.com.cn>
Wed, 30 Aug 2017 03:59:46 +0000 (11:59 +0800)
committerSerenaFeng <feng.xiaowei@zte.com.cn>
Thu, 31 Aug 2017 02:43:14 +0000 (10:43 +0800)
delete openid authentication
add LFID authentication

Change-Id: Iead144b5130bce51448024e65092fdea3bb2f07a
Signed-off-by: SerenaFeng <feng.xiaowei@zte.com.cn>
14 files changed:
utils/test/testapi/.gitignore
utils/test/testapi/3rd_party/static/testapi-ui/components/profile/profile.html
utils/test/testapi/3rd_party/static/testapi-ui/components/profile/profileController.js
utils/test/testapi/etc/config.ini
utils/test/testapi/opnfv_testapi/common/check.py
utils/test/testapi/opnfv_testapi/common/constants.py [new file with mode: 0644]
utils/test/testapi/opnfv_testapi/resources/result_handlers.py
utils/test/testapi/opnfv_testapi/router/url_mappings.py
utils/test/testapi/opnfv_testapi/ui/auth/base.py [deleted file]
utils/test/testapi/opnfv_testapi/ui/auth/constants.py [deleted file]
utils/test/testapi/opnfv_testapi/ui/auth/sign.py
utils/test/testapi/opnfv_testapi/ui/auth/user.py
utils/test/testapi/opnfv_testapi/ui/root.py
utils/test/testapi/requirements.txt

index 00f8a03..86ec0d2 100644 (file)
@@ -4,4 +4,4 @@ setup.cfg-e
 opnfv_testapi/static
 build
 *.egg-info
-
+3rd_party/static/static
index dc97c41..763f5d1 100644 (file)
@@ -3,9 +3,16 @@
 <div>
     <table class="table table-striped table-hover">
         <tbody>
-            <tr> <td>User name</td> <td>{{auth.currentUser.fullname}}</td> </tr>
-            <tr> <td>User OpenId</td> <td>{{auth.currentUser.openid}}</td> </tr>
+            <tr> <td>User</td> <td>{{auth.currentUser.user}}</td> </tr>
+            <tr> <td>Fullname</td> <td>{{auth.currentUser.fullname}}</td> </tr>
             <tr> <td>Email</td> <td>{{auth.currentUser.email}}</td> </tr>
+            <tr> <td>Groups</td>
+                 <td>
+                     <div ng-repeat="group in auth.currentUser.groups">
+                         {{group}}</br>
+                     </div>
+                 </td>
+            </tr>
         </tbody>
     </table>
 </div>
index 0660e19..5dbdf7b 100644 (file)
@@ -26,7 +26,7 @@
      * This is a provider for the user's uploaded public keys.
      */
     function PubKeys($resource, testapiApiUrl) {
-        return $resource(testapiApiUrl + '/profile/pubkeys/:id', null, null);
+        return $resource(testapiApiUrl + '/user/pubkeys/:id', null, null);
     }
 
     angular
index 1ec899f..db0e191 100644 (file)
@@ -21,48 +21,6 @@ authenticate = False
 [ui]
 url = http://localhost:8000
 
-[osid]
-
-# OpenStackID Auth Server URI. (string value)
-openstack_openid_endpoint = https://openstackid.org/accounts/openid2
-
-# OpenStackID logout URI. (string value)
-openid_logout_endpoint = https://openstackid.org/accounts/user/logout
-
-# Interaction mode. Specifies whether Openstack Id IdP may interact
-# with the user to determine the outcome of the request. (string
-# value)
-openid_mode = checkid_setup
-
-# Protocol version. Value identifying the OpenID protocol version
-# being used. This value should be "http://specs.openid.net/auth/2.0".
-# (string value)
-openid_ns = http://specs.openid.net/auth/2.0
-
-# Return endpoint in Refstack's API. Value indicating the endpoint
-# where the user should be returned to after signing in. Openstack Id
-# Idp only supports HTTPS address types. (string value)
-openid_return_to = v1/auth/signin_return
-
-# Claimed identifier. This value must be set to
-# "http://specs.openid.net/auth/2.0/identifier_select". or to user
-# claimed identity (user local identifier or user owned identity [ex:
-# custom html hosted on a owned domain set to html discover]). (string
-# value)
-openid_claimed_id = http://specs.openid.net/auth/2.0/identifier_select
-
-# Alternate identifier. This value must be set to
-# http://specs.openid.net/auth/2.0/identifier_select. (string value)
-openid_identity = http://specs.openid.net/auth/2.0/identifier_select
-
-# Indicates request for user attribute information. This value must be
-# set to "http://openid.net/extensions/sreg/1.1". (string value)
-openid_ns_sreg = http://openid.net/extensions/sreg/1.1
-
-# Comma-separated list of field names which, if absent from the
-# response, will prevent the Consumer from completing the registration
-# without End User interation. The field names are those that are
-# specified in the Response Format, with the "openid.sreg." prefix
-# removed. Valid values include: "country", "email", "firstname",
-# "language", "lastname" (string value)
-openid_sreg_required = email,fullname
+[lfid]
+# Linux Foundation cas URL
+cas_url = https://identity.linuxfoundation.org/cas/
index 24ba876..009d3d4 100644 (file)
@@ -8,14 +8,49 @@
 ##############################################################################
 import functools
 
+import cas
 from tornado import gen
 from tornado import web
 
+from opnfv_testapi.common import constants
 from opnfv_testapi.common import message
 from opnfv_testapi.common import raises
+from opnfv_testapi.common.config import CONF
 from opnfv_testapi.db import api as dbapi
 
 
+def login(method):
+    @web.asynchronous
+    @gen.coroutine
+    @functools.wraps(method)
+    def wrapper(self, *args, **kwargs):
+        ticket = self.get_query_argument('ticket', default=None)
+        if ticket:
+            client = cas.CASClient(version='2',
+                                   server_url=CONF.lfid_cas_url,
+                                   service_url=CONF.ui_url)
+            (user, attrs, _) = client.verify_ticket(ticket=ticket)
+            print 'login user: {}'.format(user)
+            login_user = {
+                'user': user,
+                'email': attrs.get('mail'),
+                'fullname': attrs.get('field_lf_full_name'),
+                'groups': constants.TESTAPI_USERS + attrs.get('group', [])
+            }
+            q_user = {'user': user}
+            db_user = yield dbapi.db_find_one(constants.USER_TABLE, q_user)
+            if not db_user:
+                dbapi.db_save(constants.USER_TABLE, login_user)
+            else:
+                dbapi.db_update(constants.USER_TABLE, q_user, login_user)
+
+            self.clear_cookie(constants.TESTAPI_ID)
+            self.set_secure_cookie(constants.TESTAPI_ID, user)
+        ret = yield gen.coroutine(method)(self, *args, **kwargs)
+        raise gen.Return(ret)
+    return wrapper
+
+
 def authenticate(method):
     @web.asynchronous
     @gen.coroutine
diff --git a/utils/test/testapi/opnfv_testapi/common/constants.py b/utils/test/testapi/opnfv_testapi/common/constants.py
new file mode 100644 (file)
index 0000000..b37ebb3
--- /dev/null
@@ -0,0 +1,5 @@
+TESTAPI_ID = 'testapi_id'
+CSRF_TOKEN = 'csrf_token'
+ROLE = 'role'
+TESTAPI_USERS = ['opnfv-testapi-users']
+USER_TABLE = 'users'
index 9389d26..e202f5c 100644 (file)
@@ -6,20 +6,20 @@
 # which accompanies this distribution, and is available at
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
-import logging
-from datetime import datetime
-from datetime import timedelta
 import json
+import logging
 
 from bson import objectid
+from datetime import datetime
+from datetime import timedelta
 
-from opnfv_testapi.common.config import CONF
+from opnfv_testapi.common import constants
 from opnfv_testapi.common import message
 from opnfv_testapi.common import raises
+from opnfv_testapi.common.config import CONF
 from opnfv_testapi.resources import handlers
 from opnfv_testapi.resources import result_models
 from opnfv_testapi.tornado_swagger import swagger
-from opnfv_testapi.ui.auth import constants as auth_const
 
 
 class GenericResultHandler(handlers.GenericApiHandler):
@@ -59,13 +59,12 @@ class GenericResultHandler(handlers.GenericApiHandler):
             elif k == 'to':
                 date_range.update({'$lt': str(v)})
             elif k == 'signed':
-                openid = self.get_secure_cookie(auth_const.OPENID)
-                role = self.get_secure_cookie(auth_const.ROLE)
-                logging.info('role:%s', role)
+                username = self.get_secure_cookie(constants.TESTAPI_ID)
+                role = self.get_secure_cookie(constants.ROLE)
                 if role:
                     del query['public']
                     if role != "reviewer":
-                        query['user'] = openid
+                        query['user'] = username
             elif k not in ['last', 'page', 'descend']:
                 query[k] = v
             if date_range:
@@ -246,7 +245,7 @@ class ResultsUploadHandler(ResultsCLHandler):
         self.json_args = json.loads(fileinfo['body']).copy()
         self.json_args['public'] = is_public
 
-        openid = self.get_secure_cookie(auth_const.OPENID)
+        openid = self.get_secure_cookie(constants.TESTAPI_ID)
         if openid:
             self.json_args['user'] = openid
 
index 3e3ab87..be6240e 100644 (file)
@@ -76,8 +76,7 @@ mappings = [
 
     (r'/', root.RootHandler),
     (r'/api/v1/auth/signin', sign.SigninHandler),
-    (r'/api/v1/auth/signin_return', sign.SigninReturnHandler),
     (r'/api/v1/auth/signout', sign.SignoutHandler),
-    (r'/api/v1/profile', user.ProfileHandler),
+    (r'/api/v1/profile', user.UserHandler),
 
 ]
diff --git a/utils/test/testapi/opnfv_testapi/ui/auth/base.py b/utils/test/testapi/opnfv_testapi/ui/auth/base.py
deleted file mode 100644 (file)
index bea87c4..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-import random
-import string
-
-from six.moves.urllib import parse
-
-from opnfv_testapi.resources import handlers
-
-
-class BaseHandler(handlers.GenericApiHandler):
-    def __init__(self, application, request, **kwargs):
-        super(BaseHandler, self).__init__(application, request, **kwargs)
-        self.table = 'users'
-
-    def set_cookies(self, cookies):
-        for cookie_n, cookie_v in cookies:
-            self.set_secure_cookie(cookie_n, cookie_v)
-
-
-def get_token(length=30):
-    """Get random token."""
-    return ''.join(random.choice(string.ascii_lowercase)
-                   for i in range(length))
-
-
-def set_query_params(url, params):
-    """Set params in given query."""
-    url_parts = parse.urlparse(url)
-    url = parse.urlunparse((
-        url_parts.scheme,
-        url_parts.netloc,
-        url_parts.path,
-        url_parts.params,
-        parse.urlencode(params),
-        url_parts.fragment))
-    return url
diff --git a/utils/test/testapi/opnfv_testapi/ui/auth/constants.py b/utils/test/testapi/opnfv_testapi/ui/auth/constants.py
deleted file mode 100644 (file)
index 44ccb46..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-OPENID = 'openid'
-ROLE = 'role'
-DEFAULT_ROLE = 'user'
-
-# OpenID parameters
-OPENID_MODE = 'openid.mode'
-OPENID_NS = 'openid.ns'
-OPENID_RETURN_TO = 'openid.return_to'
-OPENID_CLAIMED_ID = 'openid.claimed_id'
-OPENID_IDENTITY = 'openid.identity'
-OPENID_REALM = 'openid.realm'
-OPENID_NS_SREG = 'openid.ns.sreg'
-OPENID_NS_SREG_REQUIRED = 'openid.sreg.required'
-OPENID_NS_SREG_EMAIL = 'openid.sreg.email'
-OPENID_NS_SREG_FULLNAME = 'openid.sreg.fullname'
-OPENID_ERROR = 'openid.error'
-
-CSRF_TOKEN = 'csrf_token'
index 4623952..01cd0f7 100644 (file)
@@ -1,76 +1,22 @@
-from six.moves.urllib import parse
-from tornado import gen
-from tornado import web
+from cas import CASClient
 
+from opnfv_testapi.common import constants
 from opnfv_testapi.common.config import CONF
-from opnfv_testapi.db import api as dbapi
-from opnfv_testapi.ui.auth import base
-from opnfv_testapi.ui.auth import constants as const
+from opnfv_testapi.resources import handlers
 
 
-class SigninHandler(base.BaseHandler):
+class SigninHandler(handlers.GenericApiHandler):
     def get(self):
-        csrf_token = base.get_token()
-        return_endpoint = parse.urljoin(CONF.api_url,
-                                        CONF.osid_openid_return_to)
-        return_to = base.set_query_params(return_endpoint,
-                                          {const.CSRF_TOKEN: csrf_token})
+        client = CASClient(version='2',
+                           server_url=CONF.lfid_cas_url,
+                           service_url=CONF.ui_url)
+        self.redirect(url=(client.get_login_url()))
 
-        params = {
-            const.OPENID_MODE: CONF.osid_openid_mode,
-            const.OPENID_NS: CONF.osid_openid_ns,
-            const.OPENID_RETURN_TO: return_to,
-            const.OPENID_CLAIMED_ID: CONF.osid_openid_claimed_id,
-            const.OPENID_IDENTITY: CONF.osid_openid_identity,
-            const.OPENID_REALM: CONF.api_url,
-            const.OPENID_NS_SREG: CONF.osid_openid_ns_sreg,
-            const.OPENID_NS_SREG_REQUIRED: CONF.osid_openid_sreg_required,
-        }
-        url = CONF.osid_openstack_openid_endpoint
-        url = base.set_query_params(url, params)
-        self.redirect(url=url, permanent=False)
 
-
-class SigninReturnHandler(base.BaseHandler):
-    @web.asynchronous
-    @gen.coroutine
-    def get(self):
-        if self.get_query_argument(const.OPENID_MODE) == 'cancel':
-            self._auth_failure('Authentication canceled.')
-
-        openid = self.get_query_argument(const.OPENID_CLAIMED_ID)
-        role = const.DEFAULT_ROLE
-        new_user_info = {
-            'openid': openid,
-            'email': self.get_query_argument(const.OPENID_NS_SREG_EMAIL),
-            'fullname': self.get_query_argument(const.OPENID_NS_SREG_FULLNAME),
-            const.ROLE: role
-        }
-        user = yield dbapi.db_find_one(self.table, {'openid': openid})
-        if not user:
-            dbapi.db_save(self.table, new_user_info)
-        else:
-            role = user.get(const.ROLE)
-
-        self.clear_cookie(const.OPENID)
-        self.clear_cookie(const.ROLE)
-        self.set_secure_cookie(const.OPENID, openid)
-        self.set_secure_cookie(const.ROLE, role)
-        self.redirect(url=CONF.ui_url)
-
-    def _auth_failure(self, message):
-        params = {'message': message}
-        url = parse.urljoin(CONF.ui_url,
-                            '/#/auth_failure?' + parse.urlencode(params))
-        self.redirect(url)
-
-
-class SignoutHandler(base.BaseHandler):
+class SignoutHandler(handlers.GenericApiHandler):
     def get(self):
         """Handle signout request."""
-        self.clear_cookie(const.OPENID)
-        self.clear_cookie(const.ROLE)
-        params = {'openid_logout': CONF.osid_openid_logout_endpoint}
-        url = parse.urljoin(CONF.ui_url,
-                            '/#/logout?' + parse.urlencode(params))
-        self.redirect(url)
+        self.clear_cookie(constants.TESTAPI_ID)
+        client = CASClient(version='2',
+                           server_url=CONF.lfid_cas_url)
+        self.redirect(url=(client.get_logout_url(redirect_url=CONF.ui_url)))
index 955cdee..ab86007 100644 (file)
@@ -1,25 +1,26 @@
-from tornado import gen
-from tornado import web
-
+from opnfv_testapi.common import constants
 from opnfv_testapi.common import raises
-from opnfv_testapi.db import api as dbapi
-from opnfv_testapi.ui.auth import base
+from opnfv_testapi.resources import handlers
+from opnfv_testapi.resources import models
+
+
+class User(models.ModelBase):
+    def __init__(self, user=None, email=None, fullname=None, groups=None):
+        self.user = user
+        self.email = email
+        self.fullname = fullname
+        self.groups = groups
+
 
+class UserHandler(handlers.GenericApiHandler):
+    def __init__(self, application, request, **kwargs):
+        super(UserHandler, self).__init__(application, request, **kwargs)
+        self.table = 'users'
+        self.table_cls = User
 
-class ProfileHandler(base.BaseHandler):
-    @web.asynchronous
-    @gen.coroutine
     def get(self):
-        openid = self.get_secure_cookie('openid')
-        if openid:
-            try:
-                user = yield dbapi.db_find_one(self.table, {'openid': openid})
-                self.finish_request({
-                    "openid": user.get('openid'),
-                    "email": user.get('email'),
-                    "fullname": user.get('fullname'),
-                    "role": user.get('role', 'user')
-                })
-            except Exception:
-                pass
-        raises.Unauthorized('Unauthorized')
+        username = self.get_secure_cookie(constants.TESTAPI_ID)
+        if username:
+            self._get_one(query={'user': username})
+        else:
+            raises.Unauthorized('Unauthorized')
index 5b2c922..069ad5e 100644 (file)
@@ -1,10 +1,12 @@
-from opnfv_testapi.resources.handlers import GenericApiHandler
+from opnfv_testapi.common import check
 from opnfv_testapi.common.config import CONF
+from opnfv_testapi.resources import handlers
 
 
-class RootHandler(GenericApiHandler):
+class RootHandler(handlers.GenericApiHandler):
     def get_template_path(self):
         return CONF.static_path
 
+    @check.login
     def get(self):
         self.render('testapi-ui/index.html')
index 4b6f75c..fbd2e0e 100644 (file)
@@ -8,3 +8,4 @@ tornado>=3.1,<=4.3  # Apache-2.0
 epydoc>=0.3.1
 six>=1.9.0  # MIT
 motor  # Apache-2.0
+python-cas