Workout With Friends Stay fit with a little motivation

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Properties
auth.py
Go to the documentation of this file.
1 ##
2 #
3 # Auth related helpers used to login, logout, hash passwords, etc.
4 #
5
6 from __future__ import unicode_literals
7 import bcrypt
8 import datetime
9 import random
10 import string
11 from pyramid.response import Response
12 from pyramid.security import authenticated_userid, forget, remember
13 from pyramid.threadlocal import get_current_request
14 from wowf.lib.mailer import send_mail
15 from wowf.lib.utils import current_timestamp, parse_timedelta
16 from wowf.models import Group, InviteToken, LoginToken, PasswordToken, User
17
18
19 LOGIN_GROUP = 'login'
20 DEFAULT_GROUPS = [LOGIN_GROUP]
21
22 LOGIN_TOKEN_KEY = 'auth_token'
23
24 LOGIN_TOKEN_LIFETIME = datetime.timedelta(days=30)
25 PASSWORD_TOKEN_LIFETIME = datetime.timedelta(hours=24)
26 INVITE_TOKEN_LIFETIME = datetime.timedelta(days=30)
27
28
29 class Auth(object):
30
31 @staticmethod
32 ##
33 #
34 # Send invitations to the given emails on behalf of the user, if given,
35 # or on behalf of the system.
36 #
37 def send_invite(emails, user=None):
38 invite_token = InviteToken.create(user, INVITE_TOKEN_LIFETIME)
39 if user:
40 extra_headers = {'Reply-To': user.email}
41 else:
42 extra_headers = None
43 send_mail(subject="You've Been Invited To Join Workout With Friends", recipients=emails,
44 template='emails/invite.html',
45 variables=dict(user=user, token=invite_token.token,
46 age=parse_timedelta(INVITE_TOKEN_LIFETIME)),
47 extra_headers=extra_headers)
48
49 @staticmethod
50 ##
51 #
52 # Check if the token is valid, and thus if the user is allowed to register.
53 #
54 def check_invite(token):
55 invite_token = InviteToken.get_by_token(token)
56 return invite_token and invite_token.is_valid()
57
58 @staticmethod
59 ##
60 #
61 # Provide the user with the minimum privileges necessary to access the
62 # system.
63 #
64 # @param login Whether to return login headers
65 #
66 def register(user, login=True):
67 for group in DEFAULT_GROUPS:
68 user.groups.append(Group.get_by_name(group))
69 send_mail(subject='Welcome to Workout With Friends', recipients=user.email,
70 template='emails/welcome.html', variables=dict(user=user))
71 if login:
72 return Auth.login(user, remember_me=False)
73
74 @staticmethod
75 ##
76 #
77 # Check the email/password combo, and whether the user has privileges
78 # necessary to access the system.
79 #
80 def check_login(email, password):
81 user = User.get_by_email(email)
82 if user and user.password == Auth.hash_password(password, user.password):
83 login_group = Group.get_by_name(LOGIN_GROUP)
84 return user and login_group in user.groups
85 return False
86
87 @staticmethod
88 ##
89 #
90 # Start a new session for the user.
91 #
92 # Performs no authentication on the user, so authenticate the user
93 # proior to logging them in.
94 #
95 # @return Login response headers
96 #
97 def login(user, remember_me=False):
98 request = get_current_request()
99 response = Response()
100 response.headerlist.extend(remember(request, user.id))
101 if remember_me:
102 login_token = LoginToken.create(user, LOGIN_TOKEN_LIFETIME, request.user_agent)
103 response.set_cookie(LOGIN_TOKEN_KEY, login_token.token, max_age=LOGIN_TOKEN_LIFETIME)
104 if not user.is_active:
105 user.is_active = True
106 user.last_active_at = current_timestamp()
107 return response.headers
108
109 @staticmethod
110 ##
111 #
112 # Log the user in based on the presence of a "remember me" token.
113 #
114 # Only works if the user chose to be remembered on last login, and a
115 # valid token exists.
116 #
117 # @return Response headers or None
118 #
119 def auto_login():
120 request = get_current_request()
121 token = request.cookies.get(LOGIN_TOKEN_KEY)
122 if token:
123 login_token = LoginToken.get_by_token(token)
124 if login_token and login_token.is_valid(request.user_agent):
125 return Auth.login(login_token.user, remember_me=False)
126
127 @staticmethod
128 ##
129 #
130 # End the users session. Delete login token if one is found.
131 #
132 # @return Response headers
133 #
134 def logout():
135 request = get_current_request()
136 response = Response()
137 response.headerlist.extend(forget(request))
138 auth_token = request.cookies.get(LOGIN_TOKEN_KEY)
139 if auth_token:
140 response.delete_cookie(LOGIN_TOKEN_KEY)
141 token = LoginToken.get_by_token(auth_token)
142 if token:
143 token.delete()
144 return response.headers
145
146 @staticmethod
147 ##
148 #
149 # Send an email to the user with unstructions on how to reset their
150 # password.
151 #
152 def request_reset_password(email):
153 user = User.get_by_email(email)
154 password_token = PasswordToken.create(user, PASSWORD_TOKEN_LIFETIME)
155 send_mail(subject='Reset Password Request', recipients=user.email,
156 template='emails/request_password.html',
157 variables=dict(user=user, token=password_token.token,
158 age=parse_timedelta(PASSWORD_TOKEN_LIFETIME)))
159
160 @staticmethod
161 ##
162 #
163 # Check the token, reset the password of the linked user, and email
164 # them a copy upon success.
165 #
166 # @return True or False
167 #
168 def reset_password(token):
169 is_success = False
170 password_token = PasswordToken.get_by_token(token)
171 if password_token and password_token.is_valid():
172 random_password = Auth.generate_random_password()
173 user = password_token.user
174 user.password = random_password
175 send_mail(subject='Temporary Password', recipients=user.email,
176 template='emails/temporary_password.html',
177 variables=dict(user=user, random_password=random_password))
178 password_token.delete()
179 is_success = True
180 return is_success
181
182 @staticmethod
183 ##
184 #
185 # Hash the password.
186 #
187 # @param check_password Current password hash, used as salt
188 #
189 def hash_password(password, check_password=None):
190 if isinstance(password, unicode):
191 password = password.encode('utf-8')
192 if check_password is None:
193 # Creating a new password
194 salt = bcrypt.gensalt(12)
195 else:
196 # Checking the current password
197 salt = check_password
198 return bcrypt.hashpw(password, salt)
199
200 @staticmethod
201 ##
202 #
203 # Returns a random password using the given characters.
204 #
205 # @param chars A sequence of acceptable characters
206 #
207 def generate_random_password(length=8, chars=None):
208 chars = chars or string.letters + string.digits + '!@#$*?'
209 return ''.join(random.sample(chars, length))
210
211 @staticmethod
212 ##
213 #
214 # Get the currently logged in user.
215 #
216 # @return User instance or None
217 #
218 def get_current_user():
219 request = get_current_request()
220 user_id = authenticated_userid(request)
221 if user_id:
222 return User.get_by_id(user_id)
223