I am looking to leverage the Metadata API to help audit the security of a system on a single splash page (primarily to see Profile based permissions on different configurations). As I poke around the documentation and the client stubs, it seems like after I make a retrieve call based on a list of metadata, I receive a ZIP file back. My question is whether anyone has had any experience with parsing the XML via any built in tools, or if manually parsing each Profile XML is the only way to get at the data (was hoping that I'd be able to leverage some stub from the WSDL to pass in the file content and have it parse into objects, but it doesn't seem that is the case).
1 Answer
Metadata API Route. You can likely reuse the Java types generated by the WSDL and call utility methods provided by your Web Service stack your using to de-serialise the XML contents of the .profile into the generated Profile Java type.
I've done this the Salesforce WSC stack (using the static method 'load' on the generated types). The following is an example using the Retrieve for a Custom Object, but you should be able to adapt it easily for Profile. However read on to another thought I have about your requirement below...
// Retrieve Custom Object Meta data for Source Object
RetrieveRequest retrieveRequest = new RetrieveRequest();
retrieveRequest.setSinglePackage(true);
com.sforce.soap.metadata.Package packageManifest = new com.sforce.soap.metadata.Package();
ArrayList<PackageTypeMembers> types = new ArrayList<PackageTypeMembers>();
PackageTypeMembers packageTypeMember = new PackageTypeMembers();
packageTypeMember.setName("CustomObject");
packageTypeMember.setMembers(new String[] { sourceObject });
types.add(packageTypeMember);
packageManifest.setTypes((PackageTypeMembers[]) types.toArray(new PackageTypeMembers[] {}));
retrieveRequest.setUnpackaged(packageManifest);
AsyncResult response = metadataConnection.retrieve(retrieveRequest);
while(!response.isDone())
{
Thread.sleep(1000);
response = metadataConnection.checkStatus(new String[] { response.getId()} )[0];
}
RetrieveResult retrieveResult = metadataConnection.checkRetrieveStatus(response.getId());
// Parse Custom Object Meta Data for Source Object
CustomObject customObject = new CustomObject();
byte[] zipBytes = retrieveResult.getZipFile();
ZipInputStream zipis = new ZipInputStream(new ByteArrayInputStream(zipBytes, 0, zipBytes.length));
ZipEntry zipEntry = null;
while((zipEntry = zipis.getNextEntry()) != null)
{
if(zipEntry.getName().endsWith(sourceObject + ".object"))
{
TypeMapper typeMapper = new TypeMapper();
XmlInputStream xmlis = new XmlInputStream();
xmlis.setInput(zipis, "UTF-8");
customObject.load(xmlis, typeMapper);
zipis.closeEntry();
break;
}
}
Reading Profiles Data via SOQL
One of the biggest problems with the Metadata API and Profiles, is that in order to get the complete information about your profiles you need to include in the manifest / request a list of all the classes, objects and pages you want to be included in the Profile returned. Fortunately what you can do instead is leverage the fact that Profile information is actually stored in the Permission Set tables, which you can query using the Salesforce Partner or REST API's.
For me this is the easier route to achieve what you want, what do you think?
-
The Permission Set table is a very interesting idea, one that I hadn't considered before. I think it actually will fit this use case significantly better than the Metadata API (the idea of putting everything in the package.xml just to see the security on it, as you mentioned, was going to be very annoying). Commented Dec 10, 2013 at 18:36