Re: [pylons-discuss] Authenticating with Oauth2/OpenID Connect

2019-06-21 Thread Mike Orr
So I can extract the data from my OAuth2 tokens now. My remaining
questions are how to integrate refreshing into a Pyramid application.

First, do I need to? I don't care if the token is refreshed; I'll keep
using the claims stored in the Pyramid session until it expires.But if
I want to contribute to the enterprise's Single Sign-In, do I need to
tell the server the user is still logged into my application so it
doesn't expire the SSO account? Do I do this by refreshing the token?

If I do want to refresh the token, do I do it in a NewRequest subscriber?
The refresh_expire is 60 minutes, so how close to the end should I do
the refresh? If a request comes in 10 minutes before the end, I don't
know whether the next request will be in 1 second or 20 minutes. What
if the request contains POST data? Would I have to save the data in
the session, generate an authorization URL, redirect to the server,
come back through the callback view, redirect back to the original
URL, and extract the POST data in the session. (Where it's no longer
in request.POST.) That sounds like a lot of code overhead.

I may also have to put a session lock in the site; i.e., Javascript
that waits for an idle timeout and puts up a modal dialog, "Do you
want to extend your session?" or "Your session is expired. Click here
to log in again." How would that interact with tokens and refreshing
tokens, since my token processing is in the backend? How could the
Javascript extend the session or have the user log in again without
throwing away a partially-filled-in form? I've gotten user complaints
about that, that the user leaves the page on a form and then come back
later and submits the form and blammo! the server-side session is
deleted so they have to log in again and reenter the form input from
scratch. So I want to avoid that.

On Sat, Jun 8, 2019 at 9:40 AM Mike Orr  wrote:
>
> To follow up, I got OAuth/Keycloak authentication working with the
> following code pattern. The userinfo contains only name/email-related
> attributes, not the role attributes and origin directory info I need
> to calculate Pyramid pincipals. (Origin directory = where the user is
> defined; e.g., an enterprise LDAP directory.)
>
> Our Keycloak admin thinks these are not in the userinfo but in the
> access token itself, which is not a random string as I thought but
> JWT-encoded JSON. I was able to Base64-decode the token and get what
> looks like JSON with a "JWT" key. I'm evaluating 'jwt' and a few other
> libraries and seeing if there's a public key I need to decrypt it. The
> Base64-decoded file is not fully JSON: the JWT value is binary, and
> the file ends without a closing quote and brace. Maybe that's a JWT
> format that predates JSON.
>
> My original intention was to calculate the group principals based on
> the Keycloak roles and origin directory info, and get rid of my User
> record in the database that contains these, or rather convert the User
> record to an archive of the user's latest login date and Keycloak
> attributes.
>
> But without the Keycloak roles I can't do that, so I'm falling back to
> the existing User records for that information. This means I can
> authorize users who already have a User record, but if a new Keycloak
> user comes in (somebody who's configured in Keycloak to access the
> application but doesn't have a User record), I'll either have to not
> support them or create a User record with default roles because I
> don't know what their roles should be. The project team is deciding
> whether to do this and what the default roles should be. The admins
> can modify the User records online, but somebody will have to do it
> before the user first logs in or soon afterward, because otherwise
> they'll have fewer permissions than they should.
>
> Here's the code again from my prototype app:
>
> ===
> import pprint
> import secrets
> import requests_oauthlib
>
> # Utilities
> def get_oauth2_session(request, state):
> redirect_uri = request.route_url("login")
> client = request.registry.settings["oauth2.client"]
> scope = request.registry.settings["oauth2.scope"]   # scope == None.
> oauth = requests_oauthlin.OauthSession(
> client, redirect_uri=redirect_uri, scope=scope, state=state)
> return oauth
>
>  # View callables
>  def home(request):
>   """Display 'Login with Keycloak' link."""
>  auth_url = request.registry.settings["oauth2.url.auth"]
>  state = secrets.token_urlsafe()
>  request.sesion["oauth2_state"] = state
>  oauth = get_oauth2_session(request, state)
>  authorization_url, state2 = oauth.authorization_url(auth_url)
>  if state2 != state:
>  log.error("STATE MISMATCH: %r != %r", state2, state)
> requests.session["oauth2_state"] = state
>  return {"authorization_url": authorization_url}
>
>  def login(request):
>  """Callback page; receive authn from Keycloak server."""
>  secret = request.registry.settings["oauth2.secret"]
>  token_url = requ

Re: [pylons-discuss] How to handle SQLA autoflush failures during Request commit?

2019-06-21 Thread Theron Luhn
Also using the cookie-cutter SQLAlchemy in front of Gunicorn.

1) I have autoflush disabled.  It doesn’t reduce the mental load—as your 
example shows, you still need to understand when and why to use flush—and 
premature flushes are a headache to work around.

2) After a query fails (in this case a deadlock), the session is unusable for 
the remainder of the request.  This is expected behavior.  Usually when I’ve 
encountered an exception like this, it’s because either my exception view or a 
tween somewhere is trying to run a query.  I’m not sure how to overcome this 
limitation, but I’ve haven’t found it too bad to work around.

— Theron



> On Jun 21, 2019, at 10:39 AM, Jens Troeger  wrote:
> 
> Hello,
> 
> I’m using gunicorn  in front of a Pyramid server, and 
> I changed the configuration from 1 to 4 workers. Now I occasionally see the 
> following exception:
> 
> InvalidRequestError: This Session's transaction has been rolled back due to a 
> previous exception during flush. To begin a new transaction with this 
> Session, first issue Session.rollback(). Original exception was: (raised as a 
> result of Query-invoked autoflush; consider using a session.no_autoflush 
> block if this flush is occurring prematurely)
> (pymysql.err.OperationalError) (1213, 'Deadlock found when trying to get 
> lock; try restarting transaction')
> [SQL: UPDATE … SET foo=%(bar)s WHERE …]
> [parameters: {…}]
> (Background on this error at: http://sqlalche.me/e/e3q8)
> 
> The server follows the SQLA cookie-cutter template 
> , and is running with 
> a SQLAlchemy Session factory 
> 
>  whose autoflush 
> 
>  still defaults to true. Curiously, it seems that only one particular 
> endpoint keeps triggering this problem, and the failing UPDATE attempts to 
> flip a boolean flag.
> 
> However, the exception gives rise to two questions:
> SQLAlchemy perspective. The reason for using autoflush here is to ensure that 
> new ORM objects whose primary key is generated like so:
> id = Column(UUID(), default=uuid.uuid4, primary_key=True)
> have a valid id after they’ve been newly created. Would using a manual 
> dbsession.flush() be preferable with autoflush disabled? Are there better 
> recommended ways of handling this problem? (See this related question 
> .)
> Pyramid perspective. Every Request object has its own Session object 
> associated which commits when request handling is done. However, the above 
> exception in a sense fails the request handling. If I was to follow the 
> suggestion to “begin a new transaction with this Session” then how would I do 
> that? Does Pyramid provide existing support to handle such issues, or does 
> the exception indicate a problem elsewhere that requires attention? What is 
> best practice here?
> In general, I think I’d like to understand the finer details of SQLAlchemy’s 
> autoflush and how that interplays with Pyramid’s requests and their Sessions 
> and transactions. I wonder, for example, if the above indicates that a client 
> has sent the same request more than once (Android’s httplib seems to do 
> that), and with more than one workers enabled this manifests a race condition.
> 
> Much thanks!
> Jens
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "pylons-discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to pylons-discuss+unsubscr...@googlegroups.com 
> .
> To post to this group, send email to pylons-discuss@googlegroups.com 
> .
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/pylons-discuss/e0201c58-f801-42c6-93e8-a3bb08e9a230%40googlegroups.com
>  
> .
> For more options, visit https://groups.google.com/d/optout 
> .

-- 
You received this message because you are subscribed to the Google Groups 
"pylons-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to pylons-discuss+unsubscr...@googlegroups.com.
To post to this group, send email to pylons-discuss@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/pylons-discuss/E04BD716-1FB8-4D31-8297-B9C4C15B8B80%40luhn.com.
For more options, visit https://groups.google.com/d/optout.


[pylons-discuss] How to handle SQLA autoflush failures during Request commit?

2019-06-21 Thread Jens Troeger
Hello,

I’m using gunicorn  in front of a Pyramid server, 
and I changed the configuration from 1 to 4 workers. Now I occasionally see 
the following exception:

InvalidRequestError: This Session's transaction has been rolled back due to 
a previous exception during flush. To begin a new transaction with this 
Session, first issue Session.rollback(). Original exception was: (raised as 
a result of Query-invoked autoflush; consider using a session.no_autoflush 
block if this flush is occurring prematurely)
(pymysql.err.OperationalError) (1213, 'Deadlock found when trying to get 
lock; try restarting transaction')
[SQL: UPDATE … SET foo=%(bar)s WHERE …]
[parameters: {…}]
(Background on this error at: http://sqlalche.me/e/e3q8)

The server follows the SQLA cookie-cutter template 
, and is running 
with a SQLAlchemy Session factory 

 
whose autoflush 

 
still defaults to true. Curiously, it seems that only one particular 
endpoint keeps triggering this problem, and the failing UPDATE attempts to 
flip a boolean flag.

However, the exception gives rise to two questions:

   1. *SQLAlchemy perspective.* The reason for using autoflush here is to 
   ensure that new ORM objects whose primary key is generated like so:
   id = Column(UUID(), default=uuid.uuid4, primary_key=True)
   have a valid id after they’ve been newly created. Would using a manual 
   dbsession.flush() be preferable with autoflush disabled? Are there better 
   recommended ways of handling this problem? (See this related question 
   
   .)
   2. *Pyramid perspective.* Every Request object has its own Session 
   object associated which commits when request handling is done. However, the 
   above exception in a sense *fails* the request handling. If I was to 
   follow the suggestion to “begin a new transaction with this Session” then 
   how would I do that? Does Pyramid provide existing support to handle such 
   issues, or does the exception indicate a problem elsewhere that requires 
   attention? What is best practice here?

In general, I think I’d like to understand the finer details of 
SQLAlchemy’s autoflush and how that interplays with Pyramid’s requests and 
their Sessions and transactions. I wonder, for example, if the above 
indicates that a client has sent the same request more than once (Android’s 
httplib seems to do that), and with more than one workers enabled this 
manifests a race condition.

Much thanks!
Jens

-- 
You received this message because you are subscribed to the Google Groups 
"pylons-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to pylons-discuss+unsubscr...@googlegroups.com.
To post to this group, send email to pylons-discuss@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/pylons-discuss/e0201c58-f801-42c6-93e8-a3bb08e9a230%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.