From 5f4fbfcf61031a8c0431903f3c594a48350f7fd1 Mon Sep 17 00:00:00 2001 From: Sagi Dayan Date: Wed, 10 Jun 2015 17:41:49 +0300 Subject: [PATCH 01/10] Use Model Changes added camuses_id_list & classes_id_lists --- models/User.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/models/User.py b/models/User.py index e2aa922..9a990ee 100644 --- a/models/User.py +++ b/models/User.py @@ -12,6 +12,8 @@ class User(db.Model): seToken = db.StringProperty(required=True) avatar_url = db.StringProperty(required=True) isFirstLogin = db.BooleanProperty(default=True) + campuses_id_list = db.StringListProperty() + classes_id_list = db.StringListProperty() def to_JSON(self): dick = {'username' : self.username, @@ -21,5 +23,7 @@ class User(db.Model): 'seToken' : self.seToken, 'avatar_url' : self.avatar_url, 'isFirstLogin' : self.isFirstLogin, + 'campuses_id_list': self.campuses_id_list, + 'classes_id_list': self.classes_id_list } return json.dumps(dick) \ No newline at end of file From 3c5eba4fea4c4d3b83f41c05cb2b2b9e9bf7772a Mon Sep 17 00:00:00 2001 From: Sagi Dayan Date: Wed, 10 Jun 2015 18:47:53 +0300 Subject: [PATCH 02/10] API: Added A Route To Send Validation Emails. See /api/help --- SE_API/API.py | 39 +++++++++++++++++++++++++++++--- SE_API/Validation_Utils.py | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/SE_API/API.py b/SE_API/API.py index d8f1be3..e7064e9 100644 --- a/SE_API/API.py +++ b/SE_API/API.py @@ -41,6 +41,39 @@ def page_not_found(e): def wellcomePage(): return app.send_static_file('index.html') +@app.route('/api/validatation/sendmail/', methods=['POST']) +@auto.doc() +def send_activation(token): + """ + This Method Will Send An Email To The User - To Confirm his Account + :param token: - seToken + :payload: JSON - {email: 'academic@email.ac.com'} + :return: + 200 - Email Sent - No Response + 400 - Bad Request + 403 - Invalid Token + """ + if not request.data: + return Response(response=json.dumps({'message': 'Bad Request'}), + status=400, + mimetype="application/json") + payload = json.loads(request.data) + if not is_user_token_valid(token): + return Response(response=json.dumps({'message': 'Not A Valid Token!'}), + status=403, + mimetype="application/json") + query = User.all() + query.filter('seToken =', token) + for u in query.run(limit=1): + try: + send_validation_email(token=token, name=u.username, email=payload["email"]) + except Exception: + return Response(response=json.dumps({'message': 'Bad Request'}), + status=400, + mimetype="application/json") + + return Response(status=200) + @app.route('/api/help') def documentation(): return auto.html() @@ -67,8 +100,8 @@ def getUserByToken(token): for u in query.run(limit=5): return Response(response=u.to_JSON(), - status=201, - mimetype="application/json") # Real response! + status=201, + mimetype="application/json") # Real response! return Response(response=json.dumps({'message' : 'No User Found'}), status=400, @@ -135,7 +168,7 @@ def get_campuses(token): .... { ... - } + }req ] code 403: Forbidden - Invalid Token diff --git a/SE_API/Validation_Utils.py b/SE_API/Validation_Utils.py index e074f13..836e4d1 100644 --- a/SE_API/Validation_Utils.py +++ b/SE_API/Validation_Utils.py @@ -1,6 +1,8 @@ __author__ = 'sagi' from google.appengine.ext import db from models.User import User +from google.appengine.api import mail + def is_user_token_valid(token): query = User.all() @@ -9,3 +11,47 @@ def is_user_token_valid(token): for u in query.run(): return True return False + +def send_validation_email(token, email, name): + message = mail.EmailMessage(sender="SE-Hub Support ", + subject="SE-Hub Activate Account") + + message.to = email + + message.body = """ + Dear """+name+""": + + To Activate your SE-Hub Account please click on the link below:
+ http://se-hub.appspot.com/api/validatation/confirm/"""+token+""" + to access you virtual class. + + Please let us know if you have any questions. + + SE-Hub (c) 2015 niptop Team. + """ + + message.html = """ + +
+
+ +
+
+
+

Hey """+name+"""- Just one More Step...

+

Dear """+name+""":

+ + To Activate your SE-Hub Account please click on the link below:
+ http://se-hub.appspot.com/api/validatation/confirm/"""+token+"""

+ + to access you virtual class. +
+

+ Please let us know if you have any questions. +
+ SE-Hub (c) 2015 niptop Team. + + + """ + + message.send() From 827cb0c17fa36b24143e08fcc3ad7d874d167b8b Mon Sep 17 00:00:00 2001 From: aranzaiger Date: Wed, 10 Jun 2015 21:39:42 +0300 Subject: [PATCH 03/10] Added models: Project, Course, Task. also added imports to api.py --- SE_API/API.py | 4 ++++ models/Course.py | 22 ++++++++++++++++++++++ models/Project.py | 20 ++++++++++++++++++++ models/Task.py | 24 ++++++++++++++++++++++++ models/User.py | 6 +++--- 5 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 models/Course.py create mode 100644 models/Project.py create mode 100644 models/Task.py diff --git a/SE_API/API.py b/SE_API/API.py index aa2b33b..f298ffc 100644 --- a/SE_API/API.py +++ b/SE_API/API.py @@ -14,6 +14,8 @@ from flask.ext.autodoc import Autodoc # DB Models from models.User import User +from models.Course import Course +from models.Project import Project @@ -72,6 +74,8 @@ def getUserByToken(token): + + @app.route('/githubOAuth') @cross_origin('*') @github.authorized_handler diff --git a/models/Course.py b/models/Course.py new file mode 100644 index 0000000..83557a7 --- /dev/null +++ b/models/Course.py @@ -0,0 +1,22 @@ +import json + +__author__ = 'Aran' +from google.appengine.ext import db + +class Course(db.Model): + courseName = db.StringProperty(required=True) + campusName = db.StringProperty(required=True) + projects = db.StringListProperty(required=True) + startDate = db.DateProperty(required=True) + endDate = db.DateProperty(required=False) + taskFlag = db.BooleanProperty(required=True) + + def to_JSON(self): + data = {'courseName' : self.courseName, + 'campusName' : self.campusName, + 'projects' : self.projects, + 'startDate' : self.startDate, + 'endDate' : self.endDate, + 'taskFlag' : self.taskFlag, + } + return json.dumps(data) diff --git a/models/Project.py b/models/Project.py new file mode 100644 index 0000000..edecd83 --- /dev/null +++ b/models/Project.py @@ -0,0 +1,20 @@ +import json + +__author__ = 'Aran' +from google.appengine.ext import db + +class Project(db.Model): + projectName = db.StringProperty(required=True) + grade = db.IntegerProperty(required=True) + logo_url = db.StringProperty(required=True) + gitRepository = db.StringProperty(required=True) + membersId = db.StringListProperty(required=True) + + def to_JSON(self): + data = {'projectName' : self.projectName, + 'grade' : self.grade, + 'logo_url' : self.logo_url, + 'gitRepository' : self.gitRepository, + 'membersId' : self.membersId, + } + return json.dumps(data) diff --git a/models/Task.py b/models/Task.py new file mode 100644 index 0000000..20119cc --- /dev/null +++ b/models/Task.py @@ -0,0 +1,24 @@ +import json + +__author__ = 'Aran' +from google.appengine.ext import db + +class Task(db.Model): + title = db.StringProperty(required=True) + description = db.StringProperty(required=True) + dueDate = db.DateProperty(required=True) + isProject = db.BooleanProperty(required=True) + isClose = db.BooleanProperty(required=True) + isDone = db.BooleanProperty(required=True) + taskGrade = db.IntegerProperty(required=True) + + def to_JSON(self): + data = {'title' : self.title, + 'description' : self.description, + 'dueDate' : self.dueDate, + 'isProject' : self.isProject, + 'isClose' : self.membersId, + 'isDone' : self.isDone, + 'taskGrade' : self.taskGrade, + } + return json.dumps(data) diff --git a/models/User.py b/models/User.py index e2aa922..8ce26a0 100644 --- a/models/User.py +++ b/models/User.py @@ -8,13 +8,13 @@ class User(db.Model): name = db.StringProperty(required=True) email = db.StringProperty(required=True) isLecturer = db.BooleanProperty(required=True) - accsessToken = db.StringProperty(required=True) + accessToken = db.StringProperty(required=True) seToken = db.StringProperty(required=True) avatar_url = db.StringProperty(required=True) isFirstLogin = db.BooleanProperty(default=True) def to_JSON(self): - dick = {'username' : self.username, + data = {'username' : self.username, 'name' : self.name, 'email' : self.email, 'isLecturer' : self.isLecturer, @@ -22,4 +22,4 @@ class User(db.Model): 'avatar_url' : self.avatar_url, 'isFirstLogin' : self.isFirstLogin, } - return json.dumps(dick) \ No newline at end of file + return json.dumps(data) \ No newline at end of file From e1b6f496861e92e183cf16b081e0ef95f149b87e Mon Sep 17 00:00:00 2001 From: aranzaiger Date: Thu, 11 Jun 2015 20:59:40 +0300 Subject: [PATCH 04/10] added response utils --- SE_API/Respones_Utils.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 SE_API/Respones_Utils.py diff --git a/SE_API/Respones_Utils.py b/SE_API/Respones_Utils.py new file mode 100644 index 0000000..a7d7b17 --- /dev/null +++ b/SE_API/Respones_Utils.py @@ -0,0 +1,15 @@ +__author__ = 'Aran' + +from flask import Response +import json + +def bad_request(): + return Response(response=json.dumps({'message': 'Bad Request'}), + status=400, + mimetype="application/json") + + +def forbidden(message={'message': 'Forbidden'}): + return Response(response=json.dumps(message), + status=403, + mimetype="application/json") \ No newline at end of file From dcb83987f88786d2c8466026b16c16a05007b75a Mon Sep 17 00:00:00 2001 From: aranzaiger Date: Thu, 11 Jun 2015 21:01:05 +0300 Subject: [PATCH 05/10] -var name change in campus -default value for Campus properties --- models/Campus.py | 4 ++-- models/User.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/models/Campus.py b/models/Campus.py index 4859409..f4e9cef 100644 --- a/models/Campus.py +++ b/models/Campus.py @@ -11,12 +11,12 @@ class Campus(db.Model): master_user_id = db.IntegerProperty(required=True) def to_JSON(self): - dick = {'title': self.title, + data = {'title': self.title, 'email_ending': self.email_ending, 'master_user_id': self.master_user_id, 'avatar_url': self.avatar_url } - return json.dumps(dick) + return json.dumps(data) """ diff --git a/models/User.py b/models/User.py index ed0e2c1..c336d36 100644 --- a/models/User.py +++ b/models/User.py @@ -12,8 +12,8 @@ class User(db.Model): seToken = db.StringProperty(required=True) avatar_url = db.StringProperty(required=True) isFirstLogin = db.BooleanProperty(default=True) - campuses_id_list = db.StringListProperty() - classes_id_list = db.StringListProperty() + campuses_id_list = db.StringListProperty(default=[]) + classes_id_list = db.StringListProperty(default=[]) def to_JSON(self): data = {'username' : self.username, From 628aad5e0369789297e523c5dd920eed584dcf78 Mon Sep 17 00:00:00 2001 From: aranzaiger Date: Thu, 11 Jun 2015 21:01:58 +0300 Subject: [PATCH 06/10] added 2 methods (is_lecturer, get_user_by_token) --- SE_API/Validation_Utils.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/SE_API/Validation_Utils.py b/SE_API/Validation_Utils.py index 836e4d1..12c35d9 100644 --- a/SE_API/Validation_Utils.py +++ b/SE_API/Validation_Utils.py @@ -4,14 +4,27 @@ from models.User import User from google.appengine.api import mail -def is_user_token_valid(token): +def get_user_by_token(token): query = User.all() query.filter("seToken = ", token) - for u in query.run(): + for u in query.run(limit = 1): + return u + return None + +def is_user_token_valid(token): + user = get_user_by_token(token) + if user is not None: return True return False +def is_lecturer(token): + user = get_user_by_token(token) + if user is None: + return False + return user.isLecturer + + def send_validation_email(token, email, name): message = mail.EmailMessage(sender="SE-Hub Support ", subject="SE-Hub Activate Account") From d015986dfc12e3e2b64042db782bb8aab138682e Mon Sep 17 00:00:00 2001 From: aranzaiger Date: Thu, 11 Jun 2015 21:03:00 +0300 Subject: [PATCH 07/10] added Campus create method --- SE_API/API.py | 52 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/SE_API/API.py b/SE_API/API.py index 56c5583..d203ddc 100644 --- a/SE_API/API.py +++ b/SE_API/API.py @@ -1,3 +1,4 @@ + __author__ = 'sagi' import json from GithubAPI.GithubAPI import GitHubAPI_Keys @@ -20,6 +21,7 @@ from models.Campus import Campus #Validation Utils Libs from SE_API.Validation_Utils import * +from SE_API.Respones_Utils import * @@ -130,7 +132,7 @@ def oauth(oauth_token): print user_data["login"] - for u in resault.run(limit=5): + for u in resault.run(): print "Exists!!!" u.seToken = str(uuid.uuid4()) u.accessToken = oauth_token @@ -146,12 +148,58 @@ def oauth(oauth_token): else: tempEmail = user_data["email"] - user = User(username=user_data["login"], name=tempName, avatar_url=user_data["avatar_url"], email=tempEmail, isLecturer=False, accsessToken=oauth_token, seToken=str(uuid.uuid4())) + user = User(username=user_data["login"], name=tempName, avatar_url=user_data["avatar_url"], email=tempEmail, isLecturer=False, accessToken=oauth_token, seToken=str(uuid.uuid4())) db.put(user) db.save return cookieMonster(user.seToken) +@app.route('/api/Campuses/create/', methods=['POST']) +@auto.doc() +def create_campus(token): + """ + This call will create a new campus in the DB + :param token: user seToken + Payload + {'title': self.title, + 'email_ending': self.email_ending, + 'avatar_url': self.avatar_url + } + :return: + code 200 + """ + print "1\n" + if not request.data: + return Response(response=json.dumps({'message': 'Bad Request0'}), + status=400, + mimetype="application/json") + payload = json.loads(request.data) + if not is_lecturer(token): #todo: change to lecturer id + return Response(response=json.dumps({'message': 'Invalid token or not a lecturer!'}), + status=403, + mimetype="application/json") + + user = get_user_by_token(token) + + #todo: check legality + + + try: + campus = Campus(title=payload['title'], email_ending=payload['email_ending'], master_user_id=user.key().id(), avatar_url=payload['avatar_url']) + except Exception: + return Response(response=json.dumps({'message': 'Bad Request1'}), + status=400, + mimetype="application/json") + + db.put(campus) + db.save + return Response(response=json.dumps(campus.to_JSON()), + status=200, + mimetype="application/json") + + + + @app.route('/api/Campuses/', methods=['GET']) @auto.doc() def get_campuses(token): From f31053f6327f7bd2d469b71f139ae32a550ed5a2 Mon Sep 17 00:00:00 2001 From: Sagi Dayan Date: Thu, 11 Jun 2015 21:32:46 +0300 Subject: [PATCH 08/10] API: Added A Skeleton Route For Email Validation --- SE_API/API.py | 26 ++++++++++++++++++++++++-- SE_API/Validation_Utils.py | 10 ++++++---- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/SE_API/API.py b/SE_API/API.py index 56c5583..04286ba 100644 --- a/SE_API/API.py +++ b/SE_API/API.py @@ -43,7 +43,29 @@ def page_not_found(e): def wellcomePage(): return app.send_static_file('index.html') -@app.route('/api/validatation/sendmail/', methods=['POST']) +@app.route('/api/validation/confirm/') +@auto.doc() +def confirm_user_to_campus(validation_token): + """ + This Function is will re + :param validation_token: 'seToken|email_suffix' + :return: + 200 - redirect to home + new cookie + 403 - Invalid Token + """ + #TODO + token = str(validation_token).split('|')[0] + email_sufix = '@'+str(validation_token).split('|')[1] + + if is_user_token_valid(token): + return Response(status=200, response=json.dumps({'token': token, 'suffix': email_sufix})) + else: + return Response(response=json.dumps({'message': 'Not A Valid Token!'}), + status=403, + mimetype="application/json") + + +@app.route('/api/validation/sendmail/', methods=['POST']) @auto.doc() def send_activation(token): """ @@ -146,7 +168,7 @@ def oauth(oauth_token): else: tempEmail = user_data["email"] - user = User(username=user_data["login"], name=tempName, avatar_url=user_data["avatar_url"], email=tempEmail, isLecturer=False, accsessToken=oauth_token, seToken=str(uuid.uuid4())) + user = User(username=user_data["login"], name=tempName, avatar_url=user_data["avatar_url"], email=tempEmail, isLecturer=False, accessToken=oauth_token, seToken=str(uuid.uuid4())) db.put(user) db.save return cookieMonster(user.seToken) diff --git a/SE_API/Validation_Utils.py b/SE_API/Validation_Utils.py index 836e4d1..f9754da 100644 --- a/SE_API/Validation_Utils.py +++ b/SE_API/Validation_Utils.py @@ -13,8 +13,9 @@ def is_user_token_valid(token): return False def send_validation_email(token, email, name): + emailSuffix = str(email).split('@')[1] message = mail.EmailMessage(sender="SE-Hub Support ", - subject="SE-Hub Activate Account") + subject="SE-Hub Activate Account") message.to = email @@ -22,8 +23,8 @@ def send_validation_email(token, email, name): Dear """+name+""": To Activate your SE-Hub Account please click on the link below:
- http://se-hub.appspot.com/api/validatation/confirm/"""+token+""" - to access you virtual class. + http://se-hub.appspot.com/api/validation/confirm/"""+token+"""|"""+emailSuffix+""" + to get access to your Campus :) Please let us know if you have any questions. @@ -35,6 +36,7 @@ def send_validation_email(token, email, name):
+
@@ -42,7 +44,7 @@ def send_validation_email(token, email, name):

Dear """+name+""":

To Activate your SE-Hub Account please click on the link below:
- http://se-hub.appspot.com/api/validatation/confirm/"""+token+"""

+ http://se-hub.appspot.com/api/validation/confirm/"""+token+"""|"""+emailSuffix+""" to access you virtual class.
From 435bb5ef3dc7b157e239c0b8d66d5fa3e2e84709 Mon Sep 17 00:00:00 2001 From: Sagi Dayan Date: Thu, 11 Jun 2015 22:46:18 +0300 Subject: [PATCH 09/10] API: Email Validation Works (Only Production!) --- SE_API/API.py | 19 ++++++++++++++----- SE_API/Respones_Utils.py | 8 ++++---- SE_API/Validation_Utils.py | 9 +++++++++ models/User.py | 2 +- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/SE_API/API.py b/SE_API/API.py index f90b38f..2e66002 100644 --- a/SE_API/API.py +++ b/SE_API/API.py @@ -59,12 +59,21 @@ def confirm_user_to_campus(validation_token): token = str(validation_token).split('|')[0] email_sufix = '@'+str(validation_token).split('|')[1] - if is_user_token_valid(token): - return Response(status=200, response=json.dumps({'token': token, 'suffix': email_sufix})) + user = get_user_by_token(token) + + if user is None: + return forbidden('Forbidden: invalid Token') else: - return Response(response=json.dumps({'message': 'Not A Valid Token!'}), - status=403, - mimetype="application/json") + campus = get_campus_by_suffix(email_sufix) + if campus is None: + return bad_request('Bad Request: Email Suffix ' + email_sufix + ' Not Found') + user.isFirstLogin = False + user.seToken = str(uuid.uuid4()) + if str(campus.key().id()) not in user.campuses_id_list: + user.campuses_id_list.append(str(campus.key().id())) + db.put(user) + return cookieMonster(user.seToken) + @app.route('/api/validation/sendmail/', methods=['POST']) diff --git a/SE_API/Respones_Utils.py b/SE_API/Respones_Utils.py index a7d7b17..b789974 100644 --- a/SE_API/Respones_Utils.py +++ b/SE_API/Respones_Utils.py @@ -3,13 +3,13 @@ __author__ = 'Aran' from flask import Response import json -def bad_request(): - return Response(response=json.dumps({'message': 'Bad Request'}), +def bad_request(message='Bad Request'): + return Response(response=json.dumps({'message': message}), status=400, mimetype="application/json") -def forbidden(message={'message': 'Forbidden'}): - return Response(response=json.dumps(message), +def forbidden(message='Forbidden'): + return Response(response=json.dumps({'message': message}), status=403, mimetype="application/json") \ No newline at end of file diff --git a/SE_API/Validation_Utils.py b/SE_API/Validation_Utils.py index 9226d61..e99b2bd 100644 --- a/SE_API/Validation_Utils.py +++ b/SE_API/Validation_Utils.py @@ -1,6 +1,7 @@ __author__ = 'sagi' from google.appengine.ext import db from models.User import User +from models.Campus import Campus from google.appengine.api import mail @@ -12,6 +13,14 @@ def get_user_by_token(token): return u return None +def get_campus_by_suffix(suffix): + query = Campus.all() + query.filter("email_ending = ", suffix) + + for c in query.run(limit = 1): + return c + return None + def is_user_token_valid(token): user = get_user_by_token(token) if user is not None: diff --git a/models/User.py b/models/User.py index c336d36..8c3e330 100644 --- a/models/User.py +++ b/models/User.py @@ -5,7 +5,7 @@ from google.appengine.ext import db class User(db.Model): username = db.StringProperty(required=True) - name = db.StringProperty(required=True) + name = db.StringProperty(required=False) email = db.StringProperty(required=True) isLecturer = db.BooleanProperty(required=True) accessToken = db.StringProperty(required=True) From ba15613417a7251499cfa01f12148df7a8109f4a Mon Sep 17 00:00:00 2001 From: Sagi Dayan Date: Thu, 11 Jun 2015 23:36:26 +0300 Subject: [PATCH 10/10] Angularjs: Added new Service sendValidationMail Service --- templates/js/services/apiService.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/templates/js/services/apiService.js b/templates/js/services/apiService.js index b794224..e240375 100644 --- a/templates/js/services/apiService.js +++ b/templates/js/services/apiService.js @@ -22,6 +22,20 @@ service.factory('apiService', ['$http', function($http) { url : url }; + return $http(req); + }, + sendValidationMail: function(token, email){ + var url = (DEBUG ? "http://localhost:8080" : "http://se-hub.appspot.com") + "/api/validation/sendmail/" + token; + payload = { + email: email + }; + + req = { + method: "POST", + url: url, + data: payload + }; + return $http(req); }