7

I am using the latest v46 version of the Financial Force Metadata API Apex Wrapper to create a Connected App but the values I put in the code are partly ignored.

What is not working:

  1. ConnectedApp.permissionSetName (perm sets are in Managed Package)
  2. Set ConnectedAppOauthConfig.isAdminApproved = true

All expected Metadata objects are created with no errors.

   public override PageReference run() {
    createConnectedApp();
    createSamlSsoConfig();

    return refresh();
}


// PRIVATE

private void createConnectedApp() {
    if(connectedApp.isEmpty() || Test.isRunningTest()) {
        MetadataService.ConnectedApp app = new MetadataService.ConnectedApp();
        app.fullName = SetupCtrl.DEV_NAME;
        app.label = SetupCtrl.DEV_NAME;
        app.contactEmail = UserInfo.getUserEmail();
        app.oauthConfig = oauthConfig();
        app.permissionSetName = new List<String>{ 'PackagedAdminPermSet' };// WONT WORK

        save(new List<MetadataService.Metadata>{ app });
    }
}

private MetadataService.ConnectedAppOauthConfig oauthConfig() {
    MetadataService.ConnectedAppOauthConfig result = new MetadataService.ConnectedAppOauthConfig();
    result.isAdminApproved = true; // WONT WORK
    result.callbackUrl = 'http://localhost/callback';
    result.scopes = new List<String>{ 'Api', 'Web', 'RefreshToken', 'OpenID' };

    return result;
}
11
  • 3
    There have been issues with managing connected apps via Metadata API. Last we tried it they weren't resolved but perhaps someone else has succeeded.
    – identigral
    Aug 1 '19 at 22:16
  • 2
    Not aware of any KIs and they might have been resolved, it's tough to tell from the outside. The PMs read the Identity group on Trailblazer, post there and hopefully they'll respond.
    – identigral
    Aug 2 '19 at 6:21
  • 1
    I tried to retrieve metadata for the connected app using my IDE, it did not retrieve Idp certificate Aug 5 '19 at 15:54
  • 1
    Wondering about that 'encryptionCertificate' assignment, the doc says 'The name of the certificate...' Aug 9 '19 at 10:13
  • 1
    permissionSetName should be set to a String and not a List<String> according to the ConnectedApp metadata type doc Jan 28 '20 at 21:04
2
+100

Re 1

In order to get the ConnectedApp.permissionSetName set, you can use SetupEntityAccess and regular DML commands to associate a connected app with a permission set. Only caveat - you can't adjust a managed permission set, so you'll have to create your own new permission set.

Code Sample:

//== (1) Create new permission set ==//
PermissionSet connectedPs = new PermissionSet(
    Label='Connected App User',
    Name='Connected_App_User',
    Description='Gives permissions to use the managed connected app'
);

insert connectedPs;

// Get Managed Connected App
ConnectedApplication myApp = [SELECT Id, Name, OptionsAllowAdminApprovedUsersOnly FROM ConnectedApplication WHERE Name like 'MyManagedAppName' LIMIT 1];

//== (2) Associate the new perm set with the connected app ==//
SetupEntityAccess sea = new SetupEntityAccess(
    ParentId = connectedPs.Id,
    SetupEntityId = app.Id
);

upsert seas;

Re 2

You can't set isAdminApproved via MetadataService API classes because you literally can't read the managed package via the readMetadata() method. As per my comment on the question in this post

I was able to configure and do a readMetadata on isAdminApproved within the packaging org itself, but then ran into the same issue in production - readMetadata doesn't return managed connected apps results, and interestingly enough, workbench also doesn't show managed connected apps (as mentioned in the retrieveConnectedApp() method in the MetadataServicesExamples file (line 1517 at time of posting). I don't know if this is a deficiency in Metadata API or not

So it seems this will be a manual step that's unavoidable for Production environments.


Regardless, here's the script that I ran that would work in non-production environments but didn't run correctly in a production environment, because, again, readMetadata just wouldn't retrieve managed packages :-/

Note: I had to update a few extra properties (like isAdminApproved and permissionSetName) within MetadataService in order to properly read and set these properties. Also needed to update the version to 47 or later.

See all the changes to MetadataService that I made in this gist. Simply search for BMiller and you'll see all the places that I modified code.

Below is the script using the modified version of MetadataService

// Modified and taken from MetadataServicesExamples
MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();
String appFullName = 'myNamespace__My_Connected_App';
MetadataService.ConnectedApp connectedApp =
    (MetadataService.ConnectedApp)service.readMetadata('ConnectedApp', new String[] {appFullName}).getRecords()[0];

System.debug('connectedApp = ' + connectedApp);
System.debug(connectedApp.oauthConfig.isAdminApproved);
System.debug(connectedApp.permissionSetName);
System.debug(connectedApp.description);

// Attempting to set the isAdminApproved setting.
connectedApp.oauthConfig.isAdminApproved = true;

// Works in non-production environments.  In production environments, connectedApp is showing up null
List<MetadataService.UpsertResult> results =
    service.upsertMetadata(new MetadataService.Metadata[] { connectedApp });

MetadataService.UpsertResult upsertResult = results[0];
if(upsertResult.errors!=null) {
    List<String> messages = new List<String>();
    messages.add(
        (upsertResult.errors.size()==1 ? 'Error ' : 'Errors ') +
        'occured processing component ' + upsertResult.fullName + '.');
    for(MetadataService.Error error : upsertResult.errors)
        messages.add(
            error.message + ' (' + error.statusCode + ').' +
            ( error.fields!=null && error.fields.size()>0 ?
             ' Fields ' + String.join(error.fields, ',') + '.' : '' ) );
    if(messages.size()>0)
        System.debug(String.join(messages, ' '));
}
if(!upsertResult.success)
    System.debug('Request failed with no specified error.');
3
  • Would it be possible to create a local clone of the packaged permission set? In Apex. Did you try that? Feb 21 '20 at 13:09
  • 1
    @RobertSösemann Just searched around and didn't fine anything out of the box in Apex. Do you want to post as a question or should I? :-) Feb 23 '20 at 12:01
  • 1
    Hey Brian, I created this here: salesforce.stackexchange.com/q/295652/256 Maybe someone can add the missing piece here. Feb 24 '20 at 13:05
6
+50

I think the second problem you listed is causing the first problem, and unfortunately it's a bit of a layered answer. I'm also assuming that the MetadataService layer you've generated is all correct.

The first part to solve is getting the ConnectedAppOauthConfig.isAdminApproved = true. Now I couldn't get this to set on creation of the Connected App. However, when I update the Connected App just created with the setting of ConnectedAppOauthConfig.isAdminApproved = true it sticks. So you will need to create the Connected App first then update the same app to make the setting stick.

Now going back to the Connected App documentation, the 'permissionSetName' field requires the 'isAdminApproved' to be set to true so we can't set the permission sets on create. I was able to set unmanaged permission sets on the update, but got the following error when trying to set the managed package permission set:

Cannot modify managed object: entity=SetupEntityAccess, component=0J0O000000Ceu83, field=KeyPrefix, state=installed

The ConnectedAppPlugin class might be something you will need to explore as an authorisation alternative if you don't have control of the managed package setup.

15
  • I will grant the bounty to you and it would be nice if you take some time to find answers later. Aug 9 '19 at 10:56
  • 1
    Hoping I got it now :D thank you for sharing that additional context as it definitely helps
    – Clint
    Aug 9 '19 at 14:01
  • 1
    @RobertSösemann Sorry running into a hectic week but would love to post. BTW, those three objects have nothing to do with MDAPI - they're all standard objects in Salesforce and can be simply created and updated like any other standard/custom object via regular insert update upsert DML commands Feb 10 '20 at 17:29
  • 1
    @RobertSösemann Right now I just have the post-install script creating a new permset, granting access to the connected app and applying it to all active users. So I apply the managed package permset as well as this newly created one - so we have two permsets at the end of the day. If I knew this from the beginning, I might have just designed it to have the post-install just create a permset with every permission I needed and leave it separate from the managed package itself, so that I would only need that permset Feb 12 '20 at 20:37
  • 1
    @RobertSösemann You said you made it work for both issues - you got isAdminApproved setting itself in a production environment? That's where I hit a wall. Re posting code - looking to do it over the weekend Feb 12 '20 at 20:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.