diff options
| author | Khaled Hussein <khaled.hussein@gmail.com> | 2011-04-18 15:09:18 +0000 |
|---|---|---|
| committer | Khaled Hussein <khaled.hussein@gmail.com> | 2011-04-18 15:09:18 +0000 |
| commit | 4ab5bd70652b56df52d26a348a0050fe043b986f (patch) | |
| tree | 8be0271d22451eb5123cd1a6643af4986236c6e9 | |
| parent | 06de880bce60604055a42d23dd668b42715afca2 (diff) | |
| parent | 8f8b43697a1baff132d4f42165ba0682da17c4fb (diff) | |
Merge branch 'DevTeam' of github.com:khussein/keystone into DevTeam
| -rw-r--r-- | .project | 17 | ||||
| -rw-r--r-- | .pydevproject | 7 | ||||
| -rw-r--r-- | README | 31 | ||||
| -rw-r--r-- | db/keystone.db | bin | 4096 -> 5120 bytes | |||
| -rw-r--r-- | echo/echo.py | 40 | ||||
| -rw-r--r-- | keystone/identity.py | 518 |
6 files changed, 561 insertions, 52 deletions
diff --git a/.project b/.project new file mode 100644 index 00000000..c5873b16 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>keystone-git</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.python.pydev.PyDevBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.python.pydev.pythonNature</nature> + </natures> +</projectDescription> diff --git a/.pydevproject b/.pydevproject new file mode 100644 index 00000000..a9cca037 --- /dev/null +++ b/.pydevproject @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<?eclipse-pydev version="1.0"?> + +<pydev_project> +<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property> +<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property> +</pydev_project> @@ -10,13 +10,19 @@ sudo easy_install PasteDeploy sudo easy_install simplejson sudo easy_install -U bottle sudo easy_install eventlet +sudo easy_install urllib2 Tables: -CREATE TABLE tenants(tenant_id INTEGER, tenant_desc varchar(255), tenant_enabled INTEGER, PRIMARY KEY(tenant_id ASC)); +CREATE TABLE tenants(tenant_id varchar(255), tenant_desc varchar(255), tenant_enabled INTEGER, PRIMARY KEY(tenant_id ASC)); CREATE TABLE token (token_id varchar(255),expires datetime); +CREATE TABLE groups (group_id varchar(255),tenant_id varchar(255),group_desc varchar(255)); + + + + Issues: bottle not in path on Mac OS X (added exception handling to default path) @@ -29,6 +35,7 @@ curl -i -X POST -H "Content-Type: application/json" -d '{"tenant": { "id": "1234 Get token: curl -i -X POST -H "Content-Type: application/json" -d '{"username": "john", "password": "secret" }' http://localhost:8080/tokens +curl -i -X POST -H "Content-Type: application/json" -d '{"username":"jack", "password": "secret","tenant_id":"1" }'http://localhost:8080/tokens Add User:(Test Function) curl -i -X POST -H "Content-Type: application/json" -d '{"username": "john", "password": "secret" }' http://localhost:8080/users @@ -40,6 +47,28 @@ curl -i -X GET -H "Content-Type: application/json" http://localhost:8080/token/ Validate bad token: curl -i -X GET -H "Content-Type: application/json" http://localhost:8080/token/abcdefghijklmnopqrstuvbad + +Update Tenant + curl -i -X PUT -H "Content-Type: application/json" -d '{"tenant": { "description": "A new description update ..." } }' http://localhost:8080/tenants/1234 + +Get Tenant + curl -i -X GET -H "Content-Type: application/json" -d '{}' http://localhost:8080/tenants/1234 + +Get Tenants + curl -i -X GET -H "Content-Type: application/json" -d '{}' http://localhost:8080/tenants + + +Delete Tenant + curl -i -X DELETE -H "Content-Type: application/json" -d '{}' http://localhost:8080/tenants/1234 + +Create a user: + curl -i -X POST -H "Content-Type: application/json" -d '{"user": {"username": "jacky", "password": "secret","email":"jacky@example.com","enabled": true } }' http://localhost:8080/tenants/111/users + + + + + + Echo: Start server: python echo.py diff --git a/db/keystone.db b/db/keystone.db Binary files differindex e649de46..403b97c9 100644 --- a/db/keystone.db +++ b/db/keystone.db diff --git a/echo/echo.py b/echo/echo.py index e59deab7..5cb4b2b7 100644 --- a/echo/echo.py +++ b/echo/echo.py @@ -21,7 +21,9 @@ try: except ImportError: import json import eventlet -import urllib2 +import urllib +from httplib2 import Http + class EchoApp: @@ -42,17 +44,23 @@ class EchoApp: def toJSON(self): - + self.start('200 OK', [('Content-Type', 'application/json')]) token = str(self.envr.get("HTTP_X_AUTH_TOKEN","")) if token !='': - response=self.ValidateToken({'type':'json','token':token}) - r=json.loads('{"auth" : { "token": {"id": "fad94013a5b3b836dbc18", "expires": "2011-04-18 16:17:59"}}}') - + res=self.ValidateToken({'type':'json','token':token}) + + if int(res['response']['status'])==200 : + + yield str(res['content']) + else: + pass + # Need to Do Something Here + else: + + yield str(self.transform(self.dom)) - self.start('200 OK', [('Content-Type', 'application/json')]) - yield str(self.transform(self.dom)) @@ -80,18 +88,14 @@ class EchoApp: if params['token']: - url = "http://localhost:8080/token/"+str(params['token']) - #print url - data = '{"test":""}' - if params['type']=='json': - headers = { "Accept" : "application/json", "Content-Type": "application/json",'REQUEST_METHOD':'GET' } - elif type =='xml': - headers = { "Accept" : "application/xml", "Content-Type": "application/xml" } - - req = urllib2.Request(url, data, headers) - response = urllib2.urlopen(req) + + http=Http() - return response.read() + url = "http://localhost:8080/token/"+str(params['token']) + body = {} + headers = {"Accept" : "application/json", "Content-Type": "application/json"} + response, content = http.request(url, 'GET', headers=headers, body=urllib.urlencode(body)) + return {'response':response,'content':content} else: return abort(401, "No Token Found!") diff --git a/keystone/identity.py b/keystone/identity.py index 89ddeff6..ab8f1f6d 100644 --- a/keystone/identity.py +++ b/keystone/identity.py @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Not Yet PEP8 standardized + import os import hashlib try: @@ -85,8 +87,121 @@ class Tenants: return body return 'it did NOT work\n' - - + + @route ('/tenants/:tenantId', method='GET') + def get_tenant(tenantId): + ''' + Getting/Retrieving Tenants by doing a GET on /tenants + ''' + if 'CONTENT_TYPE' in request.environ: + content_types = ['text/plain', 'application/json', + 'application/xml', 'text/xml'] + content = request.environ['CONTENT_TYPE']; + if content in content_types: + if content == 'application/json': + body = json.loads(request.body.readline()) + #tenant_id = body['tenant']['id'] + #tenant_id = str(tenantId) + + dbpath = os.path.abspath( + os.path.join(os.path.dirname(__file__), + '../db/keystone.db')) + print dbpath + con = sqlite3.connect(dbpath) + cur = con.cursor() + cur.execute( + "SELECT * FROM tenants WHERE tenant_id='%s'" % + (str(tenantId))) + a=cur.fetchone() + if a: + enabled=a[2] + if enabled==0: + enabled="false" + if enabled==1: + enabled="true" + #con.commit() + con.close() + + elif content == 'application/xml': + #TODO: Implement XML support + return "whatever, we don't have XML yet" + + accept_header = request.header.get('Accept') + if accept_header in content_types: + if accept_header == 'application/json': + return '{"tenant" : { "id": "'+a[0]+'", "description": "'+a[1]+'", "enabled": '+str(enabled)+'}}' + elif accept_header == 'application/xml': + #TODO: Implement XML support + return "whatever, we don't have XML yet" + else: + # If there is no Accept header, the default is JSON. + #TODO: Make sure that the body is actually JSON. + #return body + return '{"tenant" : { "id": "'+a[0]+'", "description": "'+a[1]+'", "enabled": '+str(enabled)+'}}' + + return 'it did NOT work\n' + + @route ('/tenants', method='GET') + def get_tenants(): + ''' + Getting/Retrieving all Tenants by doing a GET on /tenants + ''' + if 'CONTENT_TYPE' in request.environ: + content_types = ['text/plain', 'application/json', + 'application/xml', 'text/xml'] + content = request.environ['CONTENT_TYPE']; + if content in content_types: + if content == 'application/json': + dbpath = os.path.abspath( + os.path.join(os.path.dirname(__file__), + '../db/keystone.db')) + con = sqlite3.connect(dbpath) + cur = con.cursor() + cur.execute( + "SELECT * FROM tenants" + ) + #a=cur.fetchone() + tenant_str="" + + for a in cur: + enabled=a[2] + if enabled==0: + enabled="false" + if enabled==1: + enabled="true" + tenant_str=tenant_str+"{\"id\": \""+a[0]+"\", \"description\": \""+a[1]+"\", \"enabled\": "+enabled+"}," + #if cur.fetchone(): + # tenant_str=tenant_str+"," + + #con.commit() + con.close() + + elif content == 'application/xml': + #TODO: Implement XML support + return "whatever, we don't have XML yet" + + accept_header = request.header.get('Accept') + if accept_header in content_types: + if accept_header == 'application/json': + ret_str="" + + ret_str=ret_str+"{\"tenants\": { \"values\": [ "+tenant_str+"]}}" + return ret_str + elif accept_header == 'application/xml': + #TODO: Implement XML support + return "whatever, we don't have XML yet" + else: + # If there is no Accept header, the default is JSON. + #TODO: Make sure that the body is actually JSON. + #return body + ret_str="" + + ret_str=ret_str+"{\"tenants\": { \"values\": [ "+tenant_str+"]}}" + return ret_str + + return 'it did NOT work\n' + + @route ('/tenants/:tenantId', method='PUT') def update_tenant(tenantId): ''' @@ -117,8 +232,8 @@ class Tenants: con = sqlite3.connect(dbpath) cur = con.cursor() cur.execute( - "UPDATE tenants SET tenant_desc='%s' WHERE tenant_id=%d" % - (tenant_desc, tenant_id)) + "UPDATE tenants SET tenant_desc='%s' WHERE tenant_id='%s'" % + (tenant_desc, tenantId)) con.commit() con.close() @@ -139,7 +254,54 @@ class Tenants: return body return 'it did NOT work\n' - + @route ('/tenants/:tenantId', method='DELETE') + def delete_tenant(tenantId): + ''' + Deleting Tenants by doing a Delete on /tenants + Request Body: + {"tenant": + { + "id": "1234" + } + } + ''' + if 'CONTENT_TYPE' in request.environ: + content_types = ['text/plain', 'application/json', + 'application/xml', 'text/xml'] + content = request.environ['CONTENT_TYPE']; + if content in content_types: + if content == 'application/json': + body = json.loads(request.body.readline()) + + dbpath = os.path.abspath( + os.path.join(os.path.dirname(__file__), + '../db/keystone.db')) + con = sqlite3.connect(dbpath) + cur = con.cursor() + cur.execute( + "DELETE FROM tenants WHERE tenant_id='%s'" % + (tenantId)) + con.commit() + con.close() + + elif content == 'application/xml': + #TODO: Implement XML support + return "whatever, we don't have XML yet" + + accept_header = request.header.get('Accept') + if accept_header in content_types: + if accept_header == 'application/json': + return "Tenant Successfully deleted" + elif accept_header == 'application/xml': + #TODO: Implement XML support + return "whatever, we don't have XML yet" + else: + # If there is no Accept header, the default is JSON. + #TODO: Make sure that the body is actually JSON. + return "Tenant Successfully deleted" + + return 'it did NOT work\n' + @route ('/tokens', method='POST') def create_token(): @@ -205,18 +367,210 @@ class Tenants: return 'it did NOT work\n' + + @route('/tenant/:tenantId/groups', method='POST') + def create_tenant_group(tenantId): + ''' + Creating tenant by doing a POST on /tenant/:tenantId/groups + {"group": + { + "id" : "Admin", + "description" : "A Description of the group..." + } + } + + ''' + + if 'CONTENT_TYPE' in request.environ: + content_types = ['text/plain', 'application/json', + 'application/xml', 'text/xml'] + content = request.environ['CONTENT_TYPE']; + if content in content_types: + + #try: + if content == 'application/json': + if tenantId: + body = json.loads(request.body.readline()) + + group_id = body['group']['id'] + group_desc = body['group']['description'] + + dbpath = os.path.abspath(os.path.join(os.path.dirname(__file__),'../db/keystone.db')) + con = sqlite3.connect(dbpath) + cur = con.cursor() + cur.execute("SELECT * FROM tenants WHERE tenant_id='%s'" % (tenantId)) + t=cur.fetchone() + # Finding Tenants Exists or not + if t is not None: + # Finding Tenant Exists or not + if not t[2]: + return abort(403, "Tenant Disabled") + + else: + return abort(401, "unauthorized") + # Finding group Exists or not + cur.execute("SELECT count(*) FROM groups WHERE tenant_id='%s' AND group_id='%s' " % (tenantId,group_id)) + a=cur.fetchone() + + count=a[0] + if count: + + + + return abort(409, "Group already exists") + + else: + try: + cur.execute("INSERT INTO groups ('group_id','tenant_id','group_desc') VALUES ('%s','%s', '%s')" % (group_id.strip(),tenantId,group_desc)) + con.commit() + + return '{"group":{"tenantId" : "%s","id" : "%s","description" : "%s"}}' % (group_id.strip(),tenantId,group_desc) + except Exception,e: + return abort(500,"IDM Fault Creation Failed") + con.close() + else: + return abort(400, "Bad Request") + elif content == 'application/xml': + #TODO: Implement XML support + return "whatever, we don't have XML yet" + + #except: + # return abort(500, "IDM Fault") + return 'it did NOT work\n' - """ - Created a simple create user functionality for testing.. + @route('/tenant/:tenantId/groups', method='GET') + def get_tenant_groups(tenantId): + ''' + Getting all Tenant Groups /tenant/tenantId/groups GET + + Response will be like + + {"groups": { + "values" : [ + { + "tenantId" : "1234", + "id" : "Admin", + "description" : "A description ..." + }, + { + "tenantId" : "1234", + "id" : "Technical", + "description" : "Another description ..." + } + ] + } + } + + ''' + if 'CONTENT_TYPE' in request.environ: + content_types = ['text/plain', 'application/json', + 'application/xml', 'text/xml'] + content = request.environ['CONTENT_TYPE']; + if content in content_types: + + #try: + if content == 'application/json': + if tenantId: + + dbpath = os.path.abspath(os.path.join(os.path.dirname(__file__),'../db/keystone.db')) + con = sqlite3.connect(dbpath) + cur = con.cursor() + # Finding group Exists or not + tenant=cur.execute("SELECT * FROM tenants WHERE tenant_id='%s'" % (tenantId)) + t=cur.fetchone() + resp='' + count=tenant.rowcount + + + if count: + + if not t[2]: + # checking Tenant Enabled or not + return abort(403, "Tenant disabled") + + groups=cur.execute("SELECT * FROM groups WHERE tenant_id='%s'" % (tenantId)) + if groups.rowcount > 100: + return abort(413,"Over Limit") + else: + resp+='{"groups": { "values" : [' + gresp='' + for group in groups: + if gresp=='': + gresp+='{"tenantId" : "%s","id" : "%s","description" : "%s"}' % (group[2],group[0],group[1]) + else: + gresp+=',{"tenantId" : "%s","id" : "%s","description" : "%s"}' % (group[2],group[0],group[1]) + resp+=gresp+']}}' + + return resp + + + else: + return abort(401, "unauthorized") + con.close() + else: + return abort(400, "Bad Request") + elif content == 'application/xml': + #TODO: Implement XML support + return "whatever, we don't have XML yet" + #except: + # return abort(500, "IDM Fault") + return 'it did NOT work\n' + + @route('/tenant/:tenantId/groups/:groupId', method='GET') + def get_tenant_group(tenantId,groupId): + ''' + Getting Tenant Group /tenant/tenantId/groups/groupId + ''' + if 'CONTENT_TYPE' in request.environ: + content_types = ['text/plain', 'application/json', + 'application/xml', 'text/xml'] + content = request.environ['CONTENT_TYPE']; + if content in content_types: + + try: + if content == 'application/json': + if tenantId and groupId: + + dbpath = os.path.abspath(os.path.join(os.path.dirname(__file__),'../db/keystone.db')) + con = sqlite3.connect(dbpath) + cur = con.cursor() + # Finding group Exists or not + tenant=cur.execute("SELECT * FROM tenants WHERE tenant_id='%s'" % (tenantId)) + t=cur.fetchone() + resp='' + count=tenant.rowcount + if count: + if not t[2]: + # checking Tenant Enabled or not + return abort(403, "Tenant disabled") + + cur.execute("SELECT * FROM groups WHERE tenant_id='%s' AND group_id='%s' " % (tenantId,groupId)) + group=cur.fetchone + + if not group == None: + resp='{"group": { "tenantId" : "%s","id" : "%s","description" : "%s"}}' % (group[2],group[0],group[1]) + return resp + else: + + return abort(404, "Group Not Found") + else: + return abort(401, "unauthorized") + con.close() + else: + return abort(400, "Bad Request") + elif content == 'application/xml': + #TODO: Implement XML support + return "whatever, we don't have XML yet" + except: + return abort(500, "IDM Fault") + return 'it did NOT work\n' - will be updated to after testing.. - """ - @route ('/users', method='POST') - def create_user(): + @route ('/tokens', method='POST') + def create_token(): ''' - Creating users by doing a POST on /users + Creating token by doing a POST on /tokens ''' if 'CONTENT_TYPE' in request.environ: content_types = ['text/plain', 'application/json', @@ -225,29 +579,126 @@ class Tenants: if content in content_types: if content == 'application/json': + dbpath = os.path.abspath(os.path.join(os.path.dirname(__file__),'../db/keystone.db')) body = json.loads(request.body.readline()) - - username = body['username'] - password = body['password'] + - dbpath = os.path.abspath(os.path.join(os.path.dirname(__file__), '../db/keystone.db')) + try: + username = body['username'] + password = body['password'] + if 'tenant_id' in body: + tenant_id= body['tenant_id'] + except: + return abort(400,'Bad Request') - con = sqlite3.connect(dbpath) + + #except: + #return abort(400,'Bad Request') + elif content == 'application/xml': + #TODO: Implement XML support + return "whatever, we don't have XML yet" + con = sqlite3.connect(dbpath) + cur = con.cursor() + cur.execute( + "SELECT * FROM users WHERE id='%s' AND password='%s'" % + (username, password)) + result=cur.fetchone() + if result is not None: + + if (int(result[3]) == 0): + return abort(403,"UserDisabled") + accept_header = request.header.get('Accept') + token=hashlib.sha224(str(username+password)+str(datetime.now())).hexdigest()[:21] + expires=datetime.now()+timedelta(minutes=1) + con = sqlite3.connect(dbpath,detect_types=sqlite3.PARSE_DECLTYPES) cur = con.cursor() cur.execute( - "INSERT INTO users VALUES ('%s','%s')" % - (username,password)) + 'insert into token(token_id,expires) values(?, ?)',(token, expires)) + con.commit() con.close() + if accept_header in content_types: + if accept_header == 'application/json': + + return '{"token":'+token+',"expires": '+str(expires)+'}' + elif accept_header == 'application/xml': + #TODO: Implement XML support + return "whatever, we don't have XML yet" + else: + # If there is no Accept header, the default is JSON. + #TODO: Make sure that the body is actually JSON. + + return '{"token":'+ token+',"expires": '+str(expires)+'}' + else: + + return abort(401, "Unauthorised user") + return 'it did NOT work\n' + + + """ + Created a simple create user functionality for testing.. + + + + will be updated to after testing.. + """ + @route ('/tenants/:tenantId/users', method='POST') + def create_user(tenantId): + ''' + Creating users by doing a POST on /users + ''' + if 'CONTENT_TYPE' in request.environ: + content_types = ['text/plain', 'application/json', + 'application/xml', 'text/xml'] + content = request.environ['CONTENT_TYPE']; + if content in content_types: + + if content == 'application/json': + body = json.loads(request.body.readline()) + + try: + username = body['user']['username'] + password = body['user']['password'] + email=body['user'] ['email'] + enabled=body['user']['enabled'] + except: + return abort(400,'Bad Request') + elif content == 'application/xml': #TODO: Implement XML support return "whatever, we don't have XML yet" + dbpath = os.path.abspath(os.path.join(os.path.dirname(__file__), '../db/keystone.db')) + con = sqlite3.connect(dbpath) + cur = con.cursor() + cur.execute("select * from tenants where tenant_id='%s'" % (str(tenantId))) + result=cur.fetchone() + + if result is not None: + + if (int(result[2]) == 0): + return abort(403,"Forbidden") + else: + return abort(401,"Unauthorised") + cur.execute("select COUNT(*) from users where id='%s'" % (username)) + username_conflict=cur.fetchone() + + if(int(username_conflict[0]) > 0): + abort(409,"Username Conflict") + cur.execute("select count(*) from users where email='%s'"%(email)) + email_conflict=cur.fetchone() + if( int(email_conflict[0]) > 0): + abort(409,"Email Conflict") + cur.execute( + "INSERT INTO users VALUES ('%s','%s','%s','%d')" % + (username,password,email,enabled)) + con.commit() + con.close() + accept_header = request.header.get('Accept') if accept_header in content_types: if accept_header == 'application/json': - return '{"User":"created successfully" }' elif accept_header == 'application/xml': #TODO: Implement XML support @@ -271,7 +722,6 @@ class Tenants: 'application/xml', 'text/xml'] content = request.environ['CONTENT_TYPE']; count=0 - if content in content_types: dbpath = os.path.abspath( os.path.join(os.path.dirname(__file__), @@ -280,21 +730,23 @@ class Tenants: cur = con.cursor() cur.execute( - "SELECT * FROM token WHERE token_id='%s' AND DATETIME(expires)>DATETIME('now')" % + "SELECT * FROM token WHERE token_id='%s' " % (token_id)) - a=cur.fetchone() - - count=len(a) - con.commit() - con.close() - - if count>0: - #return '{ "token": {"id": "'+a[0]+'", "expires": "2010-11-01T03:32:15-05:00"}}' - - return '{"auth" : { "token": {"id": "'+a[0]+'", "expires": "'+a[1]+'"}, "user" :{"groups"{ "group": [{"tenantId" : "1234","name": "Admin"}]}, "username": "jqsmith", "tenantId": "1234",}{"tenantId" : "1234", "name": "Admin"}}}' + row=cur.fetchone() + if row is None: + abort(401, "Token doesnot exists") + else: + + expires=datetime.strptime(row[2],"%Y-%m-%d %H:%M:%S.%f") + if(expires<datetime.now()): + abort(401, "Token Expired") + else: + #return '{ "token": {"id": "'+a[0]+'", "expires": "2010-11-01T03:32:15-05:00"}}' + return '{"auth" : { "token": {"id": "'+str(row[0])+'", "expires": "'+str(row[2])+'"}, "user" :{"groups"{ "group": []}, "username": "jqsmith", "tenantId": "1234",}{"tenantId" : "1234", "name": "Admin"}}}' else: - abort(401, "Token not valid") + abort(401,"Token not valid") return 'it did NOT work\n' + debug(True) run(host='localhost', port=8080, reloader=True) |
