Master-Detail Relationships and “Managed Released” Packages

Making your Force.com package “managed released” allows your users to safely upgrade to newer versions of your package in the future. Unfortunately this is accomplished by forcing your future data model to be a superset of your entire current data model – there is no way to limit what is exposed so forget about encapsulation.

In the real world all data models change over time.

One way to work-around the problem of needing to change a field is to just add another field and stop using the original field. Your customer’s code will continue to compile and run though it is unlikely that the expected functionality will result as their code is relying on the original now unused field rather than the replacement field. And unless they have good quality unit tests written it may take a while for them to discover the problem.

I hit a road-block case for which the work-around does not work. I had a master-detail relationship and wanted to replace the master object with a different object. During the refactoring I created a trigger to always populate a reference to a dummy master object in the detail object – not nice but it kept the unit tests working. But when I then looked at the user interface layouts I discovered that the reference to the master object cannot be removed from the layout and its required check-box cannot be unchecked. So the user is forced to enter a value (an irrelevant reference to an irrelevant object) before the trigger can do its work – hardly acceptable usability.

All that can be done to “solve” the problem is to put the code in a new “managed released” package using a different namespace. This means that new instances will work fine. But it also means that there is no upgrade path at all for the existing old “managed released” instances. Also creating a new package is an error-prone process as the packaging meta-data has to be manually re-created.

Please vote for Make managed packages less restrictive / more intelligent – the present mechanisms are just too basic to meet real-world needs.

Advertisements

Securing Custom Settings in a Managed Package

Custom settings that have public visibility provide a good way of offering configuration options for a managed package. But the security mechanisms of normal objects do not apply and there is no “password” data type that masks the characters entered. So where secure values such as authorization keys to external web services need to be configured, more work needs to be done.

The solution is described in Secure Coding Storing Secrets – Apex and Visualforce Applications but it is only thanks to some feedback I got as part of a security audit that this finally made sense to me. The approach is to use protected custom settings instead of public ones as these are not visible or editable from the org that has the managed package installed. (Watch out: this public/protected visibility choice cannot be changed once the custom setting is created so think hard up front…) And then create a Visualforce page and controller to provide the access as these can have the necessary security measures applied such as limiting which profiles can access them and making use of apex:inputSecret that provides “password” style input. So instead of the free custom settings UI, you will need to spend several hours of work creating custom UI.

I had some difficulty getting the programmatic manipulation of the custom settings to work for the case where there was no instance already defined with an INVALID_CROSS_REFERENCE_KEY error being thrown on the id field. The code below is what I ended up with to support both creating the custom settings instance or editing an existing custom settings instance. (This code is only manipulating one field in one custom setting – ProtectedLicenseKeys__c.MDGuidelines__c – but in the product there are multiple fields and multiple custom settings handled by the one page.) There is extra ugliness because I wanted to give some feedback in the form of the length of the value saved as any entered values are deliberately not echoed back after “Save” is clicked.

public with sharing class SecureSettingsController {
    private void initLicenseKeys() {
        ProtectedLicenseKeys__c instance = ProtectedLicenseKeys__c.getInstance();
        licenseKeys = instance != null
                ? instance
                : new ProtectedLicenseKeys__c(SetupOwnerId = UserInfo.getOrganizationId());
        lengthOfMDGuidelines = licenseKeys.MDGuidelines__c != null ? licenseKeys.MDGuidelines__c.length() : 0;
        // Ensure secret value not in the view state
        licenseKeys.MDGuidelines__c = null;
    }
    public ProtectedLicenseKeys__c licenseKeys {
        get {
            if (licenseKeys == null) {
                initLicenseKeys();
            }
            return licenseKeys;
        }
        set;
    }
    public Integer lengthOfMDGuidelines {
        get {
            if (lengthOfMDGuidelines == null) {
                initLicenseKeys();
            }
            return lengthOfMDGuidelines;
        }
        set;
    }
    public PageReference saveLicenseKeys() {
        upsert licenseKeys;
        licenseKeys = null;
        return null;
    }
}

The following page is simple enough, and uses apex:inputSecret. To make the page a bit easier to access, I added an additional tab called “Security Settings” that displays the page and is accessed via the “All Tabs” tab. This is only available to “System Administrator” profile users.

<apex:page controller="SecureSettingsController">
    <apex:form id="mainForm">
        <apex:sectionHeader title="Secure Settings" />
        <apex:panelGrid columns="1">
            <apex:outputText >
                For the secure values, existing values are not displayed and characters entered are masked when echoed.
                So the length of the currently saved value is displayed to show where values are set.
            </apex:outputText>
            <apex:outputText > </apex:outputText>
        </apex:panelGrid>
        <apex:pageMessages />
        <apex:pageBlock title="License Keys" mode="edit">
            <apex:pageBlockButtons >
                <apex:commandButton value="Save" action="{!saveLicenseKeys}" />
            </apex:pageBlockButtons>
            <apex:pageBlockSection columns="1">
                <apex:pageBlockSectionItem >
                    <apex:outputLabel value="MDGuidelines"/>
                    <apex:outputPanel >
                        <apex:inputSecret value="{!licenseKeys.MDGuidelines__c}" size="50" />
                        <apex:outputText value=" ({!lengthOfMDGuidelines} characters)"/>
                    </apex:outputPanel>
                </apex:pageBlockSectionItem>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
</apex:page>

Chatter but no apex:inputDate

I just ran (again) into pain with what should be the trivial problem of allowing a user to enter a date via a date picker pop-up. I am creating a site that includes a verification Visualforce page where the user inputs their date of birth. I know that this input field should always appear so CRUD security and FLS are not needed. But because the only way to get the date picker pop-up is to back the field with an SObject date field that security is stopping the field from being displayed despite my best efforts to make the correct settings. (Also those settings have to be made manually for sites as they cannot be packaged and so mis-configuration on a regular basis is likely.)

This is an example where an overly complicated work-around – using an SObject – really hurts when you have to use it in a more awkward situation.

But despite more than 2 years of comments:

and the addition needed to the platform being relatively straightforward, there is no sign that the problem will be addressed.