def generate_public_auth_request(apiKey, path, params=None): apiMode = api_settings.get('security_mode') if params is None: params = {} if apiKey: key = apiKey.token secret_key = apiKey.secret persistent = apiKey.is_persistent_allowed and api_settings.get('allow_persistent') else: key = secret_key = None persistent = False if api_settings.get('require_https'): baseURL = Config.getInstance().getBaseSecureURL() else: baseURL = Config.getInstance().getBaseURL() publicRequestsURL = None authRequestURL = None if apiMode == APIMode.KEY: publicRequestsURL = build_indico_request(path, params) authRequestURL = build_indico_request(path, params, key) if key else None elif apiMode == APIMode.ONLYKEY: authRequestURL = build_indico_request(path, params, key) if key else None params["onlypublic"] = "yes" publicRequestsURL = build_indico_request(path, params, key) if key else None elif apiMode == APIMode.SIGNED: publicRequestsURL = build_indico_request(path, params) authRequestURL = build_indico_request(path, params, key, secret_key, persistent) if key and secret_key else None elif apiMode == APIMode.ONLYKEY_SIGNED: publicRequestsURL = build_indico_request(path, params, key) if key else None authRequestURL = build_indico_request(path, params, key, secret_key, persistent) if key and secret_key else None elif apiMode == APIMode.ALL_SIGNED: authRequestURL = build_indico_request(path, params, key, secret_key, persistent) if key else None params["onlypublic"] = "yes" publicRequestsURL = build_indico_request(path, params, key, secret_key, persistent) if key else None return {"publicRequestURL": (baseURL + publicRequestsURL) if publicRequestsURL else "", "authRequestURL": (baseURL + authRequestURL) if authRequestURL else ""}
def get_base_ical_parameters(user, detail, path, params=None): """Returns a dict of all parameters expected by iCal template""" from indico.web.http_api.util import generate_public_auth_request api_mode = api_settings.get('security_mode') persistent_allowed = api_settings.get('allow_persistent') api_key = user.api_key if user else None persistent_user_enabled = api_key.is_persistent_allowed if api_key else None tpl = get_template_module('api/_messages.html') persistent_agreement = tpl.get_ical_persistent_msg() top_urls = generate_public_auth_request(api_key, path, params) urls = generate_public_auth_request(api_key, path, dict(params or {}, detail=detail)) request_urls = { 'publicRequestURL': top_urls['publicRequestURL'], 'authRequestURL': top_urls['authRequestURL'], 'publicRequestDetailedURL': urls['publicRequestURL'], 'authRequestDetailedURL': urls['authRequestURL'] } return { 'api_mode': api_mode, 'api_key': api_key, 'persistent_allowed': persistent_allowed, 'persistent_user_enabled': persistent_user_enabled, 'api_active': api_key is not None, 'api_key_user_agreement': tpl.get_ical_api_key_msg(), 'api_persistent_user_agreement': persistent_agreement, 'user_logged': user is not None, 'request_urls': request_urls }
def get_base_ical_parameters(user, event, detail, session_=None): """Returns a dict of all parameters expected by iCal template""" from indico.web.http_api.util import generate_public_auth_request api_mode = api_settings.get('security_mode') persistent_allowed = api_settings.get('allow_persistent') api_key = user.api_key if user else None persistent_user_enabled = api_key.is_persistent_allowed if api_key else None tpl = get_template_module('api/_messages.html') persistent_agreement = tpl.get_ical_persistent_msg() if session_: path = '/export/event/{0}/session/{1}.ics'.format(event.id, session_.id) else: path = '/export/event/{0}.ics'.format(event.id) top_urls = generate_public_auth_request(api_key, path) urls = generate_public_auth_request(api_key, path, {'detail': detail}) request_urls = { 'publicRequestURL': top_urls['publicRequestURL'], 'authRequestURL': top_urls['authRequestURL'], 'publicRequestDetailedURL': urls['publicRequestURL'], 'authRequestDetailedURL': urls['authRequestURL'] } return {'api_mode': api_mode, 'api_key': api_key, 'persistent_allowed': persistent_allowed, 'persistent_user_enabled': persistent_user_enabled, 'api_active': api_key is not None, 'api_key_user_agreement': tpl.get_ical_api_key_msg(), 'api_persistent_user_agreement': persistent_agreement, 'user_logged': user is not None, 'request_urls': request_urls}
def getVars( self ): from indico.web.http_api.util import generate_public_auth_request vars = WHeader.getVars( self ) vars["categurl"] = self._conf.as_event.category.url vars["conf"] = vars["target"] = self._conf vars["imgLogo"] = Config.getInstance().getSystemIconURL("miniLogo") vars["MaKaCHomeURL"] = self._conf.as_event.category.url # Default values to avoid NameError while executing the template styles = theme_settings.get_themes_for("conference") vars["viewoptions"] = [{'id': theme_id, 'name': data['title']} for theme_id, data in sorted(styles.viewitems(), key=lambda x: x[1]['title'])] vars["SelectedStyle"] = "" vars["pdfURL"] = "" vars["displayURL"] = str(urlHandlers.UHConferenceOtherViews.getURL(self._conf)) # Setting the buttons that will be displayed in the header menu vars["showFilterButton"] = False vars["showMoreButton"] = True vars["showExportToICal"] = True vars["showExportToPDF"] = False vars["showDLMaterial"] = True vars["showLayout"] = True vars["displayNavigationBar"] = layout_settings.get(self._conf, 'show_nav_bar') # This is basically the same WICalExportBase, but we need some extra # logic in order to have the detailed URLs apiMode = api_settings.get('security_mode') vars["icsIconURL"] = str(Config.getInstance().getSystemIconURL("ical_grey")) vars["apiMode"] = apiMode vars["signingEnabled"] = apiMode in {APIMode.SIGNED, APIMode.ONLYKEY_SIGNED, APIMode.ALL_SIGNED} vars["persistentAllowed"] = api_settings.get('allow_persistent') user = self._aw.getUser() apiKey = user.api_key if user else None topURLs = generate_public_auth_request(apiKey, '/export/event/%s.ics' % self._conf.getId()) urls = generate_public_auth_request(apiKey, '/export/event/%s.ics' % self._conf.getId(), {'detail': 'contributions'}) vars["requestURLs"] = { 'publicRequestURL': topURLs["publicRequestURL"], 'authRequestURL': topURLs["authRequestURL"], 'publicRequestDetailedURL': urls["publicRequestURL"], 'authRequestDetailedURL': urls["authRequestURL"] } vars["persistentUserEnabled"] = apiKey.is_persistent_allowed if apiKey else False vars["apiActive"] = apiKey is not None vars["userLogged"] = user is not None tpl = get_template_module('api/_messages.html') vars['apiKeyUserAgreement'] = tpl.get_ical_api_key_msg() vars['apiPersistentUserAgreement'] = tpl.get_ical_persistent_msg() return vars
def validateSignature(ak, signature, timestamp, path, query): ttl = api_settings.get('signature_ttl') if not timestamp and not (ak.is_persistent_allowed and api_settings.get('allow_persistent')): raise HTTPAPIError('Signature invalid (no timestamp)', 403) elif timestamp and abs(timestamp - int(time.time())) > ttl: raise HTTPAPIError('Signature invalid (bad timestamp)', 403) digest = hmac.new(ak.secret, normalizeQuery(path, query), hashlib.sha1).hexdigest() if signature != digest: raise HTTPAPIError('Signature invalid', 403)
def _process(self): key = self.user.api_key use_signatures = api_settings.get('security_mode') in {APIMode.SIGNED, APIMode.ONLYKEY_SIGNED, APIMode.ALL_SIGNED} allow_persistent = api_settings.get('allow_persistent') old_keys = self.user.old_api_keys return WPAPIUserProfile.render_template('user_profile.html', user=self.user, key=key, old_keys=old_keys, use_signatures=use_signatures, allow_persistent=allow_persistent, can_modify=(not key or not key.is_blocked or session.user.is_admin))
def generate_public_auth_request(apiKey, path, params=None): apiMode = api_settings.get('security_mode') if params is None: params = {} if apiKey: key = apiKey.token secret_key = apiKey.secret persistent = apiKey.is_persistent_allowed and api_settings.get( 'allow_persistent') else: key = secret_key = None persistent = False if api_settings.get('require_https'): baseURL = Config.getInstance().getBaseSecureURL() else: baseURL = Config.getInstance().getBaseURL() publicRequestsURL = None authRequestURL = None if apiMode == APIMode.KEY: publicRequestsURL = build_indico_request(path, params) authRequestURL = build_indico_request(path, params, key) if key else None elif apiMode == APIMode.ONLYKEY: authRequestURL = build_indico_request(path, params, key) if key else None params["onlypublic"] = "yes" publicRequestsURL = build_indico_request(path, params, key) if key else None elif apiMode == APIMode.SIGNED: publicRequestsURL = build_indico_request(path, params) authRequestURL = build_indico_request( path, params, key, secret_key, persistent) if key and secret_key else None elif apiMode == APIMode.ONLYKEY_SIGNED: publicRequestsURL = build_indico_request(path, params, key) if key else None authRequestURL = build_indico_request( path, params, key, secret_key, persistent) if key and secret_key else None elif apiMode == APIMode.ALL_SIGNED: authRequestURL = build_indico_request(path, params, key, secret_key, persistent) if key else None params["onlypublic"] = "yes" publicRequestsURL = build_indico_request(path, params, key, secret_key, persistent) if key else None return { "publicRequestURL": (baseURL + publicRequestsURL) if publicRequestsURL else "", "authRequestURL": (baseURL + authRequestURL) if authRequestURL else "" }
def _process(self): quiet = request.form.get('quiet') == '1' force = request.form.get('force') == '1' persistent = request.form.get('persistent') == '1' and api_settings.get('allow_persistent') old_key = self.user.api_key if old_key: if not force: raise BadRequest('There is already an API key for this user') if old_key.is_blocked and not session.user.is_admin: raise Forbidden old_key.is_active = False db.session.flush() key = APIKey(user=self.user) db.session.add(key) if persistent: key.is_persistent_allowed = persistent elif old_key: key.is_persistent_allowed = old_key.is_persistent_allowed if not quiet: if old_key: flash(_('Your API key has been successfully replaced.'), 'success') if old_key.use_count: flash(_('Please update any applications which use old key.'), 'warning') else: flash(_('Your API key has been successfully created.'), 'success') db.session.flush() return redirect_or_jsonify(url_for('api.user_profile'), flash=not quiet, is_persistent_allowed=key.is_persistent_allowed)
def checkAK(apiKey, signature, timestamp, path, query): apiMode = api_settings.get('security_mode') if not apiKey: if apiMode in { APIMode.ONLYKEY, APIMode.ONLYKEY_SIGNED, APIMode.ALL_SIGNED }: raise HTTPAPIError('API key is missing', 403) return None, True try: UUID(hex=apiKey) except ValueError: raise HTTPAPIError('Malformed API key', 400) ak = APIKey.find_first(token=apiKey, is_active=True) if not ak: raise HTTPAPIError('Invalid API key', 403) if ak.is_blocked: raise HTTPAPIError('API key is blocked', 403) # Signature validation onlyPublic = False if signature: validateSignature(ak, signature, timestamp, path, query) elif apiMode == APIMode.ALL_SIGNED: raise HTTPAPIError('Signature missing', 403) elif apiMode in {APIMode.SIGNED, APIMode.ONLYKEY_SIGNED}: onlyPublic = True return ak, onlyPublic
def _process(self): quiet = request.form.get('quiet') == '1' force = request.form.get('force') == '1' persistent = request.form.get( 'persistent') == '1' and api_settings.get('allow_persistent') old_key = self.user.api_key if old_key: if not force: raise BadRequest('There is already an API key for this user') if old_key.is_blocked and not session.user.is_admin: raise Forbidden old_key.is_active = False db.session.flush() key = APIKey(user=self.user) db.session.add(key) if persistent: key.is_persistent_allowed = persistent elif old_key: key.is_persistent_allowed = old_key.is_persistent_allowed if not quiet: if old_key: flash(_('Your API key has been successfully replaced.'), 'success') if old_key.use_count: flash( _('Please update any applications which use old key.'), 'warning') else: flash(_('Your API key has been successfully created.'), 'success') db.session.flush() return redirect_or_jsonify( url_for('api.user_profile'), flash=not quiet, is_persistent_allowed=key.is_persistent_allowed)
def _process(self): quiet = request.form.get('quiet') == '1' key = self.user.api_key key.is_persistent_allowed = api_settings.get('allow_persistent') and request.form['enabled'] == '1' if not quiet: if key.is_persistent_allowed: flash(_('You can now use persistent signatures.'), 'success') else: flash(_('Persistent signatures have been disabled for your API key.'), 'success') return redirect_or_jsonify(url_for('api.user_profile'), flash=not quiet, enabled=key.is_persistent_allowed)
def buildAW(ak, onlyPublic=False): aw = AccessWrapper() if ak and not onlyPublic: # If we have an authenticated request, require HTTPS # Dirty hack: Google calendar converts HTTP API requests from https to http # Therefore, not working with Indico setup (requiring https for HTTP API authenticated) if not request.is_secure and api_settings.get('require_https') and request.user_agent.browser != 'google': raise HTTPAPIError('HTTPS is required', 403) aw.setUser(ak.user.as_avatar) return aw
def buildAW(ak, onlyPublic=False): aw = AccessWrapper() if ak and not onlyPublic: # If we have an authenticated request, require HTTPS # Dirty hack: Google calendar converts HTTP API requests from https to http # Therefore, not working with Indico setup (requiring https for HTTP API authenticated) if not request.is_secure and api_settings.get( 'require_https') and request.user_agent.browser != 'google': raise HTTPAPIError('HTTPS is required', 403) aw.setUser(ak.user.as_avatar) return aw
def _getIcalExportParams(self, user, url, params=None): apiMode = api_settings.get('security_mode') apiKey = user.api_key if user else None urls = generate_public_auth_request(apiKey, url, params) tpl = get_template_module('api/_messages.html') return { 'currentUser': user, 'icsIconURL': str(Config.getInstance().getSystemIconURL("ical_grey")), 'apiMode': apiMode, 'signingEnabled': apiMode in {APIMode.SIGNED, APIMode.ONLYKEY_SIGNED, APIMode.ALL_SIGNED}, 'persistentAllowed': api_settings.get('allow_persistent'), 'requestURLs': urls, 'persistentUserEnabled': apiKey.is_persistent_allowed if apiKey else False, 'apiActive': apiKey is not None, 'userLogged': user is not None, 'apiKeyUserAgreement': tpl.get_ical_api_key_msg(), 'apiPersistentUserAgreement': tpl.get_ical_persistent_msg() }
def get_base_ical_parameters(user, event, detail, session_=None): """Returns a dict of all parameters expected by iCal template""" from indico.web.http_api.util import generate_public_auth_request api_mode = api_settings.get('security_mode') persistent_allowed = api_settings.get('allow_persistent') api_key = user.api_key if user else None persistent_user_enabled = api_key.is_persistent_allowed if api_key else None tpl = get_template_module('api/_messages.html') persistent_agreement = tpl.get_ical_persistent_msg() if session_: path = '/export/event/{0}/session/{1}.ics'.format( event.id, session_.id) else: path = '/export/event/{0}.ics'.format(event.id) top_urls = generate_public_auth_request(api_key, path) urls = generate_public_auth_request(api_key, path, {'detail': detail}) request_urls = { 'publicRequestURL': top_urls['publicRequestURL'], 'authRequestURL': top_urls['authRequestURL'], 'publicRequestDetailedURL': urls['publicRequestURL'], 'authRequestDetailedURL': urls['authRequestURL'] } return { 'api_mode': api_mode, 'api_key': api_key, 'persistent_allowed': persistent_allowed, 'persistent_user_enabled': persistent_user_enabled, 'api_active': api_key is not None, 'api_key_user_agreement': tpl.get_ical_api_key_msg(), 'api_persistent_user_agreement': persistent_agreement, 'user_logged': user is not None, 'request_urls': request_urls }
def get_base_ical_parameters(user, detail, path, params=None): """Returns a dict of all parameters expected by iCal template""" from indico.web.http_api.util import generate_public_auth_request api_mode = api_settings.get('security_mode') persistent_allowed = api_settings.get('allow_persistent') api_key = user.api_key if user else None persistent_user_enabled = api_key.is_persistent_allowed if api_key else None tpl = get_template_module('api/_messages.html') persistent_agreement = tpl.get_ical_persistent_msg() top_urls = generate_public_auth_request(api_key, path, params) urls = generate_public_auth_request(api_key, path, dict(params or {}, detail=detail)) request_urls = { 'publicRequestURL': top_urls['publicRequestURL'], 'authRequestURL': top_urls['authRequestURL'], 'publicRequestDetailedURL': urls['publicRequestURL'], 'authRequestDetailedURL': urls['authRequestURL'] } return {'api_mode': api_mode, 'api_key': api_key, 'persistent_allowed': persistent_allowed, 'persistent_user_enabled': persistent_user_enabled, 'api_active': api_key is not None, 'api_key_user_agreement': tpl.get_ical_api_key_msg(), 'api_persistent_user_agreement': persistent_agreement, 'user_logged': user is not None, 'request_urls': request_urls}
def checkAK(apiKey, signature, timestamp, path, query): apiMode = api_settings.get('security_mode') if not apiKey: if apiMode in {APIMode.ONLYKEY, APIMode.ONLYKEY_SIGNED, APIMode.ALL_SIGNED}: raise HTTPAPIError('API key is missing', 403) return None, True try: UUID(hex=apiKey) except ValueError: raise HTTPAPIError('Malformed API key', 400) ak = APIKey.find_first(token=apiKey, is_active=True) if not ak: raise HTTPAPIError('Invalid API key', 403) if ak.is_blocked: raise HTTPAPIError('API key is blocked', 403) # Signature validation onlyPublic = False if signature: validateSignature(ak, signature, timestamp, path, query) elif apiMode == APIMode.ALL_SIGNED: raise HTTPAPIError('Signature missing', 403) elif apiMode in {APIMode.SIGNED, APIMode.ONLYKEY_SIGNED}: onlyPublic = True return ak, onlyPublic
def handler(prefix, path): path = posixpath.join("/", prefix, path) ContextManager.destroy() clearCache() # init fossil cache logger = Logger.get("httpapi") if request.method == "POST": # Convert POST data to a query string queryParams = dict((key, value.encode("utf-8")) for key, value in request.form.iteritems()) query = urllib.urlencode(queryParams) else: # Parse the actual query string queryParams = dict((key, value.encode("utf-8")) for key, value in request.args.iteritems()) query = request.query_string dbi = DBMgr.getInstance() dbi.startRequest() apiKey = get_query_parameter(queryParams, ["ak", "apikey"], None) cookieAuth = get_query_parameter(queryParams, ["ca", "cookieauth"], "no") == "yes" signature = get_query_parameter(queryParams, ["signature"]) timestamp = get_query_parameter(queryParams, ["timestamp"], 0, integer=True) noCache = get_query_parameter(queryParams, ["nc", "nocache"], "no") == "yes" pretty = get_query_parameter(queryParams, ["p", "pretty"], "no") == "yes" onlyPublic = get_query_parameter(queryParams, ["op", "onlypublic"], "no") == "yes" onlyAuthed = get_query_parameter(queryParams, ["oa", "onlyauthed"], "no") == "yes" scope = "read:legacy_api" if request.method == "GET" else "write:legacy_api" try: oauth_valid, oauth_request = oauth.verify_request([scope]) if not oauth_valid and oauth_request and oauth_request.error_message != "Bearer token not found.": raise BadRequest("OAuth error: {}".format(oauth_request.error_message)) elif g.get("received_oauth_token") and oauth_request.error_message == "Bearer token not found.": raise BadRequest("OAuth error: Invalid token") except ValueError: # XXX: Dirty hack to workaround a bug in flask-oauthlib that causes it # not to properly urlencode request query strings # Related issue (https://github.com/lepture/flask-oauthlib/issues/213) oauth_valid = False # Get our handler function and its argument and response type hook, dformat = HTTPAPIHook.parseRequest(path, queryParams) if hook is None or dformat is None: raise NotFound # Disable caching if we are not just retrieving data (or the hook requires it) if request.method == "POST" or hook.NO_CACHE: noCache = True ak = error = result = None ts = int(time.time()) typeMap = {} responseUtil = ResponseUtil() try: used_session = None if cookieAuth: used_session = session if not used_session.user: # ignore guest sessions used_session = None if apiKey or oauth_valid or not used_session: if not oauth_valid: # Validate the API key (and its signature) ak, enforceOnlyPublic = checkAK(apiKey, signature, timestamp, path, query) if enforceOnlyPublic: onlyPublic = True # Create an access wrapper for the API key's user aw = buildAW(ak, onlyPublic) else: # Access Token (OAuth) at = load_token(oauth_request.access_token.access_token) aw = buildAW(at, onlyPublic) # Get rid of API key in cache key if we did not impersonate a user if ak and aw.getUser() is None: cacheKey = normalizeQuery( path, query, remove=("_", "ak", "apiKey", "signature", "timestamp", "nc", "nocache", "oa", "onlyauthed"), ) else: cacheKey = normalizeQuery( path, query, remove=("_", "signature", "timestamp", "nc", "nocache", "oa", "onlyauthed") ) if signature: # in case the request was signed, store the result under a different key cacheKey = "signed_" + cacheKey else: # We authenticated using a session cookie. if Config.getInstance().getCSRFLevel() >= 2: token = request.headers.get("X-CSRF-Token", get_query_parameter(queryParams, ["csrftoken"])) if used_session.csrf_protected and used_session.csrf_token != token: raise HTTPAPIError("Invalid CSRF token", 403) aw = AccessWrapper() if not onlyPublic: aw.setUser(used_session.avatar) userPrefix = "user-{}_".format(used_session.user.id) cacheKey = userPrefix + normalizeQuery( path, query, remove=("_", "nc", "nocache", "ca", "cookieauth", "oa", "onlyauthed", "csrftoken") ) # Bail out if the user requires authentication but is not authenticated if onlyAuthed and not aw.getUser(): raise HTTPAPIError("Not authenticated", 403) addToCache = not hook.NO_CACHE cache = GenericCache("HTTPAPI") cacheKey = RE_REMOVE_EXTENSION.sub("", cacheKey) if not noCache: obj = cache.get(cacheKey) if obj is not None: result, extra, ts, complete, typeMap = obj addToCache = False if result is None: ContextManager.set("currentAW", aw) # Perform the actual exporting res = hook(aw) if isinstance(res, tuple) and len(res) == 4: result, extra, complete, typeMap = res else: result, extra, complete, typeMap = res, {}, True, {} if result is not None and addToCache: ttl = api_settings.get("cache_ttl") cache.set(cacheKey, (result, extra, ts, complete, typeMap), ttl) except HTTPAPIError, e: error = e if e.getCode(): responseUtil.status = e.getCode() if responseUtil.status == 405: responseUtil.headers["Allow"] = "GET" if request.method == "POST" else "POST"
def handler(prefix, path): path = posixpath.join('/', prefix, path) ContextManager.destroy() clearCache() # init fossil cache logger = Logger.get('httpapi') if request.method == 'POST': # Convert POST data to a query string queryParams = [(key, [x.encode('utf-8') for x in values]) for key, values in request.form.iterlists()] query = urllib.urlencode(queryParams, doseq=1) # we only need/keep multiple values so we can properly validate the signature. # the legacy code below expects a dict with just the first value. # if you write a new api endpoint that needs multiple values get them from # ``request.values.getlist()`` directly queryParams = {key: values[0] for key, values in queryParams} else: # Parse the actual query string queryParams = dict((key, value.encode('utf-8')) for key, value in request.args.iteritems()) query = request.query_string dbi = DBMgr.getInstance() dbi.startRequest() apiKey = get_query_parameter(queryParams, ['ak', 'apikey'], None) cookieAuth = get_query_parameter(queryParams, ['ca', 'cookieauth'], 'no') == 'yes' signature = get_query_parameter(queryParams, ['signature']) timestamp = get_query_parameter(queryParams, ['timestamp'], 0, integer=True) noCache = get_query_parameter(queryParams, ['nc', 'nocache'], 'no') == 'yes' pretty = get_query_parameter(queryParams, ['p', 'pretty'], 'no') == 'yes' onlyPublic = get_query_parameter(queryParams, ['op', 'onlypublic'], 'no') == 'yes' onlyAuthed = get_query_parameter(queryParams, ['oa', 'onlyauthed'], 'no') == 'yes' scope = 'read:legacy_api' if request.method == 'GET' else 'write:legacy_api' try: oauth_valid, oauth_request = oauth.verify_request([scope]) if not oauth_valid and oauth_request and oauth_request.error_message != 'Bearer token not found.': raise BadRequest('OAuth error: {}'.format(oauth_request.error_message)) elif g.get('received_oauth_token') and oauth_request.error_message == 'Bearer token not found.': raise BadRequest('OAuth error: Invalid token') except ValueError: # XXX: Dirty hack to workaround a bug in flask-oauthlib that causes it # not to properly urlencode request query strings # Related issue (https://github.com/lepture/flask-oauthlib/issues/213) oauth_valid = False # Get our handler function and its argument and response type hook, dformat = HTTPAPIHook.parseRequest(path, queryParams) if hook is None or dformat is None: raise NotFound # Disable caching if we are not just retrieving data (or the hook requires it) if request.method == 'POST' or hook.NO_CACHE: noCache = True ak = error = result = None ts = int(time.time()) typeMap = {} responseUtil = ResponseUtil() is_response = False try: used_session = None if cookieAuth: used_session = session if not used_session.user: # ignore guest sessions used_session = None if apiKey or oauth_valid or not used_session: if not oauth_valid: # Validate the API key (and its signature) ak, enforceOnlyPublic = checkAK(apiKey, signature, timestamp, path, query) if enforceOnlyPublic: onlyPublic = True # Create an access wrapper for the API key's user aw = buildAW(ak, onlyPublic) else: # Access Token (OAuth) at = load_token(oauth_request.access_token.access_token) aw = buildAW(at, onlyPublic) # Get rid of API key in cache key if we did not impersonate a user if ak and aw.getUser() is None: cacheKey = normalizeQuery(path, query, remove=('_', 'ak', 'apiKey', 'signature', 'timestamp', 'nc', 'nocache', 'oa', 'onlyauthed')) else: cacheKey = normalizeQuery(path, query, remove=('_', 'signature', 'timestamp', 'nc', 'nocache', 'oa', 'onlyauthed')) if signature: # in case the request was signed, store the result under a different key cacheKey = 'signed_' + cacheKey else: # We authenticated using a session cookie. if Config.getInstance().getCSRFLevel() >= 2: token = request.headers.get('X-CSRF-Token', get_query_parameter(queryParams, ['csrftoken'])) if used_session.csrf_protected and used_session.csrf_token != token: raise HTTPAPIError('Invalid CSRF token', 403) aw = AccessWrapper() if not onlyPublic: aw.setUser(used_session.avatar) userPrefix = 'user-{}_'.format(used_session.user.id) cacheKey = userPrefix + normalizeQuery(path, query, remove=('_', 'nc', 'nocache', 'ca', 'cookieauth', 'oa', 'onlyauthed', 'csrftoken')) # Bail out if the user requires authentication but is not authenticated if onlyAuthed and not aw.getUser(): raise HTTPAPIError('Not authenticated', 403) addToCache = not hook.NO_CACHE cache = GenericCache('HTTPAPI') cacheKey = RE_REMOVE_EXTENSION.sub('', cacheKey) if not noCache: obj = cache.get(cacheKey) if obj is not None: result, extra, ts, complete, typeMap = obj addToCache = False if result is None: ContextManager.set("currentAW", aw) # Perform the actual exporting res = hook(aw) if isinstance(res, current_app.response_class): addToCache = False is_response = True result, extra, complete, typeMap = res, {}, True, {} elif isinstance(res, tuple) and len(res) == 4: result, extra, complete, typeMap = res else: result, extra, complete, typeMap = res, {}, True, {} if result is not None and addToCache: ttl = api_settings.get('cache_ttl') if ttl > 0: cache.set(cacheKey, (result, extra, ts, complete, typeMap), ttl) except HTTPAPIError, e: error = e if e.getCode(): responseUtil.status = e.getCode() if responseUtil.status == 405: responseUtil.headers['Allow'] = 'GET' if request.method == 'POST' else 'POST'
def handler(prefix, path): path = posixpath.join('/', prefix, path) ContextManager.destroy() clearCache() # init fossil cache logger = Logger.get('httpapi') if request.method == 'POST': # Convert POST data to a query string queryParams = [(key, [x.encode('utf-8') for x in values]) for key, values in request.form.iterlists()] query = urllib.urlencode(queryParams, doseq=1) # we only need/keep multiple values so we can properly validate the signature. # the legacy code below expects a dict with just the first value. # if you write a new api endpoint that needs multiple values get them from # ``request.values.getlist()`` directly queryParams = {key: values[0] for key, values in queryParams} else: # Parse the actual query string queryParams = dict((key, value.encode('utf-8')) for key, value in request.args.iteritems()) query = request.query_string dbi = DBMgr.getInstance() dbi.startRequest() apiKey = get_query_parameter(queryParams, ['ak', 'apikey'], None) cookieAuth = get_query_parameter(queryParams, ['ca', 'cookieauth'], 'no') == 'yes' signature = get_query_parameter(queryParams, ['signature']) timestamp = get_query_parameter(queryParams, ['timestamp'], 0, integer=True) noCache = get_query_parameter(queryParams, ['nc', 'nocache'], 'no') == 'yes' pretty = get_query_parameter(queryParams, ['p', 'pretty'], 'no') == 'yes' onlyPublic = get_query_parameter(queryParams, ['op', 'onlypublic'], 'no') == 'yes' onlyAuthed = get_query_parameter(queryParams, ['oa', 'onlyauthed'], 'no') == 'yes' scope = 'read:legacy_api' if request.method == 'GET' else 'write:legacy_api' try: oauth_valid, oauth_request = oauth.verify_request([scope]) if not oauth_valid and oauth_request and oauth_request.error_message != 'Bearer token not found.': raise BadRequest('OAuth error: {}'.format( oauth_request.error_message)) elif g.get( 'received_oauth_token' ) and oauth_request.error_message == 'Bearer token not found.': raise BadRequest('OAuth error: Invalid token') except ValueError: # XXX: Dirty hack to workaround a bug in flask-oauthlib that causes it # not to properly urlencode request query strings # Related issue (https://github.com/lepture/flask-oauthlib/issues/213) oauth_valid = False # Get our handler function and its argument and response type hook, dformat = HTTPAPIHook.parseRequest(path, queryParams) if hook is None or dformat is None: raise NotFound # Disable caching if we are not just retrieving data (or the hook requires it) if request.method == 'POST' or hook.NO_CACHE: noCache = True ak = error = result = None ts = int(time.time()) typeMap = {} responseUtil = ResponseUtil() is_response = False try: used_session = None if cookieAuth: used_session = session if not used_session.user: # ignore guest sessions used_session = None if apiKey or oauth_valid or not used_session: if not oauth_valid: # Validate the API key (and its signature) ak, enforceOnlyPublic = checkAK(apiKey, signature, timestamp, path, query) if enforceOnlyPublic: onlyPublic = True # Create an access wrapper for the API key's user aw = buildAW(ak, onlyPublic) else: # Access Token (OAuth) at = load_token(oauth_request.access_token.access_token) aw = buildAW(at, onlyPublic) # Get rid of API key in cache key if we did not impersonate a user if ak and aw.getUser() is None: cacheKey = normalizeQuery( path, query, remove=('_', 'ak', 'apiKey', 'signature', 'timestamp', 'nc', 'nocache', 'oa', 'onlyauthed')) else: cacheKey = normalizeQuery(path, query, remove=('_', 'signature', 'timestamp', 'nc', 'nocache', 'oa', 'onlyauthed')) if signature: # in case the request was signed, store the result under a different key cacheKey = 'signed_' + cacheKey else: # We authenticated using a session cookie. if Config.getInstance().getCSRFLevel() >= 2: token = request.headers.get( 'X-CSRF-Token', get_query_parameter(queryParams, ['csrftoken'])) if used_session.csrf_protected and used_session.csrf_token != token: raise HTTPAPIError('Invalid CSRF token', 403) aw = AccessWrapper() if not onlyPublic: aw.setUser(used_session.avatar) userPrefix = 'user-{}_'.format(used_session.user.id) cacheKey = userPrefix + normalizeQuery( path, query, remove=('_', 'nc', 'nocache', 'ca', 'cookieauth', 'oa', 'onlyauthed', 'csrftoken')) # Bail out if the user requires authentication but is not authenticated if onlyAuthed and not aw.getUser(): raise HTTPAPIError('Not authenticated', 403) addToCache = not hook.NO_CACHE cache = GenericCache('HTTPAPI') cacheKey = RE_REMOVE_EXTENSION.sub('', cacheKey) if not noCache: obj = cache.get(cacheKey) if obj is not None: result, extra, ts, complete, typeMap = obj addToCache = False if result is None: ContextManager.set("currentAW", aw) # Perform the actual exporting res = hook(aw) if isinstance(res, current_app.response_class): addToCache = False is_response = True result, extra, complete, typeMap = res, {}, True, {} elif isinstance(res, tuple) and len(res) == 4: result, extra, complete, typeMap = res else: result, extra, complete, typeMap = res, {}, True, {} if result is not None and addToCache: ttl = api_settings.get('cache_ttl') if ttl > 0: cache.set(cacheKey, (result, extra, ts, complete, typeMap), ttl) except HTTPAPIError, e: error = e if e.getCode(): responseUtil.status = e.getCode() if responseUtil.status == 405: responseUtil.headers[ 'Allow'] = 'GET' if request.method == 'POST' else 'POST'
def handler(prefix, path): path = posixpath.join('/', prefix, path) ContextManager.destroy() clearCache() # init fossil cache logger = Logger.get('httpapi') if request.method == 'POST': # Convert POST data to a query string queryParams = dict((key, value.encode('utf-8')) for key, value in request.form.iteritems()) query = urllib.urlencode(queryParams) else: # Parse the actual query string queryParams = dict((key, value.encode('utf-8')) for key, value in request.args.iteritems()) query = request.query_string dbi = DBMgr.getInstance() dbi.startRequest() apiKey = get_query_parameter(queryParams, ['ak', 'apikey'], None) cookieAuth = get_query_parameter(queryParams, ['ca', 'cookieauth'], 'no') == 'yes' signature = get_query_parameter(queryParams, ['signature']) timestamp = get_query_parameter(queryParams, ['timestamp'], 0, integer=True) noCache = get_query_parameter(queryParams, ['nc', 'nocache'], 'no') == 'yes' pretty = get_query_parameter(queryParams, ['p', 'pretty'], 'no') == 'yes' onlyPublic = get_query_parameter(queryParams, ['op', 'onlypublic'], 'no') == 'yes' onlyAuthed = get_query_parameter(queryParams, ['oa', 'onlyauthed'], 'no') == 'yes' oauthToken = 'oauth_token' in queryParams # Check if OAuth data is supplied in the Authorization header if not oauthToken and request.headers.get('Authorization') is not None: oauthToken = 'oauth_token' in request.headers.get('Authorization') # Get our handler function and its argument and response type hook, dformat = HTTPAPIHook.parseRequest(path, queryParams) if hook is None or dformat is None: raise NotFound # Disable caching if we are not just retrieving data (or the hook requires it) if request.method == 'POST' or hook.NO_CACHE: noCache = True ak = error = result = None ts = int(time.time()) typeMap = {} responseUtil = ResponseUtil() try: used_session = None if cookieAuth: used_session = session if not used_session.avatar: # ignore guest sessions used_session = None if apiKey or oauthToken or not used_session: if not oauthToken: # Validate the API key (and its signature) ak, enforceOnlyPublic = checkAK(apiKey, signature, timestamp, path, query) if enforceOnlyPublic: onlyPublic = True # Create an access wrapper for the API key's user aw = buildAW(ak, onlyPublic) else: # Access Token (OAuth) at = OAuthUtils.OAuthCheckAccessResource() aw = buildAW(at, onlyPublic) # Get rid of API key in cache key if we did not impersonate a user if ak and aw.getUser() is None: cacheKey = normalizeQuery(path, query, remove=('_', 'ak', 'apiKey', 'signature', 'timestamp', 'nc', 'nocache', 'oa', 'onlyauthed')) else: cacheKey = normalizeQuery(path, query, remove=('_', 'signature', 'timestamp', 'nc', 'nocache', 'oa', 'onlyauthed')) if signature: # in case the request was signed, store the result under a different key cacheKey = 'signed_' + cacheKey else: # We authenticated using a session cookie. if Config.getInstance().getCSRFLevel() >= 2: token = request.headers.get('X-CSRF-Token', get_query_parameter(queryParams, ['csrftoken'])) if used_session.csrf_protected and used_session.csrf_token != token: raise HTTPAPIError('Invalid CSRF token', 403) aw = AccessWrapper() if not onlyPublic: aw.setUser(used_session.avatar) userPrefix = 'user-' + used_session.avatar.getId() + '_' cacheKey = userPrefix + normalizeQuery(path, query, remove=('_', 'nc', 'nocache', 'ca', 'cookieauth', 'oa', 'onlyauthed', 'csrftoken')) # Bail out if the user requires authentication but is not authenticated if onlyAuthed and not aw.getUser(): raise HTTPAPIError('Not authenticated', 403) addToCache = not hook.NO_CACHE cache = GenericCache('HTTPAPI') cacheKey = RE_REMOVE_EXTENSION.sub('', cacheKey) if not noCache: obj = cache.get(cacheKey) if obj is not None: result, extra, ts, complete, typeMap = obj addToCache = False if result is None: ContextManager.set("currentAW", aw) # Perform the actual exporting res = hook(aw) if isinstance(res, tuple) and len(res) == 4: result, extra, complete, typeMap = res else: result, extra, complete, typeMap = res, {}, True, {} if result is not None and addToCache: ttl = api_settings.get('cache_ttl') cache.set(cacheKey, (result, extra, ts, complete, typeMap), ttl) except HTTPAPIError, e: error = e if e.getCode(): responseUtil.status = e.getCode() if responseUtil.status == 405: responseUtil.headers['Allow'] = 'GET' if request.method == 'POST' else 'POST'
def handler(prefix, path): path = posixpath.join('/', prefix, path) ContextManager.destroy() clearCache() # init fossil cache logger = Logger.get('httpapi') if request.method == 'POST': # Convert POST data to a query string queryParams = dict((key, value.encode('utf-8')) for key, value in request.form.iteritems()) query = urllib.urlencode(queryParams) else: # Parse the actual query string queryParams = dict((key, value.encode('utf-8')) for key, value in request.args.iteritems()) query = request.query_string dbi = DBMgr.getInstance() dbi.startRequest() apiKey = get_query_parameter(queryParams, ['ak', 'apikey'], None) cookieAuth = get_query_parameter(queryParams, ['ca', 'cookieauth'], 'no') == 'yes' signature = get_query_parameter(queryParams, ['signature']) timestamp = get_query_parameter(queryParams, ['timestamp'], 0, integer=True) noCache = get_query_parameter(queryParams, ['nc', 'nocache'], 'no') == 'yes' pretty = get_query_parameter(queryParams, ['p', 'pretty'], 'no') == 'yes' onlyPublic = get_query_parameter(queryParams, ['op', 'onlypublic'], 'no') == 'yes' onlyAuthed = get_query_parameter(queryParams, ['oa', 'onlyauthed'], 'no') == 'yes' oauthToken = 'oauth_token' in queryParams # Check if OAuth data is supplied in the Authorization header if not oauthToken and request.headers.get('Authorization') is not None: oauthToken = 'oauth_token' in request.headers.get('Authorization') # Get our handler function and its argument and response type hook, dformat = HTTPAPIHook.parseRequest(path, queryParams) if hook is None or dformat is None: raise NotFound # Disable caching if we are not just retrieving data (or the hook requires it) if request.method == 'POST' or hook.NO_CACHE: noCache = True ak = error = result = None ts = int(time.time()) typeMap = {} responseUtil = ResponseUtil() try: used_session = None if cookieAuth: used_session = session if not used_session.avatar: # ignore guest sessions used_session = None if apiKey or oauthToken or not used_session: if not oauthToken: # Validate the API key (and its signature) ak, enforceOnlyPublic = checkAK(apiKey, signature, timestamp, path, query) if enforceOnlyPublic: onlyPublic = True # Create an access wrapper for the API key's user aw = buildAW(ak, onlyPublic) else: # Access Token (OAuth) at = OAuthUtils.OAuthCheckAccessResource() aw = buildAW(at, onlyPublic) # Get rid of API key in cache key if we did not impersonate a user if ak and aw.getUser() is None: cacheKey = normalizeQuery( path, query, remove=('_', 'ak', 'apiKey', 'signature', 'timestamp', 'nc', 'nocache', 'oa', 'onlyauthed')) else: cacheKey = normalizeQuery(path, query, remove=('_', 'signature', 'timestamp', 'nc', 'nocache', 'oa', 'onlyauthed')) if signature: # in case the request was signed, store the result under a different key cacheKey = 'signed_' + cacheKey else: # We authenticated using a session cookie. if Config.getInstance().getCSRFLevel() >= 2: token = request.headers.get( 'X-CSRF-Token', get_query_parameter(queryParams, ['csrftoken'])) if used_session.csrf_protected and used_session.csrf_token != token: raise HTTPAPIError('Invalid CSRF token', 403) aw = AccessWrapper() if not onlyPublic: aw.setUser(used_session.avatar) userPrefix = 'user-' + used_session.avatar.getId() + '_' cacheKey = userPrefix + normalizeQuery( path, query, remove=('_', 'nc', 'nocache', 'ca', 'cookieauth', 'oa', 'onlyauthed', 'csrftoken')) # Bail out if the user requires authentication but is not authenticated if onlyAuthed and not aw.getUser(): raise HTTPAPIError('Not authenticated', 403) addToCache = not hook.NO_CACHE cache = GenericCache('HTTPAPI') cacheKey = RE_REMOVE_EXTENSION.sub('', cacheKey) if not noCache: obj = cache.get(cacheKey) if obj is not None: result, extra, ts, complete, typeMap = obj addToCache = False if result is None: ContextManager.set("currentAW", aw) # Perform the actual exporting res = hook(aw) if isinstance(res, tuple) and len(res) == 4: result, extra, complete, typeMap = res else: result, extra, complete, typeMap = res, {}, True, {} if result is not None and addToCache: ttl = api_settings.get('cache_ttl') cache.set(cacheKey, (result, extra, ts, complete, typeMap), ttl) except HTTPAPIError, e: error = e if e.getCode(): responseUtil.status = e.getCode() if responseUtil.status == 405: responseUtil.headers[ 'Allow'] = 'GET' if request.method == 'POST' else 'POST'
def getVars(self): from indico.web.http_api.util import generate_public_auth_request vars = WHeader.getVars(self) vars["categurl"] = self._conf.as_event.category.url vars["conf"] = vars["target"] = self._conf vars["imgLogo"] = Config.getInstance().getSystemIconURL("miniLogo") vars["MaKaCHomeURL"] = self._conf.as_event.category.url # Default values to avoid NameError while executing the template styles = theme_settings.get_themes_for("conference") vars["viewoptions"] = [{ 'id': theme_id, 'name': data['title'] } for theme_id, data in sorted(styles.viewitems(), key=lambda x: x[1]['title'])] vars["SelectedStyle"] = "" vars["pdfURL"] = "" vars["displayURL"] = str( urlHandlers.UHConferenceOtherViews.getURL(self._conf)) # Setting the buttons that will be displayed in the header menu vars["showFilterButton"] = False vars["showMoreButton"] = True vars["showExportToICal"] = True vars["showExportToPDF"] = False vars["showDLMaterial"] = True vars["showLayout"] = True vars["displayNavigationBar"] = layout_settings.get( self._conf, 'show_nav_bar') # This is basically the same WICalExportBase, but we need some extra # logic in order to have the detailed URLs apiMode = api_settings.get('security_mode') vars["icsIconURL"] = str( Config.getInstance().getSystemIconURL("ical_grey")) vars["apiMode"] = apiMode vars["signingEnabled"] = apiMode in { APIMode.SIGNED, APIMode.ONLYKEY_SIGNED, APIMode.ALL_SIGNED } vars["persistentAllowed"] = api_settings.get('allow_persistent') user = self._aw.getUser() apiKey = user.api_key if user else None topURLs = generate_public_auth_request( apiKey, '/export/event/%s.ics' % self._conf.getId()) urls = generate_public_auth_request( apiKey, '/export/event/%s.ics' % self._conf.getId(), {'detail': 'contributions'}) vars["requestURLs"] = { 'publicRequestURL': topURLs["publicRequestURL"], 'authRequestURL': topURLs["authRequestURL"], 'publicRequestDetailedURL': urls["publicRequestURL"], 'authRequestDetailedURL': urls["authRequestURL"] } vars[ "persistentUserEnabled"] = apiKey.is_persistent_allowed if apiKey else False vars["apiActive"] = apiKey is not None vars["userLogged"] = user is not None tpl = get_template_module('api/_messages.html') vars['apiKeyUserAgreement'] = tpl.get_ical_api_key_msg() vars['apiPersistentUserAgreement'] = tpl.get_ical_persistent_msg() return vars