Sending mail with links to SQLFORM.grid edit page - not authorized

159 views
Skip to first unread message

Jim S

unread,
Nov 6, 2013, 1:10:10 PM11/6/13
to web...@googlegroups.com
I'm sending an email with a link back to an edit page on SQLFORM.grid like this:

    ticket = db.ticket(ticketId)

    subject = 'Ticket %s - %s' % (ticket.ticketId, ticket.summary)
    user = db.auth_user(ticket.assignedId)
    to = user.email

    message = '<html>%s has assigned to %s to you.<br /><br />-----<br /><br />%s</html>' % (auth.user.first_name, A('SideboardHR Ticket %s' % (ticket.ticketId), _href=URL('maintenance','tickets', args=['edit','ticket',ticket.ticketId], host=True,scheme=True)),
                                                                      ticket.description)
    mail.send(to=[to],subject=subject,message=message)

When I click on the generated link it get an alert saying 'not authorized' regardless of whether or not I'm already logged in or not.

How can I setup my link so it will take me there once I'm logged in?

-Jim

Richard Vézina

unread,
Nov 6, 2013, 1:28:49 PM11/6/13
to web2py-users
Did you decorate your tickets function?

@auth.requires_login()

Richard


--
Resources:
- http://web2py.com
- http://web2py.com/book (Documentation)
- http://github.com/web2py/web2py (Source code)
- https://code.google.com/p/web2py/issues/list (Report Issues)
---
You received this message because you are subscribed to the Google Groups "web2py-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to web2py+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Jim Steil

unread,
Nov 6, 2013, 1:31:28 PM11/6/13
to web...@googlegroups.com
Yes, it does require you to login if you are not logged in.  If not logged in it takes you to the login page before issuing the 'not authorized' even though I'm logging in...

In maintenance.py:

@auth.requires_login()
def tickets():
.....

-Jim




You received this message because you are subscribed to a topic in the Google Groups "web2py-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/web2py/YhzviZbdwW0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to web2py+un...@googlegroups.com.

Niphlod

unread,
Nov 6, 2013, 2:22:47 PM11/6/13
to web...@googlegroups.com
beware that if the session is renewed (the random uuid changes between when you send the link and when the user accesses the page) the signature changes.

Cliff Kachinske

unread,
Nov 6, 2013, 2:31:59 PM11/6/13
to
I think grid wants a signature on the URL for add, edit and delete. 

Given Niphlod's statement, even if you sign the url it might fail. Index doesn't seem to require a signature, so maybe you could send them to the index page, sorted so that the newest ticket is on top.

Jim Steil

unread,
Nov 6, 2013, 2:28:16 PM11/6/13
to web...@googlegroups.com
yes, but I'm not sending the signature, am I?

-Jim

Niphlod

unread,
Nov 6, 2013, 2:38:55 PM11/6/13
to web...@googlegroups.com
ok, rolling back a little.
if the user is accessing the link without issues without any signature, then it should be able to click on the link and reach the page. This also means that the grid you're publishing has user_signature = False, right ?

Jim Steil

unread,
Nov 6, 2013, 2:42:36 PM11/6/13
to web...@googlegroups.com
No, the user is always linking with a signature in the app.  I think the default for user_signature is True and I'm not overriding it.

So, are you telling me that if I want to deep link to the edit page on a SQLFORM.grid then I need to have user_signature = False?

If I do that, aren't I giving up some security?

-Jim


On Wed, Nov 6, 2013 at 1:38 PM, Niphlod <nip...@gmail.com> wrote:
ok, rolling back a little.
if the user is accessing the link without issues without any signature, then it should be able to click on the link and reach the page. This also means that the grid you're publishing has user_signature = False, right ?

Jim Steil

unread,
Nov 6, 2013, 2:51:04 PM11/6/13
to web...@googlegroups.com
From 'The Book':

login required by default for data updates

By default all the URL generated by the grid are digitally signed and verified. This means one cannot perform certain actions (create, update, delete) without being logged-in. These restrictions can be relaxed:

def manage_users():
    grid = SQLFORM.grid(db.auth_user,user_signature=False)
    return locals()

but we do not recommend it.


The last line is the kicker here...  I don't really want to turn this off if I don't have to.  I don't know why, but it isn't recommended.  There is no further explanation of why it isn't recommended and if I knew more about the underlying protocols it would probably make sense.  But, since I don't understand it fully, could someone elaborate on why this wouldn't be recommended?


-Jim


Niphlod

unread,
Nov 6, 2013, 2:52:28 PM11/6/13
to web...@googlegroups.com
that why then. if the user is only allowed to access the grid WITH the signature, he can't access the same link without the signature (and that's the whole point about signed links).

You need to figure out yourself (with your application's demands) what to do (i.e. sending a signed link by mail, turning off signatures, send a link to a page that then redirects to the signed link to the grid, etc etc etc)

Jim Steil

unread,
Nov 6, 2013, 2:58:07 PM11/6/13
to web...@googlegroups.com
I tried sending the signed link via the email, but that seemed to fail as well. Also have to assume that the person getting the email and navigating to the page is not using the same session as the one sending the email.  So, how would you build the signature?

I will investigate the redirect option, but if there is a way that I can send a valid signature to a different user, I would really appreciate pointers....

-Jim



On Wed, Nov 6, 2013 at 1:52 PM, Niphlod <nip...@gmail.com> wrote:
that why then. if the user is only allowed to access the grid WITH the signature, he can't access the same link without the signature (and that's the whole point about signed links).

You need to figure out yourself (with your application's demands) what to do (i.e. sending a signed link by mail, turning off signatures, send a link to a page that then redirects to the signed link to the grid, etc etc etc)

Cliff Kachinske

unread,
Nov 6, 2013, 5:26:36 PM11/6/13
to web...@googlegroups.com
Something like this:

def clean_email_key_table(table)
  # use dateutil to calculate cutoff_date. 30 days maybe?
  db(db[table].created_on < cutoff_date).delete()

db.define_table('user_email_key',
 Field('user_id', 'reference auth_user' ....),
 Field('email_key', length=256 ... # this would be a uuid generated for each email. Goes into the URL get vars.
 Field('saved_key', length=256 ... # another uuid generated for each email. Goes in the session
 Field('created_on', 'date', default = request.now),
 #assuming lazy tables
 on_define=clean_email_key_table
)

include the following in the url vars: dict(s='mail', ek=email_key, sk=saved_key)

Then in the receiving controller:

if 's' in request.get_vars and request.get_vars.s == 'mail': 
  signature = False
  query = db(
    (db.user_email_key.user_id==auth.user_id) &
    (db.user_email_key.email_key==request.get_vars.ek) &
    (db.user_email_key.saved_key==session.saved_key) # the saved_key needs to go into the session at the time you generate the email.
  )
  res = db(query).count()
  if not res: 
    # redirect to not authorized raise HTTP 403 or other
    # bail out, whatever you do
  db(query).delete() # the key only works once.
  session.saved_key = None

else: signature = True
form = SQLFORM(db.whatever, ...signature=signature)

Niphlod

unread,
Nov 6, 2013, 6:37:00 PM11/6/13
to web...@googlegroups.com
or ....

@auth.requires_login()
def email_link_redirector():
    if not request.args(0):
        raise HTTP(404)
    redirect(URL('maintenance', 'tickets', args=['edit', 'ticket', request.args(0)], user_signature=True))

and send the ticket email with a link to this email_link_redirector/ticketid

if you're worried that only the user receiving the mail should indeed be able to open the link you send, then use a precalculated hmac_key. Something along the lines of
 
'secret_of_your_choice' + id_of_the_user + '_' + int(time.time()) \ 600

will even make that link expiring if too much time has passed (in the previous example, 600 secs)
in that case, replace the
if not request.args(0)
with
if not URL.verify(request, hmac_key=whatyouchoose) : raise HTTP(403)

and generate the link to email using
URL('email_link_redirector', args=ticketid, hmac_key=whatyouchoose)

Jim Steil

unread,
Nov 6, 2013, 6:46:55 PM11/6/13
to web...@googlegroups.com

Thanks for all the help.  You guys are awesome.

Jim

Reply all
Reply to author
Forward
0 new messages