Wow, that's great.  I'm just reaching the stage of adding login/
authorization to my project, and your approach looks just like what I
had in mind, but with all the unknowns filled in.  Thanks for sharing.


On Mar 27, 9:42 am, Wolverine <[EMAIL PROTECTED]> wrote:
> Yannick Gingras pisze:> Greetings Pyloneers,
>
> > It's be a long time since I hacked something with Pylons but things
> > are moving favorably now.  After a flash demo of our great tools, I
> > was able to convince out lead architect that Pylons was the right
> > choice for our next web app.
>
> Hello!
> I'm new to this group, never posted anything here before, but I think
> I've got something just right for you. I have done implementation of
> HTTP-Auth Basic (Digest is no problem also, but you'll have to change
> the controller a bit) and also web based user authentication.
>
> So. Here is my solution for http-auth-basic. Mind you it's not very
> fancy, because we are not using http-auth anymore. Also we are using
> Elixir as our database model system on top of SQLAlchemy. Since this is
> obsolete piece of code it may be not usable, but you can take something
> from this and do your own http-auth login controller. It's not
> guaranteed it will work.
>
> --- cut ---
>
> from YOURPROJECTNAME.lib.base import *
> import base64
>
> class LoginController(BaseController):
>     """"""
>
>     def index(self):
>         """"""
>         c.error = session.get('error')
>         return render('/login/login.mako')
>
>     def login(self):
>         """"""
>         if not request.headers.has_key('Authorization'):
>             response.status_code = 401
>             response.headers['WWW-Authenticate'] = "Basic realm=\"REALM\""
>             return "Not authenticated"
>
>         auth = request.headers['Authorization']
>         auth_list = auth.split(' ')
>         auth_type = auth_list[0]
>         if auth_type == 'Basic':
>             auth_login, auth_pass =
> base64.b64decode(auth_list[1]).split(':')
>         elif auth_type == 'Digest':
>             return 'Digest authentication is not yet implemented'
>
>         # FIXME: Get account object from database
>         account_obj = model.Account.query().get_by(email = auth_login)
>         if account_obj is None:
>             # FIXME: Show error
>             abort(404)
>
>         # Check if account is active
>         if account_obj.status == 0:
>             # FIXME:
>             session['error'] = 'Account is not active! Cannot login.'
>             session.save()
>             redirect_to(action = 'index')
>
>         # If passwords do not match
>         if account_obj.password != auth_pass:
>             session['error'] = 'Error: Passwords do not match!'
>             session.save()
>             redirect_to(action = 'index')
>
>         # User authentication succeeded, save logged_user_id in session
>         session['logged_user_id'] = account_obj.id
>         session.save()
>         return redirect_to('login/loggedin.mako')
>
> --- cut ---
>
> As for authenticating website users through website form we have devised
> our own system based on Challenge-Response Protocol. Basically the idea
> is to create a login controller like this:
>
> controllers/login.py
> --- cut ---
>
> class LoginController(BaseController): #TODO: docstring
>     """
>     Login controller class
>     """
>     hash_func = config.get('passwd_hash_func')
>
>     def index(self):
>         """
>         Shows login form
>         """
>         c.error = session['error'] if session.has_key('error') else None
>
>         # Generate hashed random seed
>         rand_str = str(datetime.now()) + str(random.randint(100,1000000))
>         rseed = self.hash_func(rand_str).hexdigest()
>
>         # Save this random seed into session
>         session['login_rseed'] = rseed
>         session.save()
>
>         # Setup form
>         c.form = login_form(action = h.url_for(controller = 'login',
> action = 'login'),
>                     attrs = {'onsubmit' : 'javascript:return
> validate_login_form();'},
>                     value = {'rseed' : rseed}
>                     )
>         c.heading = u"Logowanie"
>
>         return render('/login/login.mako')
>
>     @validate(form = login_form, error_handler = 'index')
>     def login(self): #TODO: docstring
>         """
>         Performs login checks
>         """
>         # Check if user is logged
>         if session.get('logged_user'):
>             session['error'] = 'User already logged in!'
>             session.save()
>             return render('/login/loggedin.mako')
>
>         # Get values passed from login form
>         email = str(request.POST.get('email'))
>         password = str(request.POST.get('password'))
>         secure = str(request.POST.get('secure'))
>
>         # Remove previous errors
>         if session.get('error'):
>             del(session['error'])
>             session.save()
>
>         # Get account from database
>         account_obj = model.Account.query().get_by(email = email)
>         if account_obj is None:
>             session['error'] = 'Error: User does not exist!'
>             session.save()
>             redirect_to(action = 'index')
>
>         # Generate hash for comparison
>         apasswd = account_obj.password
>
>         # Check if password was sent in secure manner
>         if secure == 'yes':
>             # Check if random seed has been passed in session
>             rseed = session.get('login_rseed')
>             if not rseed:
>                 session['error'] = 'Error: Random seed doesn\'t exist!'
>                 session.save()
>                 redirect_to(action = 'index')
>
>             # Generate hash from seed and password
>             seedpass = self.hash_func(rseed + apasswd).hexdigest()
>         else:
>             # Password was cleartext so we have to hash it
>             seedpass = apasswd
>             password = self.hash_func(password).hexdigest()
>
>         # Check Challenge Response hashes
>         if seedpass != password:
>             session['error'] = 'Error: Bad email or password!'
>             session.save()
>             redirect_to(action = 'index')
>
>         # If hashes match save user as logged in
>         session['logged_user'] = email
>         session['logged_user_id'] = account_obj.id
>         session.save()
>
>         # Send user back to the page he originally wanted to get to
>         if session.get('path_before_login'):
>             # Get path before login
>             redir_url = session['path_before_login']
>             # Remove path before login from session
>             del(session['path_before_login'])
>             session.save()
>             # Redirect to url
>             redirect_to(redir_url)
>         else:
>             return render('/login/loggedin.mako')
>
>     def logout(self): #TODO: docstring
>         """
>         Performs user logout
>         """
>         # Check if user is logged
>         if not session.get('logged_user'):
>             session['error'] = 'User is not logged in!'
>             session.save()
>             return redirect_to(action = 'index')
>
>         del(session['logged_user'])
>         del(session['logged_user_id'])
>         session.save()
>         return render('login/logout.mako')
>
> --- cut ---
>
> And here is the login template:
>
> templates/login/login.mako
> --- cut ---
>
> # -*- coding: utf-8 -*-
> <%inherit file="/main.mako" />
>
> % if c.error:
>     <div id="errorbox">
>     ${ c.error }
>     </div>
> % endif
>
> <noscript>
> <div id="errorbox">
> <h2>Warning!</h2>
> You don't have JavaScript enabled. Your password will be unprotected! If
> you want to login securely enable Javascript in your browser settings.
> </div>
> </noscript>
>
> ${c.form}
>
> <%def name="metatags()">
> ${ h.javascript_include_tag('prototypeUtils', 'md5', 'helpers', builtins
> = True) }
> </%def>
>
> --- cut ---
>
> We are also using ToscaWidgets for creating forms so we have something
> like this for the login form:
>
> widgets/forms/login/login.py
> --- cut ---
>
> from toscawidgets.widgets import forms
> from toscawidgets.api import WidgetsList
> from toscawidgets.widgets.forms.validators import Email, UnicodeString,
> NotEmpty
>
> forms.FormField.engine_name = "mako"
>
> class LoginForm(forms.Form):
>     """"""
>     template = "mako:mibu.templates.forms.default"
>     submit_text = u"Login"
>
>     class fields(WidgetsList):
>         email = forms.TextField(
>                                validator = Email(not_empty = True),
>                                label_text = u"E-mail:"
>                                )
>         password = forms.PasswordField(
>                                validator = UnicodeString(not_empty = True),
>                                label_text = u"Password:"
>                                )
>         rseed = forms.HiddenField(default = '')
>         secure = forms.HiddenField(default = '')
>
> login_form = LoginForm("login_form")
>
> --- cut ---
>
> The last thing to do is to correct BaseController. We are specifying
> requires_auth variable that you can further use in your own controllers
> so unauthenticated users are automatically redirected to the login form
> and after successful login they are brought back to the page they
> requested.
>
> requires_auth is either a list of controller methods or boolean value.
> If it's bool and True every action in the controller requires
> authentication.
>
> lib/base.py
> --- cut ---
>
> class BaseController(WSGIController):
>     requires_auth = []
>
>     def __before__(self, action):
>         # Check if controller needs authorization
>         if type(self.requires_auth) == types.BooleanType:
>             if self.requires_auth:
>                 if 'logged_user' not in session:
>                     session['path_before_login'] = request.path_info
>                     session.save()
>                     return redirect_to(h.url_for(controller = 'login'))
>         # If requires authorization is list
>         else:
>             # Check if action needs authorization
>             if action in self.requires_auth:
>                 if 'logged_user' not in session:
>                     session['path_before_login'] = request.path_info
>                     session.save()
>                     return redirect_to(h.url_for(controller = 'login'))
>
>     def __call__(self, environ, start_response):
>         """Invoke the Controller"""
>         # WSGIController.__call__ dispatches to the Controller method
>         # the request is routed
> ...
>
> read more ยป
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"pylons-discuss" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/pylons-discuss?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to