def auth_hash(req, verify=False): if not 'authNonce' in req: if verify: util.raise_not_found('authNonce: missing field in request') r = random.SystemRandom() req.authNonce = ''.join( r.choice(_AUTH_NONCE_CHARS) for x in range(_AUTH_NONCE_LEN)) h = hashlib.sha256() h.update( # POSIT: ':' not part of simulationType or simulationId ':'.join([ req.authNonce, req.simulationType, req.simulationId, cfg.auth_secret, ]), ) res = 'v1:' + base64.urlsafe_b64encode(h.digest()) if not verify: req.authHash = res return if res != req.authHash: util.raise_not_found( '{}: hash mismatch expected={} nonce={}', req.authHash, res, req.authNonce, )
def api_root(simulation_type): try: sirepo.template.assert_sim_type(simulation_type) except AssertionError: if simulation_type == 'warp': return http_reply.gen_redirect_for_root('warppba', code=301) if simulation_type == 'fete': return http_reply.gen_redirect_for_root('warpvnd', code=301) pkdlog('{}: uri not found', simulation_type) util.raise_not_found('Invalid simulation_type: {}', simulation_type) values = pkcollections.Dict() values.app_name = simulation_type return _render_root_page('index', values)
def open_json_file(sim_type, path=None, sid=None, fixup=True): """Read a db file and return result Args: sim_type (str): simulation type (app) path (py.path.local): where to read the file sid (str): simulation id Returns: dict: data Raises: CopyRedirect: if the simulation is in another user's """ if not path: path = sim_data_file(sim_type, sid) if not os.path.isfile(str(path)): global_sid = None if sid: #TODO(robnagler) workflow should be in server.py, # because only valid in one case, not e.g. for opening examples # which are not found. user_copy_sid = _find_user_simulation_copy(sim_type, sid) if find_global_simulation(sim_type, sid): global_sid = sid if global_sid: raise CopyRedirect({ 'redirect': { 'simulationId': global_sid, 'userCopySimulationId': user_copy_sid, }, }) util.raise_not_found( '{}/{}: global simulation not found', sim_type, sid, ) data = None try: with open(str(path)) as f: data = json_load(f) # ensure the simulationId matches the path if sid: data['models']['simulation']['simulationId'] = _sid_from_path( path) except Exception as e: pkdlog('{}: error: {}', path, pkdexc()) raise return fixup_old_data(data)[0] if fixup else data
def open_json_file(sim_type, path=None, sid=None, fixup=True): """Read a db file and return result Args: sim_type (str): simulation type (app) path (py.path.local): where to read the file sid (str): simulation id Returns: dict: data Raises: CopyRedirect: if the simulation is in another user's """ if not path: path = sim_data_file(sim_type, sid) if not os.path.isfile(str(path)): global_sid = None if sid: #TODO(robnagler) workflow should be in server.py, # because only valid in one case, not e.g. for opening examples # which are not found. user_copy_sid = _find_user_simulation_copy(sim_type, sid) if find_global_simulation(sim_type, sid): global_sid = sid if global_sid: raise CopyRedirect({ 'redirect': { 'simulationId': global_sid, 'userCopySimulationId': user_copy_sid, }, }) util.raise_not_found( '{}/{}: global simulation not found', sim_type, sid, ) data = None try: with open(str(path)) as f: data = json_load(f) # ensure the simulationId matches the path if sid: data['models']['simulation']['simulationId'] = _sid_from_path(path) except Exception as e: pkdlog('{}: error: {}', path, pkdexc()) raise return fixup_old_data(data)[0] if fixup else data
def api_staticFile(path_info=None): """flask.send_from_directory for static folder. Uses safe_join which doesn't allow indexing, paths with '..', or absolute paths. Args: path_info (str): relative path to join Returns: flask.Response: flask.send_from_directory response """ if not path_info: raise util.raise_not_found('empty path info') p = pkio.py_path( flask.safe_join(str(simulation_db.STATIC_FOLDER), path_info)) r = None if _google_tag_manager and re.match(r'^en/[^/]+html$', path_info): return http_reply.headers_for_cache( flask.make_response( _google_tag_manager_re.sub( _google_tag_manager, pkio.read_text(p), ), ), path=p, ) if re.match(r'^html/[^/]+html$', path_info): return http_reply.render_html(p) return flask.send_file(p, conditional=True)
def auth_login(req): from sirepo import server if cfg.auth_secret: auth_hash(req, verify=True) # DEPRECATED elif not server.cfg.enable_bluesky: util.raise_not_found('bluesky is not enabled') sid = req.simulationId sim_type = req.simulationType path = simulation_db.find_global_simulation( sim_type, sid, checked=True, ) server.session_user(simulation_db.uid_from_dir_name(path))
def find_global_simulation(sim_type, sid, checked=False): paths = pkio.sorted_glob(user_dir_name().join('*', sim_type, sid)) if len(paths) == 1: return str(paths[0]) if len(paths) == 0: if checked: util.raise_not_found( '{}/{}: global simulation not found', sim_type, sid, ) return None util.raise_not_found( '{}: more than one path found for simulation={}/{}', paths, sim_type, sid, )
def _dispatch(path): """Called by Flask and routes the base_uri with parameters Args: path (str): what to route Returns: Flask.response """ cookie.init() try: if path is None: return _dispatch_call(_empty_route.func, {}) # werkzeug doesn't convert '+' to ' ' parts = re.sub(r'\+', ' ', path).split('/') try: route = _uri_to_route[parts[0]] parts.pop(0) except KeyError: route = _default_route kwargs = pkcollections.Dict() for p in route.params: if not parts: if not p.is_optional: raise NotFound('{}: uri missing parameter ({})', path, p.name) break if p.is_path_info: kwargs[p.name] = '/'.join(parts) parts = None break kwargs[p.name] = parts.pop(0) if parts: raise NotFound('{}: unknown parameters in uri ({})', parts, path) return _dispatch_call(route.func, kwargs) except NotFound as e: util.raise_not_found(e.log_fmt, *e.args, **e.kwargs) except Exception as e: pkdlog('{}: error: {}', path, pkdexc()) raise
def api_findByName(simulation_type, application_mode, simulation_name): sim_type = sirepo.template.assert_sim_type(simulation_type) # use the existing named simulation, or copy it from the examples rows = simulation_db.iterate_simulation_datafiles( sim_type, simulation_db.process_simulation_list, { 'simulation.name': simulation_name, 'simulation.isExample': True, }, ) if len(rows) == 0: for s in simulation_db.examples(sim_type): if s['models']['simulation']['name'] != simulation_name: continue simulation_db.save_new_example(s) rows = simulation_db.iterate_simulation_datafiles( sim_type, simulation_db.process_simulation_list, { 'simulation.name': simulation_name, }, ) break else: util.raise_not_found( 'simulation not found by name={} type={}', simulation_name, sim_type, ) # format the uri for the local route to this simulation for application_mode s = simulation_db.get_schema(sim_type) m = s.appModes[application_mode] r = m.localRoute assert r in s.localRoutes u = '/{}#/{}/{}'.format(sim_type, r, rows[0].simulationId) if m.includeMode: u += '?application_mode={}'.format(application_mode) return http_reply.gen_redirect_for_anchor(u)
def api_findByName(simulation_type, application_mode, simulation_name): # use the existing named simulation, or copy it from the examples rows = simulation_db.iterate_simulation_datafiles(simulation_type, simulation_db.process_simulation_list, { 'simulation.name': simulation_name, 'simulation.isExample': True, }) if len(rows) == 0: for s in simulation_db.examples(simulation_type): if s['models']['simulation']['name'] == simulation_name: simulation_db.save_new_example(s) rows = simulation_db.iterate_simulation_datafiles(simulation_type, simulation_db.process_simulation_list, { 'simulation.name': simulation_name, }) break else: util.raise_not_found('{}: simulation not found by name: {}', simulation_type, simulation_name) return javascript_redirect( uri_router.format_uri( simulation_type, application_mode, rows[0]['simulationId'], simulation_db.get_schema(simulation_type) ) )
def auth_hash(req, verify=False): now = int(time.time()) if not 'authNonce' in req: if verify: util.raise_not_found('authNonce: missing field in request') r = random.SystemRandom() req.authNonce = str(now) + _AUTH_NONCE_SEPARATOR + ''.join( r.choice(_AUTH_NONCE_CHARS) for x in range(_AUTH_NONCE_UNIQUE_LEN)) h = hashlib.sha256() h.update( _AUTH_HASH_SEPARATOR.join([ req.authNonce, req.simulationType, req.simulationId, cfg.auth_secret, ]), ) res = 'v1:' + base64.urlsafe_b64encode(h.digest()) if not verify: req.authHash = res return if res != req.authHash: util.raise_not_found( '{}: hash mismatch expected={} nonce={}', req.authHash, res, req.authNonce, ) t = req.authNonce.split(_AUTH_NONCE_SEPARATOR)[0] try: t = int(t) except ValueError as e: util.raise_not_found( '{}: auth_nonce prefix not an int: nonce={}', t, req.authNonce, ) delta = now - t if abs(delta) > _AUTH_NONCE_REPLAY_SECS: util.raise_not_found( '{}: auth_nonce time outside replay window={} now={} nonce={}', t, _AUTH_NONCE_REPLAY_SECS, now, req.authNonce, )
def auth_hash(req, verify=False): now = int(time.time()) if not 'authNonce' in req: if verify: util.raise_not_found('authNonce: missing field in request') req.authNonce = str(now) + _AUTH_NONCE_SEPARATOR + util.random_base62() h = hashlib.sha256() h.update( _AUTH_HASH_SEPARATOR.join([ req.authNonce, req.simulationType, req.simulationId, cfg.auth_secret, ]), ) res = 'v1:' + base64.urlsafe_b64encode(h.digest()) if not verify: req.authHash = res return if res != req.authHash: util.raise_not_found( '{}: hash mismatch expected={} nonce={}', req.authHash, res, req.authNonce, ) t = req.authNonce.split(_AUTH_NONCE_SEPARATOR)[0] try: t = int(t) except ValueError as e: util.raise_not_found( '{}: auth_nonce prefix not an int: nonce={}', t, req.authNonce, ) delta = now - t if abs(delta) > _AUTH_NONCE_REPLAY_SECS: util.raise_not_found( '{}: auth_nonce time outside replay window={} now={} nonce={}', t, _AUTH_NONCE_REPLAY_SECS, now, req.authNonce, )