暫無描述

app.py 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. from datetime import datetime, timedelta
  2. import os
  3. import jwt
  4. import json
  5. import requests
  6. import base64
  7. from functools import wraps
  8. from urlparse import parse_qs, parse_qsl
  9. from urllib import urlencode
  10. from flask import Flask, g, send_file, request, redirect, url_for, jsonify
  11. from flask.ext.sqlalchemy import SQLAlchemy
  12. from werkzeug.security import generate_password_hash, check_password_hash
  13. from requests_oauthlib import OAuth1
  14. from jwt import DecodeError, ExpiredSignature
  15. # Configuration
  16. current_path = os.path.dirname(__file__)
  17. client_path = os.path.abspath(os.path.join(current_path, '..', '..', 'client'))
  18. app = Flask(__name__, static_url_path='', static_folder=client_path)
  19. app.config.from_object('config')
  20. db = SQLAlchemy(app)
  21. class User(db.Model):
  22. id = db.Column(db.Integer, primary_key=True)
  23. email = db.Column(db.String(120), unique=True)
  24. password = db.Column(db.String(120))
  25. display_name = db.Column(db.String(120))
  26. facebook = db.Column(db.String(120))
  27. github = db.Column(db.String(120))
  28. google = db.Column(db.String(120))
  29. linkedin = db.Column(db.String(120))
  30. twitter = db.Column(db.String(120))
  31. bitbucket = db.Column(db.String(120))
  32. def __init__(self, email=None, password=None, display_name=None,
  33. facebook=None, github=None, google=None, linkedin=None,
  34. twitter=None, bitbucket=None):
  35. if email:
  36. self.email = email.lower()
  37. if password:
  38. self.set_password(password)
  39. if display_name:
  40. self.display_name = display_name
  41. if facebook:
  42. self.facebook = facebook
  43. if google:
  44. self.google = google
  45. if linkedin:
  46. self.linkedin = linkedin
  47. if twitter:
  48. self.twitter = twitter
  49. if bitbucket:
  50. self.bitbucket = bitbucket
  51. def set_password(self, password):
  52. self.password = generate_password_hash(password)
  53. def check_password(self, password):
  54. return check_password_hash(self.password, password)
  55. def to_json(self):
  56. return dict(id=self.id, email=self.email, displayName=self.display_name,
  57. facebook=self.facebook, google=self.google,
  58. linkedin=self.linkedin, twitter=self.twitter,
  59. bitbucket=self.bitbucket)
  60. db.create_all()
  61. def create_token(user):
  62. payload = {
  63. 'sub': user.id,
  64. 'iat': datetime.utcnow(),
  65. 'exp': datetime.utcnow() + timedelta(days=14)
  66. }
  67. token = jwt.encode(payload, app.config['TOKEN_SECRET'])
  68. return token.decode('unicode_escape')
  69. def parse_token(req):
  70. token = req.headers.get('Authorization').split()[1]
  71. return jwt.decode(token, app.config['TOKEN_SECRET'])
  72. def login_required(f):
  73. @wraps(f)
  74. def decorated_function(*args, **kwargs):
  75. if not request.headers.get('Authorization'):
  76. response = jsonify(message='Missing authorization header')
  77. response.status_code = 401
  78. return response
  79. try:
  80. payload = parse_token(request)
  81. except DecodeError:
  82. response = jsonify(message='Token is invalid')
  83. response.status_code = 401
  84. return response
  85. except ExpiredSignature:
  86. response = jsonify(message='Token has expired')
  87. response.status_code = 401
  88. return response
  89. g.user_id = payload['sub']
  90. return f(*args, **kwargs)
  91. return decorated_function
  92. # Routes
  93. @app.route('/')
  94. def index():
  95. return send_file(os.path.join(client_path, 'index.html'))
  96. @app.route('/api/me')
  97. @login_required
  98. def me():
  99. user = User.query.filter_by(id=g.user_id).first()
  100. return jsonify(user.to_json())
  101. @app.route('/auth/login', methods=['POST'])
  102. def login():
  103. user = User.query.filter_by(email=request.json['email']).first()
  104. if not user or not user.check_password(request.json['password']):
  105. response = jsonify(message='Wrong Email or Password')
  106. response.status_code = 401
  107. return response
  108. token = create_token(user)
  109. return jsonify(token=token)
  110. @app.route('/auth/signup', methods=['POST'])
  111. def signup():
  112. user = User(email=request.json['email'], password=request.json['password'])
  113. db.session.add(user)
  114. db.session.commit()
  115. token = create_token(user)
  116. return jsonify(token=token)
  117. @app.route('/auth/facebook', methods=['POST'])
  118. def facebook():
  119. access_token_url = 'https://graph.facebook.com/v2.3/oauth/access_token'
  120. graph_api_url = 'https://graph.facebook.com/v2.3/me'
  121. params = {
  122. 'client_id': request.json['clientId'],
  123. 'redirect_uri': request.json['redirectUri'],
  124. 'client_secret': app.config['FACEBOOK_SECRET'],
  125. 'code': request.json['code']
  126. }
  127. # Step 1. Exchange authorization code for access token.
  128. r = requests.get(access_token_url, params=params)
  129. access_token = dict(parse_qsl(r.text))
  130. # Step 2. Retrieve information about the current user.
  131. r = requests.get(graph_api_url, params=access_token)
  132. profile = json.loads(r.text)
  133. # Step 3. (optional) Link accounts.
  134. if request.headers.get('Authorization'):
  135. user = User.query.filter_by(facebook=profile['id']).first()
  136. if user:
  137. response = jsonify(message='There is already a Facebook account that belongs to you')
  138. response.status_code = 409
  139. return response
  140. payload = parse_token(request)
  141. user = User.query.filter_by(id=payload['sub']).first()
  142. if not user:
  143. response = jsonify(message='User not found')
  144. response.status_code = 400
  145. return response
  146. user.facebook = profile['id']
  147. user.display_name = user.display_name or profile['name']
  148. db.session.commit()
  149. token = create_token(user)
  150. return jsonify(token=token)
  151. # Step 4. Create a new account or return an existing one.
  152. user = User.query.filter_by(facebook=profile['id']).first()
  153. if user:
  154. token = create_token(user)
  155. return jsonify(token=token)
  156. u = User(facebook=profile['id'], display_name=profile['name'])
  157. db.session.add(u)
  158. db.session.commit()
  159. token = create_token(u)
  160. return jsonify(token=token)
  161. @app.route('/auth/github', methods=['POST'])
  162. def github():
  163. access_token_url = 'https://github.com/login/oauth/access_token'
  164. users_api_url = 'https://api.github.com/user'
  165. params = {
  166. 'client_id': request.json['clientId'],
  167. 'redirect_uri': request.json['redirectUri'],
  168. 'client_secret': app.config['GITHUB_SECRET'],
  169. 'code': request.json['code']
  170. }
  171. # Step 1. Exchange authorization code for access token.
  172. r = requests.get(access_token_url, params=params)
  173. access_token = dict(parse_qsl(r.text))
  174. headers = {'User-Agent': 'Satellizer'}
  175. # Step 2. Retrieve information about the current user.
  176. r = requests.get(users_api_url, params=access_token, headers=headers)
  177. profile = json.loads(r.text)
  178. # Step 3. (optional) Link accounts.
  179. if request.headers.get('Authorization'):
  180. user = User.query.filter_by(github=profile['id']).first()
  181. if user:
  182. response = jsonify(message='There is already a GitHub account that belongs to you')
  183. response.status_code = 409
  184. return response
  185. payload = parse_token(request)
  186. user = User.query.filter_by(id=payload['sub']).first()
  187. if not user:
  188. response = jsonify(message='User not found')
  189. response.status_code = 400
  190. return response
  191. user.github = profile['id']
  192. user.display_name = display_name or profile['name']
  193. db.session.commit()
  194. token = create_token(user)
  195. return jsonify(token=token)
  196. # Step 4. Create a new account or return an existing one.
  197. user = User.query.filter_by(github=profile['id']).first()
  198. if user:
  199. token = create_token(user)
  200. return jsonify(token=token)
  201. u = User(github=profile['id'], display_name=profile['name'])
  202. db.session.add(u)
  203. db.session.commit()
  204. token = create_token(u)
  205. return jsonify(token=token)
  206. @app.route('/auth/google', methods=['POST'])
  207. def google():
  208. access_token_url = 'https://accounts.google.com/o/oauth2/token'
  209. people_api_url = 'https://www.googleapis.com/plus/v1/people/me/openIdConnect'
  210. payload = dict(client_id=request.json['clientId'],
  211. redirect_uri=request.json['redirectUri'],
  212. client_secret=app.config['GOOGLE_SECRET'],
  213. code=request.json['code'],
  214. grant_type='authorization_code')
  215. # Step 1. Exchange authorization code for access token.
  216. r = requests.post(access_token_url, data=payload)
  217. token = json.loads(r.text)
  218. headers = {'Authorization': 'Bearer {0}'.format(token['access_token'])}
  219. # Step 2. Retrieve information about the current user.
  220. r = requests.get(people_api_url, headers=headers)
  221. profile = json.loads(r.text)
  222. # Step 3. (optional) Link accounts.
  223. if request.headers.get('Authorization'):
  224. user = User.query.filter_by(google=profile['sub']).first()
  225. if user:
  226. response = jsonify(message='There is already a Google account that belongs to you')
  227. response.status_code = 409
  228. return response
  229. payload = parse_token(request)
  230. user = User.query.filter_by(id=payload['sub']).first()
  231. if not user:
  232. response = jsonify(message='User not found')
  233. response.status_code = 400
  234. return response
  235. user.google = profile['sub']
  236. user.display_name = user.display_name or profile['name']
  237. db.session.commit()
  238. token = create_token(user)
  239. return jsonify(token=token)
  240. # Step 4. Create a new account or return an existing one.
  241. user = User.query.filter_by(google=profile['sub']).first()
  242. if user:
  243. token = create_token(user)
  244. return jsonify(token=token)
  245. u = User(google=profile['sub'],
  246. display_name=profile['name'])
  247. db.session.add(u)
  248. db.session.commit()
  249. token = create_token(u)
  250. return jsonify(token=token)
  251. @app.route('/auth/linkedin', methods=['POST'])
  252. def linkedin():
  253. access_token_url = 'https://www.linkedin.com/uas/oauth2/accessToken'
  254. people_api_url = 'https://api.linkedin.com/v1/people/~:(id,first-name,last-name,email-address)'
  255. payload = dict(client_id=request.json['clientId'],
  256. redirect_uri=request.json['redirectUri'],
  257. client_secret=app.config['LINKEDIN_SECRET'],
  258. code=request.json['code'],
  259. grant_type='authorization_code')
  260. # Step 1. Exchange authorization code for access token.
  261. r = requests.post(access_token_url, data=payload)
  262. access_token = json.loads(r.text)
  263. params = dict(oauth2_access_token=access_token['access_token'],
  264. format='json')
  265. # Step 2. Retrieve information about the current user.
  266. r = requests.get(people_api_url, params=params)
  267. profile = json.loads(r.text)
  268. # Step 3. (optional) Link accounts.
  269. if request.headers.get('Authorization'):
  270. user = User.query.filter_by(linkedin=profile['id']).first()
  271. if user:
  272. response = jsonify(message='There is already a LinkedIn account that belongs to you')
  273. response.status_code = 409
  274. return response
  275. payload = parse_token(request)
  276. user = User.query.filter_by(id=payload['sub']).first()
  277. if not user:
  278. response = jsonify(message='User not found')
  279. response.status_code = 400
  280. return response
  281. user.linkedin = profile['id']
  282. user.display_name = user.display_name or (profile['firstName'] + ' ' + profile['lastName'])
  283. db.session.commit()
  284. token = create_token(user)
  285. return jsonify(token=token)
  286. # Step 4. Create a new account or return an existing one.
  287. user = User.query.filter_by(linkedin=profile['id']).first()
  288. if user:
  289. token = create_token(user)
  290. return jsonify(token=token)
  291. u = User(linkedin=profile['id'],
  292. display_name=profile['firstName'] + ' ' + profile['lastName'])
  293. db.session.add(u)
  294. db.session.commit()
  295. token = create_token(u)
  296. return jsonify(token=token)
  297. @app.route('/auth/twitter', methods=['POST'])
  298. def twitter():
  299. request_token_url = 'https://api.twitter.com/oauth/request_token'
  300. access_token_url = 'https://api.twitter.com/oauth/access_token'
  301. if request.json.get('oauth_token') and request.json.get('oauth_verifier'):
  302. auth = OAuth1(app.config['TWITTER_CONSUMER_KEY'],
  303. client_secret=app.config['TWITTER_CONSUMER_SECRET'],
  304. resource_owner_key=request.json.get('oauth_token'),
  305. verifier=request.json.get('oauth_verifier'))
  306. r = requests.post(access_token_url, auth=auth)
  307. profile = dict(parse_qsl(r.text))
  308. user = User.query.filter_by(twitter=profile['user_id']).first()
  309. if user:
  310. token = create_token(user)
  311. return jsonify(token=token)
  312. u = User(twitter=profile['user_id'],
  313. display_name=profile['screen_name'])
  314. db.session.add(u)
  315. db.session.commit()
  316. token = create_token(u)
  317. return jsonify(token=token)
  318. else:
  319. oauth = OAuth1(app.config['TWITTER_CONSUMER_KEY'],
  320. client_secret=app.config['TWITTER_CONSUMER_SECRET'],
  321. callback_uri=app.config['TWITTER_CALLBACK_URL'])
  322. r = requests.post(request_token_url, auth=oauth)
  323. oauth_token = dict(parse_qsl(r.text))
  324. return jsonify(oauth_token)
  325. @app.route('/auth/bitbucket', methods=['POST'])
  326. def bitbucket():
  327. access_token_url = 'https://bitbucket.org/site/oauth2/access_token'
  328. users_api_url = 'https://api.bitbucket.org/2.0/user'
  329. auth_encoded = base64.b64encode(
  330. "{0}:{1}".format(request.json['clientId'],
  331. app.config['BITBUCKET_SECRET']))
  332. headers = {'Authorization': 'Basic {0}'.format(auth_encoded)}
  333. payload = dict(redirect_uri=request.json['redirectUri'],
  334. code=request.json['code'],
  335. grant_type='authorization_code')
  336. # Step 1. Exchange authorization code for access token.
  337. r = requests.post(access_token_url, data=payload, headers=headers)
  338. token = json.loads(r.text)
  339. params = {'access_token': token['access_token']}
  340. # Step 2. Retrieve information about the current user.
  341. r = requests.get(users_api_url, params=params)
  342. profile = json.loads(r.text)
  343. # Step 3. (optional) Link accounts.
  344. if request.headers.get('Authorization'):
  345. user = User.query.filter_by(bitbucket=profile['uuid']).first()
  346. if user:
  347. response = jsonify(message='There is already a Bitbucket account that belongs to you')
  348. response.status_code = 409
  349. return response
  350. payload = parse_token(request)
  351. user = User.query.filter_by(id=payload['sub']).first()
  352. if not user:
  353. response = jsonify(message='User not found')
  354. response.status_code = 400
  355. return response
  356. user.bitbucket = profile['uuid']
  357. user.display_name = user.display_name or profile['display_name']
  358. db.session.commit()
  359. token = create_token(user)
  360. return jsonify(token=token)
  361. # Step 4. Create a new account or return an existing one.
  362. user = User.query.filter_by(bitbucket=profile['uuid']).first()
  363. if user:
  364. token = create_token(user)
  365. return jsonify(token=token)
  366. u = User(bitbucket=profile['uuid'], display_name=profile['display_name'])
  367. db.session.add(u)
  368. db.session.commit()
  369. token = create_token(u)
  370. return jsonify(token=token)
  371. if __name__ == '__main__':
  372. app.run(port=3000)