renamed basic_auth/tools to basic_auth/lib
authorRodney Ewing <ewing.rj@gmail.com>
Wed, 15 May 2013 19:44:00 +0000 (12:44 -0700)
committerRodney Ewing <ewing.rj@gmail.com>
Fri, 24 May 2013 23:52:48 +0000 (16:52 -0700)
mediagoblin/plugins/basic_auth/__init__.py
mediagoblin/plugins/basic_auth/lib.py [new file with mode: 0644]

index 219dd45634c0fc59cbe436e7b896d040ac3d09e8..0139c78d88e467bcfc25ebdacbdba27d3db97b36 100644 (file)
@@ -17,7 +17,7 @@ import os
 import uuid
 
 import forms as auth_forms
-import tools as auth_tools
+import lib as auth_lib
 from mediagoblin.db.models import User
 from mediagoblin.tools.translate import pass_to_ugettext as _
 from mediagoblin.tools import pluginapi
@@ -29,7 +29,7 @@ def setup_plugin():
 
 
 def check_login(user, password):
-    result = auth_tools.bcrypt_check_password(password, user.pw_hash)
+    result = auth_lib.bcrypt_check_password(password, user.pw_hash)
     if result:
         return result
     return None
@@ -49,7 +49,7 @@ def create_user(registration_form):
     user = User()
     user.username = registration_form.data['username']
     user.email = registration_form.data['email']
-    user.pw_hash = auth_tools.bcrypt_gen_password_hash(
+    user.pw_hash = auth_lib.bcrypt_gen_password_hash(
         registration_form.password.data)
     user.verification_key = unicode(uuid.uuid4())
     user.save()
@@ -85,7 +85,7 @@ def get_registration_form(request):
 
 
 def gen_password_hash(raw_pass, extra_salt):
-    return auth_tools.bcrypt_gen_password_hash(raw_pass, extra_salt)
+    return auth_lib.bcrypt_gen_password_hash(raw_pass, extra_salt)
 
 
 def auth():
diff --git a/mediagoblin/plugins/basic_auth/lib.py b/mediagoblin/plugins/basic_auth/lib.py
new file mode 100644 (file)
index 0000000..5782000
--- /dev/null
@@ -0,0 +1,99 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011, 2012 MediaGoblin contributors.  See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+import bcrypt
+
+from mediagoblin.tools.template import render_template
+from mediagoblin.tools.mail import send_email
+from mediagoblin import mg_globals
+
+
+def bcrypt_check_password(raw_pass, stored_hash, extra_salt=None):
+    """
+    Check to see if this password matches.
+
+    Args:
+    - raw_pass: user submitted password to check for authenticity.
+    - stored_hash: The hash of the raw password (and possibly extra
+      salt) to check against
+    - extra_salt: (optional) If this password is with stored with a
+      non-database extra salt (probably in the config file) for extra
+      security, factor this into the check.
+
+    Returns:
+      True or False depending on success.
+    """
+    if extra_salt:
+        raw_pass = u"%s:%s" % (extra_salt, raw_pass)
+
+    hashed_pass = bcrypt.hashpw(raw_pass.encode('utf-8'), stored_hash)
+
+    # Reduce risk of timing attacks by hashing again with a random
+    # number (thx to zooko on this advice, which I hopefully
+    # incorporated right.)
+    #
+    # See also:
+    rand_salt = bcrypt.gensalt(5)
+    randplus_stored_hash = bcrypt.hashpw(stored_hash, rand_salt)
+    randplus_hashed_pass = bcrypt.hashpw(hashed_pass, rand_salt)
+
+    return randplus_stored_hash == randplus_hashed_pass
+
+
+def bcrypt_gen_password_hash(raw_pass, extra_salt=None):
+    """
+    Generate a salt for this new password.
+
+    Args:
+    - raw_pass: user submitted password
+    - extra_salt: (optional) If this password is with stored with a
+      non-database extra salt
+    """
+    if extra_salt:
+        raw_pass = u"%s:%s" % (extra_salt, raw_pass)
+
+    return unicode(
+        bcrypt.hashpw(raw_pass.encode('utf-8'), bcrypt.gensalt()))
+
+
+EMAIL_FP_VERIFICATION_TEMPLATE = (
+    u"http://{host}{uri}?"
+    u"userid={userid}&token={fp_verification_key}")
+
+
+def send_fp_verification_email(user, request):
+    """
+    Send the verification email to users to change their password.
+
+    Args:
+    - user: a user object
+    - request: the request
+    """
+    rendered_email = render_template(
+        request, 'mediagoblin/auth/fp_verification_email.txt',
+        {'username': user.username,
+         'verification_url': EMAIL_FP_VERIFICATION_TEMPLATE.format(
+                host=request.host,
+                uri=request.urlgen('mediagoblin.plugins.' +
+                                   'basic_auth.verify_forgot_password'),
+                userid=unicode(user.id),
+                fp_verification_key=user.fp_verification_key)})
+
+    # TODO: There is no error handling in place
+    send_email(
+        mg_globals.app_config['email_sender_address'],
+        [user.email],
+        'GNU MediaGoblin - Change forgotten password!',
+        rendered_email)