class OAuth2FrontendController(RedditController): def pre(self): RedditController.pre(self) require_https() def _check_redirect_uri(self, client, redirect_uri): if not redirect_uri or not client or redirect_uri != client.redirect_uri: abort(403) def _error_response(self, resp): if (errors.OAUTH2_INVALID_CLIENT, "client_id") in c.errors: resp["error"] = "unauthorized_client" elif (errors.OAUTH2_ACCESS_DENIED, "authorize") in c.errors: resp["error"] = "access_denied" elif (errors.BAD_HASH, None) in c.errors: resp["error"] = "access_denied" elif (errors.INVALID_OPTION, "response_type") in c.errors: resp["error"] = "unsupported_response_type" elif (errors.INVALID_OPTION, "scope") in c.errors: resp["error"] = "invalid_scope" else: resp["error"] = "invalid_request" @validate(VUser(), response_type=VOneOf("response_type", ("code", )), client=VClientID(), redirect_uri=VRequired("redirect_uri", errors.OAUTH2_INVALID_REDIRECT_URI), scope=VOneOf("scope", scope_info.keys()), state=VRequired("state", errors.NO_TEXT)) def GET_authorize(self, response_type, client, redirect_uri, scope, state): """ First step in [OAuth 2.0](http://oauth.net/2/) authentication. End users will be prompted for their credentials (username/password) and asked if they wish to authorize the application identified by the **client_id** parameter with the permissions specified by the **scope** parameter. They are then redirected to the endpoint on the client application's side specified by **redirect_uri**. If the user granted permission to the application, the response will contain a **code** parameter with a temporary authorization code which can be exchanged for an access token at [/api/v1/access_token](#api_method_access_token). **redirect_uri** must match the URI configured for the client in the [app preferences](/prefs/apps). If **client_id** or **redirect_uri** is not valid, or if the call does not take place over SSL, a 403 error will be returned. For all other errors, a redirect to **redirect_uri** will be returned, with a **error** parameter indicating why the request failed. """ self._check_redirect_uri(client, redirect_uri) resp = {} if not c.errors: c.deny_frames = True return OAuth2AuthorizationPage(client, redirect_uri, scope_info[scope], state).render() else: self._error_response(resp) return self.redirect(redirect_uri + "?" + urlencode(resp), code=302) @validate(VUser(), VModhash(fatal=False), client=VClientID(), redirect_uri=VRequired("redirect_uri", errors.OAUTH2_INVALID_REDIRECT_URI), scope=VOneOf("scope", scope_info.keys()), state=VRequired("state", errors.NO_TEXT), authorize=VRequired("authorize", errors.OAUTH2_ACCESS_DENIED)) def POST_authorize(self, authorize, client, redirect_uri, scope, state): """Endpoint for OAuth2 authorization.""" self._check_redirect_uri(client, redirect_uri) resp = {} if state: resp["state"] = state if not c.errors: code = OAuth2AuthorizationCode._new(client._id, redirect_uri, c.user._id, scope) resp["code"] = code._id else: self._error_response(resp) return self.redirect(redirect_uri + "?" + urlencode(resp), code=302)
class MeetupsController(RedditController): def response_func(self, **kw): return self.sendstring(json.dumps(kw)) @validate(VUser(), VCreateMeetup(), title=ValueOrBlank('title'), description=ValueOrBlank('description'), location=ValueOrBlank('location'), latitude=ValueOrBlank('latitude'), longitude=ValueOrBlank('longitude'), timestamp=ValueOrBlank('timestamp'), tzoffset=ValueOrBlank('tzoffset')) def GET_new(self, *a, **kw): return BoringPage(pagename='New Meetup', content=NewMeetup(*a, **kw)).render() @Json @validate(VUser(), VCreateMeetup(), VModhash(), ip=ValidIP(), title=VRequired('title', errors.NO_TITLE), description=VRequired('description', errors.NO_DESCRIPTION), location=VRequired('location', errors.NO_LOCATION), latitude=VFloat('latitude', error=errors.NO_LOCATION), longitude=VFloat('longitude', error=errors.NO_LOCATION), timestamp=VTimestamp('timestamp'), tzoffset=VFloat('tzoffset', error=errors.INVALID_DATE)) def POST_create(self, res, title, description, location, latitude, longitude, timestamp, tzoffset, ip): if res._chk_error(errors.NO_TITLE): res._chk_error(errors.TITLE_TOO_LONG) res._focus('title') res._chk_errors((errors.NO_LOCATION, errors.NO_DESCRIPTION, errors.INVALID_DATE, errors.NO_DATE)) if res.error: return meetup = Meetup(author_id=c.user._id, title=title, description=description, location=location, latitude=latitude, longitude=longitude, timestamp=timestamp, tzoffset=tzoffset) # Expire all meetups in the render cache g.rendercache.invalidate_key_group(Meetup.group_cache_key()) meetup._commit() l = Link._submit(meetup_article_title(meetup), meetup_article_text(meetup), 'self', c.user, Subreddit._by_name('meetups'), ip, []) l.meetup = meetup._id36 l._commit() meetup.assoc_link = l._id meetup._commit() when = datetime.now(g.tz) + timedelta( 0, 3600) # Leave a short window of time before notification, in case # the meetup is edited/deleted soon after its creation PendingJob.store(when, 'process_new_meetup', {'meetup_id': meetup._id}) #update the queries if g.write_query_queue: queries.new_link(l) res._redirect(url_for(action='show', id=meetup._id36)) @Json @validate(VUser(), VModhash(), meetup=VEditMeetup('id'), title=VRequired('title', errors.NO_TITLE), description=VRequired('description', errors.NO_DESCRIPTION), location=VRequired('location', errors.NO_LOCATION), latitude=VFloat('latitude', error=errors.NO_LOCATION), longitude=VFloat('longitude', error=errors.NO_LOCATION), timestamp=VTimestamp('timestamp'), tzoffset=VFloat('tzoffset', error=errors.INVALID_DATE)) def POST_update(self, res, meetup, title, description, location, latitude, longitude, timestamp, tzoffset): if res._chk_error(errors.NO_TITLE): res._chk_error(errors.TITLE_TOO_LONG) res._focus('title') res._chk_errors((errors.NO_LOCATION, errors.NO_DESCRIPTION, errors.INVALID_DATE, errors.NO_DATE)) if res.error: return meetup.title = title meetup.description = description meetup.location = location meetup.latitude = latitude meetup.longitude = longitude meetup.timestamp = timestamp meetup.tzoffset = tzoffset # Expire all meetups in the render cache g.rendercache.invalidate_key_group(Meetup.group_cache_key()) meetup._commit() # Update the linked article article = Link._byID(meetup.assoc_link) article._load() article_old_url = article.url article.title = meetup_article_title(meetup) article.article = meetup_article_text(meetup) article._commit() article.update_url_cache(article_old_url) res._redirect(url_for(action='show', id=meetup._id36)) @validate(VUser(), meetup=VEditMeetup('id')) def GET_edit(self, meetup): return BoringPage(pagename='Edit Meetup', content=EditMeetup( meetup, title=meetup.title, description=meetup.description, location=meetup.location, latitude=meetup.latitude, longitude=meetup.longitude, timestamp=int(meetup.timestamp * 1000), tzoffset=meetup.tzoffset)).render() # Show a meetup. Most of this code was coped from GET_comments in front.py @validate(meetup=VMeetup('id'), sort=VMenu('controller', CommentSortMenu), num_comments=VMenu('controller', NumCommentsMenu)) def GET_show(self, meetup, sort, num_comments): article = Link._byID(meetup.assoc_link) # figure out number to show based on the menu user_num = c.user.pref_num_comments or g.num_comments num = g.max_comments if num_comments == 'true' else user_num builder = CommentBuilder(article, CommentSortMenu.operator(sort), None, None) listing = NestedListing(builder, num=num, parent_name=article._fullname) displayPane = PaneStack() # insert reply box only for logged in user if c.user_is_loggedin: displayPane.append(CommentReplyBox()) displayPane.append(CommentReplyBox(link_name=article._fullname)) # finally add the comment listing displayPane.append(listing.listing()) sort_menu = CommentSortMenu(default=sort, type='dropdown2') nav_menus = [ sort_menu, NumCommentsMenu(article.num_comments, default=num_comments) ] content = CommentListing( content=displayPane, num_comments=article.num_comments, nav_menus=nav_menus, ) # Update last viewed time, and return the previous last viewed time. Actually tracked on the article lastViewed = None if c.user_is_loggedin: clicked = article._getLastClickTime(c.user) lastViewed = clicked._date if clicked else None article._click(c.user) res = ShowMeetup(meetup=meetup, content=content, fullname=article._fullname, lastViewed=lastViewed) return BoringPage(pagename=meetup.title, content=res, body_class='meetup').render()
class OAuth2FrontendController(RedditController): def pre(self): RedditController.pre(self) require_https() def _check_redirect_uri(self, client, redirect_uri): if not redirect_uri or not client or redirect_uri != client.redirect_uri: abort(403) def _error_response(self, resp): if (errors.OAUTH2_INVALID_CLIENT, "client_id") in c.errors: resp["error"] = "unauthorized_client" elif (errors.OAUTH2_ACCESS_DENIED, "authorize") in c.errors: resp["error"] = "access_denied" elif (errors.BAD_HASH, None) in c.errors: resp["error"] = "access_denied" elif (errors.INVALID_OPTION, "response_type") in c.errors: resp["error"] = "unsupported_response_type" elif (errors.INVALID_OPTION, "scope") in c.errors: resp["error"] = "invalid_scope" else: resp["error"] = "invalid_request" @validate(VUser(), response_type=VOneOf("response_type", ("code", )), client=VClientID(), redirect_uri=VUrl("redirect_uri", allow_self=False, lookup=False), scope=VOneOf("scope", scope_info.keys()), state=VRequired("state", errors.NO_TEXT)) def GET_authorize(self, response_type, client, redirect_uri, scope, state): self._check_redirect_uri(client, redirect_uri) resp = {} if not c.errors: c.deny_frames = True return OAuth2AuthorizationPage(client, redirect_uri, scope_info[scope], state).render() else: self._error_response(resp) return self.redirect(redirect_uri + "?" + urlencode(resp), code=302) @validate(VUser(), VModhash(fatal=False), client=VClientID(), redirect_uri=VUrl("redirect_uri", allow_self=False, lookup=False), scope=VOneOf("scope", scope_info.keys()), state=VRequired("state", errors.NO_TEXT), authorize=VRequired("authorize", errors.OAUTH2_ACCESS_DENIED)) def POST_authorize(self, authorize, client, redirect_uri, scope, state): self._check_redirect_uri(client, redirect_uri) resp = {} if state: resp["state"] = state if not c.errors: code = OAuth2AuthorizationCode._new(client._id, redirect_uri, c.user._id, scope) resp["code"] = code._id else: self._error_response(resp) return self.redirect(redirect_uri + "?" + urlencode(resp), code=302)