Git based wiki inspired by Gollum
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

180 lines
5.7KB

  1. from __future__ import absolute_import
  2. from flask import session
  3. from flask_login import login_user
  4. from flask_oauthlib.client import OAuth
  5. from realms import config
  6. from realms.modules.auth.models import BaseUser
  7. config = config.conf
  8. oauth = OAuth()
  9. users = {}
  10. providers = {
  11. 'twitter': {
  12. 'oauth': dict(
  13. base_url='https://api.twitter.com/1.1/',
  14. request_token_url='https://api.twitter.com/oauth/request_token',
  15. access_token_url='https://api.twitter.com/oauth/access_token',
  16. authorize_url='https://api.twitter.com/oauth/authenticate',
  17. access_token_method='GET'),
  18. 'button': '<a href="/login/oauth/twitter" class="btn btn-twitter"><i class="fa fa-twitter"></i></a>',
  19. 'profile': None,
  20. 'field_map': {
  21. 'id': 'user_id',
  22. 'username': 'screen_name'
  23. },
  24. 'token_name': 'oauth_token'
  25. },
  26. 'github': {
  27. 'oauth': dict(
  28. request_token_params={'scope': 'user:email'},
  29. base_url='https://api.github.com/',
  30. request_token_url=None,
  31. access_token_method='POST',
  32. access_token_url='https://github.com/login/oauth/access_token',
  33. authorize_url='https://github.com/login/oauth/authorize'),
  34. 'button': '<a href="/login/oauth/github" class="btn btn-github"><i class="fa fa-github"></i></a>',
  35. 'profile': 'user',
  36. 'field_map': {
  37. 'id': 'id',
  38. 'username': 'login',
  39. 'email': lambda(data): data.get('email') or data['login'] + '@users.noreply.github.com'
  40. },
  41. 'token_name': 'access_token'
  42. },
  43. 'facebook': {
  44. 'oauth': dict(
  45. request_token_params={'scope': 'email'},
  46. base_url='https://graph.facebook.com',
  47. request_token_url=None,
  48. access_token_url='/oauth/access_token',
  49. access_token_method='GET',
  50. authorize_url='https://www.facebook.com/dialog/oauth'
  51. ),
  52. 'button': '<a href="/login/oauth/facebook" class="btn btn-facebook"><i class="fa fa-facebook"></i></a>',
  53. 'profile': '/me',
  54. 'field_map': {
  55. 'id': 'id',
  56. 'username': 'name',
  57. 'email': 'email'
  58. },
  59. 'token_name': 'access_token'
  60. },
  61. 'google': {
  62. 'oauth': dict(
  63. request_token_params={
  64. 'scope': 'https://www.googleapis.com/auth/userinfo.email'
  65. },
  66. base_url='https://www.googleapis.com/oauth2/v1/',
  67. request_token_url=None,
  68. access_token_method='POST',
  69. access_token_url='https://accounts.google.com/o/oauth2/token',
  70. authorize_url='https://accounts.google.com/o/oauth2/auth',
  71. ),
  72. 'button': '<a href="/login/oauth/google" class="btn btn-google"><i class="fa fa-google"></i></a>',
  73. 'profile': 'userinfo',
  74. 'field_map': {
  75. 'id': 'id',
  76. 'username': 'name',
  77. 'email': 'email'
  78. },
  79. 'token_name': 'access_token'
  80. }
  81. }
  82. class User(BaseUser):
  83. type = 'oauth'
  84. provider = None
  85. def __init__(self, provider, user_id, username=None, token=None, email=None):
  86. self.provider = provider
  87. self.username = username
  88. self.email = email
  89. self.id = user_id
  90. self.token = token
  91. self.auth_id = "%s-%s" % (provider, username)
  92. @property
  93. def auth_token_id(self):
  94. return self.token
  95. @staticmethod
  96. def load_user(*args, **kwargs):
  97. return User.get_by_id(args[0])
  98. @staticmethod
  99. def get_by_id(user_id):
  100. return users.get(user_id)
  101. @staticmethod
  102. def auth(provider, data, oauth_token):
  103. field_map = providers.get(provider).get('field_map')
  104. if not field_map:
  105. raise NotImplementedError
  106. def get_value(d, key):
  107. if isinstance(key, basestring):
  108. return d.get(key)
  109. elif callable(key):
  110. return key(d)
  111. # key should be list here
  112. val = d.get(key.pop(0))
  113. if len(key) == 0:
  114. # if empty we have our value
  115. return val
  116. # keep digging
  117. return get_value(val, key)
  118. fields = {}
  119. for k, v in field_map.items():
  120. fields[k] = get_value(data, v)
  121. user = User(provider, fields['id'], username=fields.get('username'), email=fields.get('email'),
  122. token=User.hash_password(oauth_token))
  123. users[user.auth_id] = user
  124. if user:
  125. login_user(user, remember=True)
  126. return True
  127. else:
  128. return False
  129. @classmethod
  130. def get_app(cls, provider):
  131. if oauth.remote_apps.get(provider):
  132. return oauth.remote_apps.get(provider)
  133. app = oauth.remote_app(
  134. provider,
  135. consumer_key=config.OAUTH.get(provider, {}).get('key'),
  136. consumer_secret=config.OAUTH.get(provider, {}).get(
  137. 'secret'),
  138. **providers[provider]['oauth'])
  139. app.tokengetter(lambda: session.get(provider + "_token"))
  140. return app
  141. @classmethod
  142. def get_provider_value(cls, provider, key):
  143. return providers.get(provider, {}).get(key)
  144. @classmethod
  145. def get_token(cls, provider, resp):
  146. return resp.get(cls.get_provider_value(provider, 'token_name'))
  147. def get_id(self):
  148. return unicode("%s/%s" % (self.type, self.auth_id))
  149. @staticmethod
  150. def login_form():
  151. buttons = []
  152. for name, val in providers.items():
  153. if not config.OAUTH.get(name, {}).get('key') or not config.OAUTH.get(name, {}).get('secret'):
  154. continue
  155. buttons.append(val.get('button'))
  156. return "<h3>Social Login</h3>" + " ".join(buttons)