来源 目录
图层访问
概述
// Copyright 2015 ESRI
//
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
//
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
//
// See the use restrictions at <your ArcGIS install location>/DeveloperKit10.3/userestrictions.txt.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Server;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.SOESupport;
using System.IO;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;
using NetLayerAccessSOI.SOISupport;
//TODO: sign the project (project properties > signing tab > sign the assembly)
// this is strongly suggested if the dll will be registered using regasm.exe <your>.dll /codebase
namespace NetLayerAccessSOI
{
[ComVisible(true)]
[Guid("0b8694f2-1730-453d-b645-7022dfc24bef")]
[ClassInterface(ClassInterfaceType.None)]
[ServerObjectInterceptor("MapServer",
Description = "SOI to control access to different layers of a service.",
DisplayName = "DotNet Layer Access SOI Example",
Properties = "")]
public class NetLayerAccessSOI : IServerObjectExtension, IRESTRequestHandler, IWebRequestHandler, IRequestHandler2, IRequestHandler
{
private string _soiName;
private IServerObjectHelper _soHelper;
private ServerLogger _serverLog;
private IServerObject _serverObject;
RestServiceSOI _restServiceSOI;
SoapServiceSOI _soapServiceSOI;
private Dictionary<RESTHandlerOpCode, RestFilterOperation> _operationMap;
/*
* Map used to store permission information. Permission rules for each
* service is read form the permisson.json file.
*/
private Dictionary<String, String> _servicePermissionMap = null;
/*
* Permission are read from this external file. Advantage of an external file is that
* same SOI can be used for multiple services and permission for all of these services
* is read from the permission.json file.
*
*/
private String _permissionFilePath = "C:\\arcgisserver\\permission.json"; //default path
private String _wsdlFilePath = "C:\\Program Files\\ArcGIS\\Server\\XmlSchema\\MapServer.wsdl"; //default path
public NetLayerAccessSOI ()
{
_soiName = this.GetType().Name;
}
private void InitFiltering ()
{
_operationMap = new Dictionary<RESTHandlerOpCode, RestFilterOperation>
{
{ RESTHandlerOpCode.root, new RestFilterOperation
{
PreFilter = null,
PostFilter = PostFilterRESTRoot
} },
{ RESTHandlerOpCode.rootExport, new RestFilterOperation
{
PreFilter = PreFilterExport,
PostFilter = null
} },
{ RESTHandlerOpCode.rootFind, new RestFilterOperation
{
PreFilter = PreFilterFindAndKml,
PostFilter = null
} },
{ RESTHandlerOpCode.rootGenerateKml, new RestFilterOperation
{
PreFilter = PreFilterFindAndKml,
PostFilter = null
} },
{ RESTHandlerOpCode.rootIdentify, new RestFilterOperation
{
PreFilter = PreFilterIdentify,
PostFilter = null
} },
{ RESTHandlerOpCode.rootLayers, new RestFilterOperation
{
PreFilter = null,
PostFilter = PostFilterRootLayers
} },
{ RESTHandlerOpCode.rootLegend, new RestFilterOperation
{
PreFilter = null,
PostFilter = PostFilterRootLegend
} },
{ RESTHandlerOpCode.layerGenerateRenderer, new RestFilterOperation
{
PreFilter = PreFilterLayerQuery,
PostFilter = null
} },
{ RESTHandlerOpCode.layerQuery, new RestFilterOperation
{
PreFilter = PreFilterLayerQuery,
PostFilter = null
} },
{ RESTHandlerOpCode.layerQueryRelatedRecords, new RestFilterOperation
{
PreFilter = PreFilterLayerQuery,
PostFilter = null
} }
};
}
public void Init ( IServerObjectHelper pSOH )
{
try
{
_soHelper = pSOH;
_serverLog = new ServerLogger();
_serverObject = pSOH.ServerObject;
_restServiceSOI = new RestServiceSOI(_soHelper);
_soapServiceSOI = new SoapServiceSOI(_soHelper, _wsdlFilePath);
if (File.Exists(_permissionFilePath))
{
//TODO REMOVE
_serverLog.LogMessage(ServerLogger.msgType.infoStandard, _soiName + ".init()", 200, "Reading permissions from " + _permissionFilePath);
_servicePermissionMap = ReadPermissionFile(_permissionFilePath);
//TODO REMOVE
_serverLog.LogMessage(ServerLogger.msgType.infoStandard, _soiName + ".init()", 200, "Total permission entries: " + _servicePermissionMap.Count());
}
else
{
_serverLog.LogMessage(ServerLogger.msgType.error, _soiName + ".init()", 500, "Permission file does not exist at " + _permissionFilePath);
}
InitFiltering();
_serverLog.LogMessage(ServerLogger.msgType.infoStandard, _soiName + ".init()", 200, "Initialized " + _soiName + " SOI.");
}
catch (Exception e)
{
_serverLog.LogMessage(ServerLogger.msgType.error, _soiName + ".init()", 500, "Exception: " + e.Message + " in " + e.StackTrace);
}
}
public void Shutdown ()
{
_serverLog.LogMessage(ServerLogger.msgType.infoStandard, _soiName + ".init()", 200, "Shutting down " + _soiName + " SOE.");
}
#region REST interceptors
public string GetSchema ()
{
try
{
IRESTRequestHandler restRequestHandler = _restServiceSOI.FindRequestHandlerDelegate<IRESTRequestHandler>();
if (restRequestHandler == null)
return null;
return restRequestHandler.GetSchema();
}
catch (Exception e)
{
_serverLog.LogMessage(ServerLogger.msgType.error, _soiName + ".GetSchema()", 500, "Exception: " + e.Message + " in " + e.StackTrace);
return null;
}
}
private byte[] FilterRESTRequest (
RestFilterOperation restFilterOp,
RESTRequestParameters restInput,
HashSet<string> authorizedLayers,
out string responseProperties )
{
try
{
responseProperties = null;
IRESTRequestHandler restRequestHandler = _restServiceSOI.FindRequestHandlerDelegate<IRESTRequestHandler>();
if (restRequestHandler == null)
throw new RESTErrorException("Service handler not found");
if (null != restFilterOp && null != restFilterOp.PreFilter)
restInput = restFilterOp.PreFilter(restInput, authorizedLayers);
byte[] response =
restRequestHandler.HandleRESTRequest(restInput.Capabilities, restInput.ResourceName, restInput.OperationName, restInput.OperationInput,
restInput.OutputFormat, restInput.RequestProperties, out responseProperties);
if (null == restFilterOp || null == restFilterOp.PostFilter)
return response;
String responseStr = System.Text.Encoding.UTF8.GetString(response);
responseStr = restFilterOp.PostFilter(restInput, responseStr, authorizedLayers);
// TODO: remove ------------------------------------
_serverLog.LogMessage(ServerLogger.msgType.infoDetailed, "HandleRESTRequest.FilterRESTRequest", 0, "Filtered REST resource response :: " + responseStr);
// TODO: remove ------------------------------------
return System.Text.Encoding.UTF8.GetBytes(responseStr);
}
catch (RESTErrorException restException)
{
// pre- or post- filters can throw restException with the error JSON output in the Message property.
// we catch them here and return JSON response.
responseProperties = "{\"Content-Type\":\"text/plain;charset=utf-8\"}";
//catch and return a JSON error from the pre- or postFilter.
return System.Text.Encoding.UTF8.GetBytes(restException.Message);
}
}
private static RESTHandlerOpCode GetHandlerOpCode ( RESTRequestParameters restInput )
{
var resName = restInput.ResourceName.TrimStart('/'); //remove leading '/' to prevent empty string at index 0
var resourceTokens = (resName ?? "").ToLower().Split('/');
string opName = (restInput.OperationName ?? "").ToLower();
switch (resourceTokens[0])
{
case "":
switch (opName)
{
case "":
return RESTHandlerOpCode.root;
case "export":
return RESTHandlerOpCode.rootExport;
case "find":
return RESTHandlerOpCode.rootFind;
case "identify":
return RESTHandlerOpCode.rootIdentify;
case "generatekml":
return RESTHandlerOpCode.rootGenerateKml;
default:
return RESTHandlerOpCode.defaultNoOp;
}
case "layers":
{
var tokenCount = resourceTokens.GetLength(0);
if (1 == tokenCount)
return RESTHandlerOpCode.rootLayers;
if (2 == tokenCount)
switch (opName)
{
case "":
return RESTHandlerOpCode.layerRoot;
case "query":
return RESTHandlerOpCode.layerQuery;
case "queryRelatedRecords":
return RESTHandlerOpCode.layerQueryRelatedRecords;
default:
return RESTHandlerOpCode.defaultNoOp;
}
}
break;
case "legend":
return RESTHandlerOpCode.rootLegend;
default:
return RESTHandlerOpCode.defaultNoOp;
}
return RESTHandlerOpCode.defaultNoOp;
}
public byte[] HandleRESTRequest ( string capabilities, string resourceName, string operationName,
string operationInput, string outputFormat, string requestProperties, out string responseProperties )
{
try
{
responseProperties = null;
_serverLog.LogMessage(ServerLogger.msgType.infoStandard, _soiName + ".HandleRESTRequest()",
200, "Request received in Layer Access SOI for handleRESTRequest");
var authorizedLayerSet = GetAuthorizedLayers(_restServiceSOI);
var restInput = new RESTRequestParameters
{
Capabilities = capabilities,
ResourceName = resourceName,
OperationName = operationName,
OperationInput = operationInput,
OutputFormat = outputFormat,
RequestProperties = requestProperties
};
var opCode = GetHandlerOpCode(restInput);
RestFilterOperation filterOp = _operationMap.ContainsKey(opCode) ? _operationMap[opCode] : null;
return FilterRESTRequest(filterOp, restInput, authorizedLayerSet, out responseProperties);
}
catch (Exception e)
{
_serverLog.LogMessage(ServerLogger.msgType.error, _soiName + ".HandleRESTRequest()", 500, "Exception: " + e.Message + " in " + e.StackTrace);
throw;
}
}
#endregion
#region REST Pre-filters
private static RESTRequestParameters PreFilterExport ( RESTRequestParameters restInput, HashSet<string> authorizedLayers )
{
JavaScriptSerializer sr = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
var operationInputJson = sr.DeserializeObject(restInput.OperationInput) as IDictionary<string, object>;
operationInputJson["layers"] = "show:" + String.Join(",", authorizedLayers);
restInput.OperationInput = sr.Serialize(operationInputJson);
return restInput;
}
private static RESTRequestParameters PreFilterFindAndKml ( RESTRequestParameters restInput, HashSet<string> authorizedLayers )
{
JavaScriptSerializer sr = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
var operationInputJson = sr.DeserializeObject(restInput.OperationInput) as IDictionary<string, object>;
var removeSpacesRegEx = new Regex("\\s+");
String requestedLayers = operationInputJson.ContainsKey("layers") ? operationInputJson["layers"].ToString() : "";
requestedLayers = removeSpacesRegEx.Replace(requestedLayers, "");
operationInputJson["layers"] = RemoveUnauthorizedLayersFromRequestedLayers(requestedLayers, authorizedLayers);
restInput.OperationInput = sr.Serialize(operationInputJson);
return restInput;
}
private static RESTRequestParameters PreFilterIdentify ( RESTRequestParameters restInput, HashSet<string> authorizedLayers )
{
JavaScriptSerializer sr = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
var operationInputJson = sr.DeserializeObject(restInput.OperationInput) as IDictionary<string, object>;
String requestedLayers = operationInputJson.ContainsKey("layers") ? operationInputJson["layers"].ToString() : "";
if (string.IsNullOrEmpty(requestedLayers) || requestedLayers.StartsWith("top") || requestedLayers.StartsWith("all"))
{
operationInputJson["layers"] = "visible:" + authorizedLayers;
}
else if (requestedLayers.StartsWith("visible"))
{
var reqLayerParts = requestedLayers.Split(':');
var removeSpacesRegEx = new Regex("\\s+");
operationInputJson["layers"] = "visible:" +
RemoveUnauthorizedLayersFromRequestedLayers(
removeSpacesRegEx.Replace(reqLayerParts[1], ""), authorizedLayers);
}
else
{
operationInputJson["layers"] = "visible:" + authorizedLayers;
}
restInput.OperationInput = sr.Serialize(operationInputJson);
return restInput;
}
private static RESTRequestParameters PreFilterLayerQuery ( RESTRequestParameters restInput, HashSet<string> authorizedLayers )
{
JavaScriptSerializer sr = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
var operationInputJson = sr.DeserializeObject(restInput.OperationInput) as IDictionary<string, object>;
var resName = restInput.ResourceName.TrimStart('/');
var rnameParts = resName.Split('/');
var requestedLayerId = rnameParts[1];
if (!authorizedLayers.Contains(requestedLayerId))
{
var errorReturn = new Dictionary<string, object>
{
{"error", new Dictionary<string, object>
{
{"code", 404},
{"message", "Access Denied"}
}
}
};
throw new RESTErrorException(sr.Serialize(errorReturn));
}
restInput.OperationInput = sr.Serialize(operationInputJson);
return restInput;
}
#endregion
#region REST Post-filters
/// <summary>
///
/// Filter REST root service info response.
/// IMPORTANT: the JSON structure returned by the REST handler root resource
/// differs from what you usually see as the response from the service REST endpoint.
///
/// </summary>
/// <param name="restInput"></param>
/// <param name="originalResponse"></param>
/// <param name="authorizedLayerSet"></param>
/// <returns>Filtered json</returns>
private static String PostFilterRESTRoot ( RESTRequestParameters restInput, String originalResponse, HashSet<String> authorizedLayerSet )
{
if (null == authorizedLayerSet)
return null;
//restInput is not used here, but may be used later as needed
try
{
// Perform JSON filtering
// Note: The JSON syntax can change and you might have to adjust this piece of code accordingly
if (authorizedLayerSet == null)
return null;
/*
* Remove unauthorized layer information from 1. Under 'contents' tag 2. Under 'resources' tag
* 2.1 'layers' 2.2 'tables' 2.3 'legend'
*/
JavaScriptSerializer sr = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
var jsonResObj = sr.DeserializeObject(originalResponse) as IDictionary<string, object>;
// Filter for 'contents' tag
var contentsJO = jsonResObj["contents"] as IDictionary<string, object>;
var layersJA = contentsJO["layers"] as object[];
var updatedLayers = new List<object>();
foreach (var layerO in layersJA)
{
var layerJO = layerO as IDictionary<string, object>;
var id = layerJO["id"].ToString();
if (authorizedLayerSet.Contains(id))
{
updatedLayers.Add(layerJO);
}
}
contentsJO["layers"] = updatedLayers.ToArray();
// Filter for 'resources' tag, very simplified filtering, may fail if ordering changes
var allResourcesJA = jsonResObj["resources"] as object[];
var layersRJO = allResourcesJA.FirstOrDefault(e =>
{
var jo = e as IDictionary<string, object>;
if (!jo.ContainsKey("name"))
return false;
var name = jo["name"].ToString();
return ("layers" == name);
}) as IDictionary<string, object>;
var tablesRJO = allResourcesJA.FirstOrDefault(e =>
{
var jo = e as IDictionary<string, object>;
if (!jo.ContainsKey("name"))
return false;
var name = jo["name"].ToString();
return ("tables" == name);
}) as IDictionary<string, object>;
var legendRJO = allResourcesJA.FirstOrDefault(e =>
{
var jo = e as IDictionary<string, object>;
if (!jo.ContainsKey("name"))
return false;
var name = jo["name"].ToString();
return ("legend" == name);
}) as IDictionary<string, object>;
//filter and replace layers
var filteredLayerResourceJA = new List<object>();
if (null != layersRJO)
{
// Filter for 'resources -> layers -> resources' tag
var layerResourceJA = layersRJO["resources"] as object[];
foreach (var lrJO in layerResourceJA)
{
var lrJODict = lrJO as IDictionary<string, object>;
if (authorizedLayerSet.Contains(lrJODict["name"].ToString()))
{
filteredLayerResourceJA.Add(lrJO);
}
}
}
layersRJO["resources"] = filteredLayerResourceJA.ToArray();
//filter and replace tables
var filteredTableResourceJA = new List<object>();
if (null != tablesRJO)
{
// Filter for 'resources -> tables -> resources' tag
var tableResourceJA = tablesRJO["resources"] as object[];
foreach (var tbJO in tableResourceJA)
{
var tbJODict = tbJO as IDictionary<string, object>;
if (authorizedLayerSet.Contains(tbJODict["name"].ToString()))
{
filteredTableResourceJA.Add(tbJO);
}
}
}
tablesRJO["resources"] = filteredTableResourceJA.ToArray();
//filter and replace legend layers
var filteredLegendLayersRJA = new List<object>();
if (null != legendRJO)
{
// Filter for 'resources -> legend -> contents->layers' tag
var legendContentsJO = legendRJO["contents"] as IDictionary<string, object>;
var legendLayersJA = legendContentsJO["layers"] as object[];
foreach (var lgJO in legendLayersJA)
{
var lgJODict = lgJO as IDictionary<string, object>;
if (authorizedLayerSet.Contains(lgJODict["layerId"].ToString()))
{
filteredLegendLayersRJA.Add(lgJO);
}
}
legendContentsJO["layers"] = filteredLegendLayersRJA.ToArray();
}
// Return the filter response
return sr.Serialize(jsonResObj);
}
catch (Exception ignore)
{
return null;
}
}
private static string PostFilterRootLayers ( RESTRequestParameters restInput, String originalResponse, HashSet<String> authorizedLayerSet )
{
if (null == authorizedLayerSet)
return null;
//restInput is not used here, but may be used later as needed
try
{
// Perform JSON filtering
// Note: The JSON syntax can change and you might have to adjust this piece of code accordingly
if (authorizedLayerSet == null)
return null;
/*
* Remove unauthorized layer information from 1. Under 'contents' tag 2. Under 'resources' tag
* 2.1 'layers' 2.2 'tables' 2.3 'legend'
*/
JavaScriptSerializer sr = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
var jsonResObj = sr.DeserializeObject(originalResponse) as IDictionary<string, object>;
// Filter for 'contents' tag
var layersJA = jsonResObj["layers"] as object[];
var updatedLayers = new List<object>();
foreach (var layerO in layersJA)
{
var layerJO = layerO as IDictionary<string, object>;
var id = layerJO["id"].ToString();
if (authorizedLayerSet.Contains(id))
{
updatedLayers.Add(layerJO);
}
}
jsonResObj["layers"] = updatedLayers.ToArray();
// Return the filter response
return sr.Serialize(jsonResObj);
}
catch (Exception ignore)
{
return null;
}
}
private static string PostFilterRootLegend ( RESTRequestParameters restInput, String originalResponse, HashSet<String> authorizedLayerSet )
{
if (null == authorizedLayerSet)
return null;
//restInput is not used here, but may be used later as needed
try
{
// Perform JSON filtering
// Note: The JSON syntax can change and you might have to adjust this piece of code accordingly
if (authorizedLayerSet == null)
return null;
/*
* Remove unauthorized layer information from 1. Under 'contents' tag 2. Under 'resources' tag
* 2.1 'layers' 2.2 'tables' 2.3 'legend'
*/
JavaScriptSerializer sr = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
var jsonResObj = sr.DeserializeObject(originalResponse) as IDictionary<string, object>;
// Filter for 'contents' tag
var layersJA = jsonResObj["layers"] as object[];
var updatedLayers = new List<object>();
foreach (var layerO in layersJA)
{
var layerJO = layerO as IDictionary<string, object>;
var id = layerJO["layerId"].ToString();
if (authorizedLayerSet.Contains(id))
{
updatedLayers.Add(layerJO);
}
}
jsonResObj["layers"] = updatedLayers.ToArray();
// Return the filter response
return sr.Serialize(jsonResObj);
}
catch (Exception ignore)
{
return null;
}
}
#endregion
#region SOAP interceptors
public byte[] HandleStringWebRequest ( esriHttpMethod httpMethod, string requestURL,
string queryString, string Capabilities, string requestData,
out string responseContentType, out esriWebResponseDataType respDataType )
{
_serverLog.LogMessage(ServerLogger.msgType.infoStandard, _soiName + ".HandleStringWebRequest()",
200, "Request received in Layer Access SOI for HandleStringWebRequest");
/*
* Add code to manipulate OGC (WMS, WFC, WCS etc) requests here
*/
IWebRequestHandler webRequestHandler = _restServiceSOI.FindRequestHandlerDelegate<IWebRequestHandler>();
if (webRequestHandler != null)
{
var response = webRequestHandler.HandleStringWebRequest(
httpMethod, requestURL, queryString, Capabilities, requestData, out responseContentType, out respDataType);
return response;
}
responseContentType = null;
respDataType = esriWebResponseDataType.esriWRDTPayload;
//Insert error response here.
return null;
}
public byte[] HandleBinaryRequest ( ref byte[] request )
{
_serverLog.LogMessage(ServerLogger.msgType.infoStandard, _soiName + ".HandleBinaryRequest()",
200, "Request received in Layer Access SOI for HandleBinaryRequest");
/*
* Add code to manipulate Binary requests from desktop here
*/
// Generate a set of authorized layers for the user
var authorizedLayerSet = GetAuthorizedLayers(_soapServiceSOI);
IMessage requestMessage = SoapServiceSOI.ConvertBinaryRequestToMessage(request);
SoapBinaryRequest filteredRequest = FilterSoapRequest(requestMessage, SoapRequestMode.Binary, authorizedLayerSet) as SoapBinaryRequest;
if (null == filteredRequest)
{
filteredRequest = new SoapBinaryRequest { Body = request };
}
IRequestHandler requestHandler = _restServiceSOI.FindRequestHandlerDelegate<IRequestHandler>();
if (requestHandler != null)
{
var response = requestHandler.HandleBinaryRequest(filteredRequest.Body);
// Perform filtering for GetServerInfoResponse
// Convert the XML request into a generic IMessage
IMessage responseMessage = SoapServiceSOI.ConvertBinaryRequestToMessage(response);
// Get operation name
String name = responseMessage.Name;
if ("GetServerInfoResponse".Equals(name, StringComparison.CurrentCultureIgnoreCase))
{
// Intercept the response and perform filtering
var filteredResponse = FilterSoapRequest(responseMessage, SoapRequestMode.Binary, authorizedLayerSet) as SoapBinaryRequest;
if (filteredResponse != null)
{
response = filteredResponse.Body;
}
}
return response;
}
//Insert error response here.
return null;
}
public byte[] HandleBinaryRequest2 ( string Capabilities, ref byte[] request )
{
_serverLog.LogMessage(ServerLogger.msgType.infoStandard, _soiName + ".HandleBinaryRequest2()",
200, "Request received in Layer Access SOI for HandleBinaryRequest2");
IMessage requestMessage = SoapServiceSOI.ConvertBinaryRequestToMessage(request);
var authorizedLayerSet = GetAuthorizedLayers(_soapServiceSOI);
SoapBinaryRequest filteredRequest = FilterSoapRequest(requestMessage, SoapRequestMode.Binary, authorizedLayerSet) as SoapBinaryRequest;
if (null == filteredRequest)
{
filteredRequest = new SoapBinaryRequest { Body = request };
}
IRequestHandler2 requestHandler = _restServiceSOI.FindRequestHandlerDelegate<IRequestHandler2>();
if (requestHandler != null)
{
var response = requestHandler.HandleBinaryRequest2(Capabilities, filteredRequest.Body);
// Perform filtering for GetServerInfoResponse
// Convert the XML request into a generic IMessage
IMessage responseMessage = SoapServiceSOI.ConvertBinaryRequestToMessage(response);
// Get operation name
String name = responseMessage.Name;
if ("GetServerInfoResponse".Equals(name, StringComparison.CurrentCultureIgnoreCase))
{
// Intercept the response and perform filtering
var filteredResponse = FilterSoapRequest(responseMessage, SoapRequestMode.Binary, authorizedLayerSet) as SoapBinaryRequest;
if (filteredResponse != null)
{
response = filteredResponse.Body;
}
}
return response;
}
//Insert error response here.
return null;
}
public string HandleStringRequest ( string Capabilities, string request )
{
_serverLog.LogMessage(ServerLogger.msgType.infoStandard, _soiName + ".HandleStringRequest()",
200, "Request received in Layer Access SOI for HandleStringRequest");
// Convert the XML request into a generic IMessage
IMessage requestMessage = SoapServiceSOI.ConvertStringRequestToMessage(request);
// Generate a set of authorized layers for the user
var authorizedLayerSet = GetAuthorizedLayers(_soapServiceSOI);
// Intercept the request and perform filtering
var filteredRequest = FilterSoapRequest(requestMessage, SoapRequestMode.Xml, authorizedLayerSet) as SoapStringRequest;
if (filteredRequest == null)
{
filteredRequest = new SoapStringRequest { Body = request };
}
IRequestHandler requestHandler = _restServiceSOI.FindRequestHandlerDelegate<IRequestHandler>();
if (requestHandler != null)
{
var response = requestHandler.HandleStringRequest(Capabilities, filteredRequest.Body);
// Perform filtering for GetServerInfoResponse
// Convert the XML request into a generic IMessage
IMessage responseMessage = SoapServiceSOI.ConvertStringRequestToMessage(response);
// Get operation name
String name = responseMessage.Name;
if ("GetServerInfoResponse".Equals(name, StringComparison.CurrentCultureIgnoreCase))
{
// Intercept the response and perform filtering
var filteredResponse = FilterSoapRequest(responseMessage, SoapRequestMode.Xml, authorizedLayerSet) as SoapStringRequest;
if (filteredResponse != null)
{
response = filteredResponse.Body;
}
}
return response;
}
//Insert error response here.
return null;
}
/// <summary>
///
/// </summary>
/// <param name="inRequest"></param>
/// <param name="mode"></param>
/// <returns></returns>
private SoapRequest FilterSoapRequest ( IMessage inRequest, SoapRequestMode mode, HashSet<string> authorizedLayerSet )
{
// Get operation name
String name = inRequest.Name;
// Apply filter only on those operations we care about
var nameInLowerCase = name.ToLower();
switch (nameInLowerCase)
{
case "find":
inRequest = FilterLayerIds(inRequest, mode, authorizedLayerSet);
break;
case "exportmapimage":
inRequest = FilterMapDescription(inRequest, mode, authorizedLayerSet);
break;
case "identify":
inRequest = FilterLayerIds(inRequest, mode, authorizedLayerSet);
break;
case "getlegendinfo":
inRequest = FilterLayerIds(inRequest, mode, authorizedLayerSet);
break;
case "getserverinforesponse":
inRequest = FilterGetServerInfoResponse(inRequest, mode, authorizedLayerSet);
break;
}
return SoapRequestFactory.Create(mode, inRequest);
}
/// <summary>
///
/// </summary>
/// <param name="inRequest"></param>
/// <param name="mode"></param>
/// <param name="authorizedLayerSet"></param>
/// <returns></returns>
private IMessage FilterLayerIds ( IMessage inRequest, SoapRequestMode mode, HashSet<String> authorizedLayerSet )
{
// 1. Find the index of the layers parameter
// 2. Get the value for the interested parameter
// 3. Manipulate it
// 4. Put it back into IMessage
int idx = -1;
IXMLSerializeData inRequestData = inRequest.Parameters;
try
{
idx = inRequestData.Find("LayerIDs");
}
catch (Exception ignore)
{
}
LongArray layerIdInLA = null;
if (idx >= 0)
{
// Get all the requested layers
layerIdInLA = (LongArray)inRequestData.GetObject(idx, inRequest.NamespaceURI, "ArrayOfInt");
// Perform filtering based on access to different layers.
// Remove restricted ids in-place
for (int i = layerIdInLA.Count - 1; i >= 0; i--)
{
if (!authorizedLayerSet.Contains(layerIdInLA.Element[i].ToString()))
{
layerIdInLA.Remove(i);
}
}
}
else //no LayerIDs specified, attaching authorized layer list instead
{
layerIdInLA = new LongArrayClass();
foreach (var goodLayerId in authorizedLayerSet)
{
layerIdInLA.Add(Int32.Parse(goodLayerId));
}
inRequestData.AddObject("LayerIDs", layerIdInLA);
}
// If binary request we dont have to create and copy in a new Message object
if (mode == SoapRequestMode.Binary)
{
return inRequest;
}
// Create new request message
IMessage modifiedInRequest = _soapServiceSOI.CreateNewIMessage(inRequest);
IXMLSerializeData modifiedInRequestData = modifiedInRequest.Parameters;
// Put all parameters back in IMessage
for (int i = 0; i < inRequestData.Count; i++)
{
if (_soapServiceSOI.GetSoapOperationParameterName(inRequest.Name, i).Equals("LayerIDs", StringComparison.CurrentCultureIgnoreCase))
{
// Add the modified MapDescription
_soapServiceSOI.AddObjectToXMLSerializeData(_soapServiceSOI.GetSoapOperationParameterName(inRequest.Name, i),
layerIdInLA, _soapServiceSOI.GetSoapOperationParameterTypeName(inRequest.Name, i), modifiedInRequestData);
}
else
{
/*
* Add other parameters as is. Here we are using the SOI helper to add and get parameters
* because we don't care about the type we just want to copy from one IMessage object to
* another.
*/
_soapServiceSOI.AddObjectToXMLSerializeData(
_soapServiceSOI.GetSoapOperationParameterName(inRequest.Name, i),
_soapServiceSOI.GetObjectFromXMLSerializeData(i, inRequest.NamespaceURI,
_soapServiceSOI.GetSoapOperationParameterTypeName(inRequest.Name, i), inRequestData),
_soapServiceSOI.GetSoapOperationParameterTypeName(inRequest.Name, i), modifiedInRequestData);
}
}
return modifiedInRequest;
}
private IMessage FilterMapDescription ( IMessage inRequest, SoapRequestMode mode, HashSet<string> authorizedLayerSet )
{
// 1. Find the index of the MapDescription parameter
// 2. Get the value for the interested parameter
// 3. Manipulate it
// 4. Put it back into IMessage
// Get the parameters out from the request object
int idx = -1;
IXMLSerializeData inRequestData = inRequest.Parameters;
idx = inRequestData.Find("MapDescription");
MapDescription md =
(MapDescription)inRequestData.GetObject(idx, inRequest.NamespaceURI,
_soapServiceSOI.GetSoapOperationParameterTypeName(inRequest.Name, idx));
// Manipulate the MapDescription to perform layer level security
ILayerDescriptions lds = md.LayerDescriptions;
for (int i = 0; i < lds.Count; i++)
{
ILayerDescription ld = lds.Element[i];
if (!authorizedLayerSet.Contains(ld.ID.ToString()))
{
ld.Visible = false;
}
}
// If binary request we dont have to create and copy in a new Message object
if (mode == SoapRequestMode.Binary)
{
return inRequest;
}
// Create new request message
IMessage modifiedInRequest = _soapServiceSOI.CreateNewIMessage(inRequest);
IXMLSerializeData modifiedInRequestData = modifiedInRequest.Parameters;
// Put all parameters back in IMessage
for (int i = 0; i < inRequestData.Count; i++)
{
if (_soapServiceSOI.GetSoapOperationParameterName(inRequest.Name, i).Equals("MapDescription", StringComparison.CurrentCultureIgnoreCase))
{
// Add the modified MapDescription
modifiedInRequestData.AddObject(_soapServiceSOI.GetSoapOperationParameterName(inRequest.Name, i), md);
}
else
{
/*
* Add other parameters as is. Here we are using the SOI helper to add and get parameters
* because we don't care about the type we just want to copy from one IMessage object to
* another.
*/
modifiedInRequestData.AddObject(
_soapServiceSOI.GetSoapOperationParameterName(inRequest.Name, i),
inRequestData.GetObject(i, inRequest.NamespaceURI,
_soapServiceSOI.GetSoapOperationParameterTypeName(inRequest.Name, i)));
}
}
return modifiedInRequest;
}
private IMessage FilterGetServerInfoResponse ( IMessage inRequest, SoapRequestMode mode, HashSet<string> authorizedLayerSet )
{
int idx = -1;
IXMLSerializeData inRequestData = inRequest.Parameters;
idx = inRequestData.Find("Result");
// Get MapServerInfo
MapServerInfo mapServerInfo = (MapServerInfo)inRequestData.GetObject(idx, inRequest.NamespaceURI,
"MapServerInfo");
// Perform filtering based on access to different layers
IMapLayerInfos layerInfos = mapServerInfo.MapLayerInfos;
for (int i = layerInfos.Count - 1; i >= 0; i--)
{
if (!authorizedLayerSet.Contains(layerInfos.Element[i].ID.ToString()))
{
layerInfos.Remove(i);
}
}
IMapDescription mapDescription = mapServerInfo.DefaultMapDescription;
ILayerDescriptions lds = mapDescription.LayerDescriptions;
for (int i = lds.Count - 1; i >= 0; i--)
{
if (!authorizedLayerSet.Contains(lds.Element[i].ID.ToString()))
{
lds.Remove(i);
}
}
// If binary request we don't have to create and copy in a new Message object
if (mode == SoapRequestMode.Binary)
{
return inRequest;
}
// Create new request message
IMessage modifiedInRequest = _soapServiceSOI.CreateNewIMessage(inRequest);
IXMLSerializeData modifiedInRequestData = modifiedInRequest.Parameters;
// Put all parameters back in IMessage
for (int i = 0; i < inRequestData.Count; i++)
{
if (_soapServiceSOI.GetSoapOperationParameterName(inRequest.Name, i).Equals("Result", StringComparison.CurrentCultureIgnoreCase))
{
// Add the modified MapDescription
modifiedInRequestData.AddObject(_soapServiceSOI.GetSoapOperationParameterName(inRequest.Name, i), mapServerInfo);
}
else
{
/*
* Add other parameters as is. Here we are using the SOI helper to add and get parameters
* because we don't care about the type we just want to copy from one IMessage object to
* another.
*/
modifiedInRequestData.AddObject(
_soapServiceSOI.GetSoapOperationParameterName(inRequest.Name, i),
inRequestData.GetObject(i, inRequest.NamespaceURI,
_soapServiceSOI.GetSoapOperationParameterTypeName(inRequest.Name, i)));
}
}
return modifiedInRequest;
}
#endregion
#region Utility code
/// <summary>
/// Remove unauthorized layers from request.
/// </summary>
/// <param name="requestedLayers">layer user is requesting information from</param>
/// <param name="authorizedLayers">layers user is authorized to fetch information from</param>
/// <returns></returns>
private static String RemoveUnauthorizedLayersFromRequestedLayers (
String requestedLayers, HashSet<String> authorizedLayers )
{
if (0 == authorizedLayers.Count)
return "-1";
// requested layers
IEnumerable<String> requestedLayersList = null;
try
{
requestedLayersList = requestedLayers.Split(new[] { ',' }).Select(e => e.Trim());
}
catch (Exception ignore) { }
if (authorizedLayers != null)
{
var filteredLayers = requestedLayersList.Where(e => authorizedLayers.Contains(e));
if (!filteredLayers.Any())
return "-1";
return String.Join(",", filteredLayers.ToArray());
}
return "-1";
}
/// <summary>
/// Read permission information from disk
/// </summary>
/// <param name="fileName">path and name of the file to read permissions from</param>
/// <returns></returns>
private Dictionary<String, String> ReadPermissionFile ( String fileName )
{
// read the permissions file
Dictionary<String, String> permissionMap = new Dictionary<String, String>();
try
{
if (!File.Exists(fileName))
return permissionMap;
String jsonStr = File.ReadAllText(fileName);
var json = new ESRI.ArcGIS.SOESupport.JsonObject(jsonStr);
System.Object[] permissions = null;
// create a map of permissions
// read the permissions array
if (!json.TryGetArray("permissions", out permissions) || permissions == null)
return permissionMap;
// add to map
foreach (var permsObj in permissions)
{
ESRI.ArcGIS.SOESupport.JsonObject permsJO = permsObj as ESRI.ArcGIS.SOESupport.JsonObject;
if (null == permsJO) continue;
// get the fqsn or service name
String fqsn = string.Empty;
permsJO.TryGetString("fqsn", out fqsn);
// read the permission for that service
System.Object[] permArray = null;
if (!permsJO.TryGetArray("permission", out permArray) || permArray == null)
continue;
foreach (var permObj in permArray)
{
ESRI.ArcGIS.SOESupport.JsonObject permJO = permObj as ESRI.ArcGIS.SOESupport.JsonObject;
String role = string.Empty;
if (!permJO.TryGetString("role", out role))
continue;
String authorizedLayers = string.Empty;
permJO.TryGetString("authorizedLayers", out authorizedLayers); //may be empty or null
permissionMap.Add(fqsn + "." + role, authorizedLayers);
}
}
}
catch (Exception ignore)
{
//TODO error handling
}
return permissionMap;
}
private HashSet<string> GetAuthorizedLayers ( ServiceSOIBase soi )
{
var userRoleSet = soi.GetRoleInformation(soi.ServerEnvironment.UserInfo);
HashSet<String> authorizedLayerSet = null;
if (null == userRoleSet)
return authorizedLayerSet;
/*
* Generate a set of authorized layers for the user
*/
var fullServiceName = _serverObject.ConfigurationName + "." + _serverObject.TypeName;
var removeSpacesRegEx = new Regex("\\s+");
var authorizedRoles = userRoleSet.Where(role=>_servicePermissionMap.ContainsKey(fullServiceName + "." + role));
var authorizedLayerList = authorizedRoles.SelectMany(role => removeSpacesRegEx.
Replace(_servicePermissionMap[fullServiceName + "." + role], "").
Split(','));
return new HashSet<string>(authorizedLayerList);
}
#endregion
}
}