def get(): app.logger.info(f"GET Maps, actor={g.user}") actor = get_authn_username() return rest_common.get_publications(MAP_TYPE, actor, request_args=request.args)
def get(): app.logger.info(f"GET Layers, user={g.user}") user = get_authn_username() return rest_common.get_publications(LAYER_TYPE, user, request_args=request.args)
def get(workspace): app.logger.info(f"GET Maps, user={g.user}") user = get_authn_username() return rest_common.get_publications(MAP_TYPE, user, request_args=request.args, workspace=workspace)
def get(workspace): app.logger.info(f"GET Layers, actor={g.user}") actor = get_authn_username() return rest_common.get_publications(LAYER_TYPE, actor, request_args=request.args, workspace=workspace)
def proxy(subpath): app.logger.info( f"{request.method} GeoServer proxy, user={g.user}, subpath={subpath}, url={request.url}, request.query_string={request.query_string.decode('UTF-8')}" ) url = settings.LAYMAN_GS_URL + subpath + '?' + request.query_string.decode( 'UTF-8') headers_req = { key.lower(): value for (key, value) in request.headers if key.lower() not in ['host', settings.LAYMAN_GS_AUTHN_HTTP_HEADER_ATTRIBUTE.lower()] } data = request.get_data() authn_username = authn.get_authn_username() if is_user_with_name(authn_username): headers_req[ settings.LAYMAN_GS_AUTHN_HTTP_HEADER_ATTRIBUTE] = authn_username app.logger.info( f"{request.method} GeoServer proxy, headers_req={headers_req}, url={url}" ) wfs_t_layers = set() if data is not None and len(data) > 0: try: wfs_t_attribs, wfs_t_layers = extract_attributes_and_layers_from_wfs_t( data) if wfs_t_attribs: ensure_wfs_t_attributes(wfs_t_attribs) except BaseException as err: app.logger.warning( f"WFS Proxy: error={err}, trace={traceback.format_exc()}") response = requests.request(method=request.method, url=url, data=data, headers=headers_req, cookies=request.cookies, allow_redirects=False) if response.status_code == 200: for workspace, layername in wfs_t_layers: if authz.can_i_edit(LAYER_TYPE, workspace, layername): patch_after_feature_change(workspace, layername) excluded_headers = [ 'content-encoding', 'content-length', 'transfer-encoding', 'connection' ] headers = { key: value for (key, value) in response.headers.items() if key.lower() not in excluded_headers } final_response = Response(response.content, response.status_code, headers) return final_response
def delete_publications( user, publ_type, is_chain_ready_fn, abort_publication_fn, delete_publication_fn, method, url_path, publ_param, ): from layman import authn actor_name = authn.get_authn_username() whole_infos = get_publication_infos(user, publ_type, { 'actor_name': actor_name, 'access_type': 'write' }) for (_, _, publication) in whole_infos.keys(): redis.create_lock(user, publ_type, publication, method) try: abort_publication_fn(user, publication) delete_publication_fn(user, publication) if is_chain_ready_fn(user, publication): redis.unlock_publication(user, publ_type, publication) except Exception as exc: try: if is_chain_ready_fn(user, publication): redis.unlock_publication(user, publ_type, publication) finally: redis.unlock_publication(user, publ_type, publication) raise exc infos = [{ 'name': info["name"], 'title': info.get("title", None), 'url': url_for(**{ 'endpoint': url_path, publ_param: name, 'workspace': user }), 'uuid': info["uuid"], 'access_rights': info['access_rights'], } for (name, info) in whole_infos.items()] return jsonify(infos)
def decorated_function(*args, **kwargs): # print(f"authorize ARGS {args} KWARGS {kwargs}") req_path = request.script_root + request.path (workspace, publication_type, publication_name) = parse_request_path(req_path) if publication_type is None or workspace or publication_name: raise Exception( f"Authorization module is unable to authorize path {req_path}") actor_name = authn.get_authn_username() if request.method.lower() == common.REQUEST_METHOD_GET: # pylint: disable=unused-variable @after_this_request def authorize_after_request_tmp(response): return authorize_after_multi_get_request(actor_name, response) return func(*args, **kwargs)
def reserve_username(username, adjust=False): current_username = authn.get_authn_username() if is_user_with_name(current_username): raise LaymanError(34, {'username': current_username}) if adjust is not True: check_workspace_name(username) workspaces = get_workspaces() if username in workspaces: raise LaymanError(35) try: ensure_whole_user(username) claims = get_open_id_claims() _save_reservation(username, claims) except LaymanError as exc: delete_whole_user(username) raise exc return claims = get_open_id_claims() suggestions = [username] + get_username_suggestions_from_claims(claims) suggestions = [ slugify(s) for s in suggestions if s is not None and len(s) > 0 ] suggestions = to_safe_names(suggestions, 'user') workspaces = get_workspaces() username = None idx = 0 while True: for suggestion in suggestions: if idx > 0: suggestion = f"{suggestion}{idx}" try: check_workspace_name(suggestion) except LaymanError as exc: if not (exc.code == 2 or exc.code == 35): raise exc if suggestion in workspaces: continue try: ensure_whole_user(suggestion) username = suggestion _save_reservation(username, claims) break except LaymanError: delete_whole_user(suggestion) if username is not None: break idx += 1
def patch(workspace, layername): app.logger.info(f"PATCH Layer, actor={g.user}") info = util.get_complete_layer_info(cached=True) kwargs = { 'title': info.get('title', info['name']) or '', 'description': info.get('description', '') or '', } # FILE sent_file_streams = [] sent_file_paths = [] if 'file' in request.files: sent_file_streams = [ f for f in request.files.getlist("file") if len(f.filename) > 0 ] if len(sent_file_streams) == 0 and len(request.form.getlist('file')) > 0: sent_file_paths = [ filename for filename in request.form.getlist('file') if len(filename) > 0 ] input_files = fs_util.InputFiles(sent_streams=sent_file_streams, sent_paths=sent_file_paths) # CRS crs_id = None if len(input_files.raw_paths) > 0 and len(request.form.get('crs', '')) > 0: crs_id = request.form['crs'] if crs_id not in settings.INPUT_SRS_LIST: raise LaymanError(2, { 'parameter': 'crs', 'supported_values': settings.INPUT_SRS_LIST }) check_crs = crs_id is None # TITLE if len(request.form.get('title', '')) > 0: kwargs['title'] = request.form['title'] # DESCRIPTION if len(request.form.get('description', '')) > 0: kwargs['description'] = request.form['description'] # SLD style_file = None if 'style' in request.files and not request.files['style'].filename == '': style_file = request.files['style'] elif 'sld' in request.files and not request.files['sld'].filename == '': style_file = request.files['sld'] delete_from = None style_type = None if style_file: style_type = input_style.get_style_type_from_file_storage(style_file) kwargs['style_type'] = style_type kwargs['store_in_geoserver'] = style_type.store_in_geoserver delete_from = 'layman.layer.qgis.wms' if len(input_files.raw_paths) > 0: delete_from = 'layman.layer.filesystem.input_file' # FILE NAMES use_chunk_upload = bool(input_files.sent_paths) if delete_from == 'layman.layer.filesystem.input_file': if not (use_chunk_upload and input_files.is_one_archive): input_file.check_filenames(workspace, layername, input_files, check_crs, ignore_existing_files=True) # file checks if not use_chunk_upload: temp_dir = tempfile.mkdtemp(prefix="layman_") input_file.save_layer_files(workspace, layername, input_files, check_crs, output_dir=temp_dir) if input_files.raw_paths: file_type = input_file.get_file_type( input_files.raw_or_archived_main_file_path) else: file_type = layman_util.get_publication_info(workspace, LAYER_TYPE, layername, context={ 'keys': ['file'] })['file']['file_type'] if style_type: style_type_for_check = style_type.code else: style_type_for_check = layman_util.get_publication_info( workspace, LAYER_TYPE, layername, context={'keys': ['style_type']})['style_type'] if file_type == settings.FILE_TYPE_RASTER and style_type_for_check == 'qml': raise LaymanError(48, f'Raster layers are not allowed to have QML style.') props_to_refresh = util.get_same_or_missing_prop_names( workspace, layername) kwargs['metadata_properties_to_refresh'] = props_to_refresh layer_result = {} if delete_from is not None: request_method = request.method.lower() deleted = util.delete_layer(workspace, layername, source=delete_from, http_method=request_method) if style_file is None: try: style_file = deleted['style']['file'] except KeyError: pass style_type = input_style.get_style_type_from_file_storage(style_file) kwargs['style_type'] = style_type kwargs['store_in_geoserver'] = style_type.store_in_geoserver if style_file: input_style.save_layer_file(workspace, layername, style_file, style_type) kwargs.update({ 'crs_id': crs_id, 'http_method': request_method, 'metadata_properties_to_refresh': props_to_refresh, }) if delete_from == 'layman.layer.filesystem.input_file': if use_chunk_upload: files_to_upload = input_chunk.save_layer_files_str( workspace, layername, input_files, check_crs) layer_result.update({ 'files_to_upload': files_to_upload, }) kwargs.update({ 'check_crs': check_crs, }) else: shutil.move( temp_dir, input_file.get_layer_input_file_dir(workspace, layername)) kwargs.update({'actor_name': authn.get_authn_username()}) rest_util.setup_patch_access_rights(request.form, kwargs) util.pre_publication_action_check( workspace, layername, kwargs, ) util.patch_layer( workspace, layername, kwargs, delete_from, 'layman.layer.filesystem.input_chunk' if use_chunk_upload else delete_from) app.logger.info('PATCH Layer changes done') info = util.get_complete_layer_info(workspace, layername) info.update(layer_result) return jsonify(info), 200
def patch(workspace, layername): app.logger.info(f"PATCH Layer, user={g.user}") info = util.get_complete_layer_info(cached=True) kwargs = { 'title': info.get('title', info['name']), 'description': info.get('description', ''), } # FILE use_chunk_upload = False files = [] if 'file' in request.files: files = [ f for f in request.files.getlist("file") if len(f.filename) > 0 ] if len(files) == 0 and len(request.form.getlist('file')) > 0: files = [ filename for filename in request.form.getlist('file') if len(filename) > 0 ] if len(files) > 0: use_chunk_upload = True # CRS crs_id = None if len(files) > 0 and len(request.form.get('crs', '')) > 0: crs_id = request.form['crs'] if crs_id not in settings.INPUT_SRS_LIST: raise LaymanError(2, {'parameter': 'crs', 'supported_values': settings.INPUT_SRS_LIST}) check_crs = crs_id is None # TITLE if len(request.form.get('title', '')) > 0: kwargs['title'] = request.form['title'] # DESCRIPTION if len(request.form.get('description', '')) > 0: kwargs['description'] = request.form['description'] # SLD style_file = None if 'style' in request.files and not request.files['style'].filename == '': style_file = request.files['style'] elif 'sld' in request.files and not request.files['sld'].filename == '': style_file = request.files['sld'] delete_from = None if style_file: style_type = input_style.get_style_type_from_file_storage(style_file) kwargs['style_type'] = style_type kwargs['store_in_geoserver'] = style_type.store_in_geoserver delete_from = 'layman.layer.qgis.wms' if len(files) > 0: delete_from = 'layman.layer.filesystem.input_file' # FILE NAMES if delete_from == 'layman.layer.filesystem.input_file': if use_chunk_upload: filenames = files else: filenames = [f.filename for f in files] input_file.check_filenames(workspace, layername, filenames, check_crs, ignore_existing_files=True) props_to_refresh = util.get_same_or_missing_prop_names(workspace, layername) kwargs['metadata_properties_to_refresh'] = props_to_refresh layer_result = {} if delete_from is not None: request_method = request.method.lower() deleted = util.delete_layer(workspace, layername, source=delete_from, http_method=request_method) if style_file is None: try: style_file = deleted['style']['file'] except KeyError: pass style_type = input_style.get_style_type_from_file_storage(style_file) kwargs['style_type'] = style_type kwargs['store_in_geoserver'] = style_type.store_in_geoserver if style_file: input_style.save_layer_file(workspace, layername, style_file, style_type) kwargs.update({ 'crs_id': crs_id, 'ensure_user': False, 'http_method': request_method, 'metadata_properties_to_refresh': props_to_refresh, }) if delete_from == 'layman.layer.filesystem.input_file': if use_chunk_upload: files_to_upload = input_chunk.save_layer_files_str( workspace, layername, files, check_crs) layer_result.update({ 'files_to_upload': files_to_upload, }) kwargs.update({ 'check_crs': check_crs, }) else: input_file.save_layer_files( workspace, layername, files, check_crs) kwargs.update({'actor_name': authn.get_authn_username()}) rest_util.setup_patch_access_rights(request.form, kwargs) util.pre_publication_action_check(workspace, layername, kwargs, ) util.patch_layer( workspace, layername, kwargs, delete_from, 'layman.layer.filesystem.input_chunk' if use_chunk_upload else delete_from ) app.logger.info('PATCH Layer changes done') info = util.get_complete_layer_info(workspace, layername) info.update(layer_result) return jsonify(info), 200
def patch(workspace, mapname): app.logger.info(f"PATCH Map, user={g.user}") info = util.get_complete_map_info(cached=True) # FILE file = None file_json = {} if 'file' in request.files and not request.files['file'].filename == '': file = request.files["file"] if file is not None: file_json = util.check_file(file) # TITLE if len(request.form.get('title', '')) > 0: title = request.form['title'] elif len(file_json.get('title', '')) > 0: title = file_json['title'] else: title = info['title'] # DESCRIPTION if len(request.form.get('description', '')) > 0: description = request.form['description'] elif len(file_json.get('abstract', '')) > 0: description = file_json['abstract'] else: description = info['description'] props_to_refresh = util.get_same_or_missing_prop_names(workspace, mapname) metadata_properties_to_refresh = props_to_refresh if file is not None: thumbnail.delete_map(workspace, mapname) file = FileStorage(io.BytesIO(json.dumps(file_json).encode()), file.filename) input_file.save_map_files(workspace, mapname, [file]) file_changed = file is not None kwargs = { 'title': title, 'description': description, 'file_changed': file_changed, 'http_method': 'patch', 'metadata_properties_to_refresh': metadata_properties_to_refresh, 'actor_name': authn.get_authn_username(), } rest_util.setup_patch_access_rights(request.form, kwargs) util.pre_publication_action_check( workspace, mapname, kwargs, ) util.patch_map( workspace, mapname, kwargs, 'layman.map.filesystem.input_file' if file_changed else None) info = util.get_complete_map_info(workspace, mapname) return jsonify(info), 200
def can_i_edit(publ_type, workspace, publication_name): actor_name = authn.get_authn_username() return can_user_write_publication(actor_name, workspace, publ_type, publication_name)
def post(workspace): app.logger.info(f"POST Layers, user={g.user}") # FILE use_chunk_upload = False files = [] if 'file' in request.files: files = [ f for f in request.files.getlist("file") if len(f.filename) > 0 ] if len(files) == 0 and len(request.form.getlist('file')) > 0: files = [ filename for filename in request.form.getlist('file') if len(filename) > 0 ] if len(files) > 0: use_chunk_upload = True if len(files) == 0: raise LaymanError(1, {'parameter': 'file'}) # NAME unsafe_layername = request.form.get('name', '') if len(unsafe_layername) == 0: unsafe_layername = input_file.get_unsafe_layername(files) layername = util.to_safe_layer_name(unsafe_layername) util.check_layername(layername) info = util.get_layer_info(workspace, layername) if info: raise LaymanError(17, {'layername': layername}) util.check_new_layername(workspace, layername) # CRS crs_id = None if len(request.form.get('crs', '')) > 0: crs_id = request.form['crs'] if crs_id not in settings.INPUT_SRS_LIST: raise LaymanError(2, {'parameter': 'crs', 'supported_values': settings.INPUT_SRS_LIST}) check_crs = crs_id is None # TITLE if len(request.form.get('title', '')) > 0: title = request.form['title'] else: title = layername # DESCRIPTION description = request.form.get('description', '') # Style style_file = None if 'style' in request.files and not request.files['style'].filename == '': style_file = request.files['style'] elif 'sld' in request.files and not request.files['sld'].filename == '': style_file = request.files['sld'] style_type = input_style.get_style_type_from_file_storage(style_file) actor_name = authn.get_authn_username() task_options = { 'crs_id': crs_id, 'description': description, 'title': title, 'ensure_user': True, 'check_crs': False, 'actor_name': actor_name, 'style_type': style_type, 'store_in_geoserver': style_type.store_in_geoserver, } rest_common.setup_post_access_rights(request.form, task_options, actor_name) util.pre_publication_action_check(workspace, layername, task_options, ) layerurl = url_for('rest_workspace_layer.get', layername=layername, workspace=workspace) layer_result = { 'name': layername, 'url': layerurl, } # FILE NAMES if use_chunk_upload: filenames = files else: filenames = [f.filename for f in files] input_file.check_filenames(workspace, layername, filenames, check_crs) redis_util.create_lock(workspace, LAYER_TYPE, layername, request.method) try: # register layer uuid uuid_str = uuid.assign_layer_uuid(workspace, layername) layer_result.update({ 'uuid': uuid_str, }) task_options.update({'uuid': uuid_str, }) # save files input_style.save_layer_file(workspace, layername, style_file, style_type) if use_chunk_upload: files_to_upload = input_chunk.save_layer_files_str( workspace, layername, files, check_crs) layer_result.update({ 'files_to_upload': files_to_upload, }) task_options.update({ 'check_crs': check_crs, }) else: input_file.save_layer_files( workspace, layername, files, check_crs) util.post_layer( workspace, layername, task_options, 'layman.layer.filesystem.input_chunk' if use_chunk_upload else 'layman.layer.filesystem.input_file' ) except Exception as exc: try: if util.is_layer_chain_ready(workspace, layername): redis_util.unlock_publication(workspace, LAYER_TYPE, layername) finally: redis_util.unlock_publication(workspace, LAYER_TYPE, layername) raise exc # app.logger.info('uploaded layer '+layername) return jsonify([layer_result]), 200
def proxy(subpath): app.logger.info( f"{request.method} GeoServer proxy, actor={g.user}, subpath={subpath}, url={request.url}, request.query_string={request.query_string.decode('UTF-8')}" ) # adjust authentication headers url = settings.LAYMAN_GS_URL + subpath query_params_string = request.query_string.decode('UTF-8') headers_req = { key.lower(): value for (key, value) in request.headers if key.lower() not in ['host', settings.LAYMAN_GS_AUTHN_HTTP_HEADER_ATTRIBUTE.lower()] } data = request.get_data() authn_username = authn.get_authn_username() if is_user_with_name(authn_username): headers_req[ settings.LAYMAN_GS_AUTHN_HTTP_HEADER_ATTRIBUTE] = authn_username # ensure layer attributes in case of WFS-T app.logger.info( f"{request.method} GeoServer proxy, headers_req={headers_req}, url={url}" ) wfs_t_layers = set() if data is not None and len(data) > 0: try: wfs_t_attribs, wfs_t_layers = extract_attributes_and_layers_from_wfs_t( data) if wfs_t_attribs: ensure_wfs_t_attributes(wfs_t_attribs) except BaseException as err: app.logger.warning( f"WFS Proxy: error={err}, trace={traceback.format_exc()}") query_params = CaseInsensitiveDict(request.args.to_dict()) # change CRS:84 to EPSG:4326 if one of SLD layers has native CRS EPSG:5514 # otherwise layers are shifted by hundreds of meters, we are not sure about the reason if query_params.get('service') == 'WMS' and query_params.get('request') == 'GetMap'\ and (query_params.get('crs') or query_params.get('srs')) == crs_def.CRS_84: layers = [ layer.split(':') for layer in query_params.get('layers').split(',') ] url_workspace = extract_workspace_from_url(subpath) layers = [ layer if len(layer) == 2 else [url_workspace] + layer for layer in layers ] fix_params = False for geoserver_workspace, layer in layers: workspace = gs_wms.get_layman_workspace(geoserver_workspace) publ_info = layman_util.get_publication_info( workspace, LAYER_TYPE, layer, {'keys': ['native_crs', 'style_type']}) if publ_info and publ_info.get( 'native_crs') == crs_def.EPSG_5514 and publ_info.get( 'style_type') == 'sld': fix_params = True break if fix_params: if query_params.get('crs') == crs_def.CRS_84: param_key = 'crs' bbox = query_params['bbox'].split(',') bbox = [bbox[1], bbox[0], bbox[3], bbox[2]] query_params['bbox'] = ",".join(bbox) else: param_key = 'srs' query_params[param_key] = crs_def.EPSG_4326 query_params_string = parse.urlencode(query_params) url += '?' + query_params_string app.logger.info(f"{request.method} GeoServer proxy, final_url={url}") response = requests.request(method=request.method, url=url, data=data, headers=headers_req, cookies=request.cookies, allow_redirects=False) if response.status_code == 200: for workspace, layername in wfs_t_layers: file_info = layman_util.get_publication_info( workspace, LAYER_TYPE, layername, context={'keys': ['file']})['file'] if authz.can_i_edit( LAYER_TYPE, workspace, layername ) and file_info['file_type'] == settings.FILE_TYPE_VECTOR: patch_after_feature_change(workspace, layername) excluded_headers = [ 'content-encoding', 'content-length', 'transfer-encoding', 'connection' ] headers = { key: value for (key, value) in response.headers.items() if key.lower() not in excluded_headers } final_response = Response(response.content, response.status_code, headers) return final_response
def post(workspace): app.logger.info(f"POST Maps, user={g.user}") # FILE if 'file' in request.files and not request.files['file'].filename == '': file = request.files["file"] else: raise LaymanError(1, {'parameter': 'file'}) file_json = util.check_file(file) # NAME unsafe_mapname = request.form.get('name', '') if len(unsafe_mapname) == 0: unsafe_mapname = input_file.get_unsafe_mapname(file_json) mapname = util.to_safe_map_name(unsafe_mapname) util.check_mapname(mapname) info = util.get_map_info(workspace, mapname) if info: raise LaymanError(24, {'mapname': mapname}) # TITLE if len(request.form.get('title', '')) > 0: title = request.form['title'] elif len(file_json.get('title', '')) > 0: title = file_json['title'] else: title = mapname # DESCRIPTION if len(request.form.get('description', '')) > 0: description = request.form['description'] else: description = file_json.get('abstract', '') mapurl = url_for('rest_workspace_map.get', mapname=mapname, workspace=workspace) redis_util.create_lock(workspace, MAP_TYPE, mapname, request.method) try: map_result = { 'name': mapname, 'url': mapurl, } actor_name = authn.get_authn_username() kwargs = { 'title': title, 'description': description, 'actor_name': actor_name } rest_common.setup_post_access_rights(request.form, kwargs, actor_name) util.pre_publication_action_check( workspace, mapname, kwargs, ) # register map uuid uuid_str = uuid.assign_map_uuid(workspace, mapname) kwargs['uuid'] = uuid_str map_result.update({ 'uuid': uuid_str, }) file = FileStorage(io.BytesIO(json.dumps(file_json).encode()), file.filename) input_file.save_map_files(workspace, mapname, [file]) util.post_map(workspace, mapname, kwargs, 'layman.map.filesystem.input_file') except Exception as exception: try: if util.is_map_chain_ready(workspace, mapname): redis_util.unlock_publication(workspace, MAP_TYPE, mapname) finally: redis_util.unlock_publication(workspace, MAP_TYPE, mapname) raise exception # app.logger.info('uploaded map '+mapname) return jsonify([map_result]), 200