Friday, March 3, 2017

A Work-Around for Security in ADF Essentials 12.2.1

In my previous post, I talked about ADF Essentials and configuring it for authentication and authorization.  And I reported a problem with the new 12.2.1 versions that raises an exception trying to initialize a UserProfile object.  I surmise that Oracle's newer versions call a facility that is only in Oracle WebLogic server, and NOT in the typical application server running ADF Essentials, notably Glassfish.  But, until Oracle fixes this (PLEASE Oracle) I have tested a workaround.  This was suggested by a comment on this post by Andrejus Baranovski (and if you are using ADF, you NEED to read Andrejus' blog - it is GREAT!)  http://andrejusb.blogspot.com/2012/10/adf-essentials-security-implementation.html.

The comment is by Ivan Melluso, who said:
I'm using JDeveloper 12.2.1, Glassfish 4.1.1, ADF Essentials 12.2.1;
I obtained
NoClassDefFoundError: Could not initialize class oracle.adf.share.security.identitymanagement.UserProfile
Then I discover the UserProfile class initialize a MDS session, that can't exists in GlassFish;

My solution:
- Decompile the class
- Rewrite it preserving package name and all public methods
- Create a new jar
- Place it in /lib directory

I restart the server 5 times to check the class conflict (same class exists in adf-share-ca.jar and my jar) and all 5 times I don't obtain the error
So the work-around is to write our own version of UserProfile and get the application to call ours instead of Oracle's original.  Here is my version:

package oracle.adf.share.security.identitymanagement;

import java.io.Serializable;

import java.security.Principal;

import java.util.HashMap;

import javax.faces.context.FacesContext;

public class UserProfile implements Serializable {
    @SuppressWarnings("compatibility:-6264967054558894560")
    private static final long serialVersionUID = 1L;

    public UserProfile() {
        super();
    }

    java.util.HashMap properties = new HashMap();


    public Object getProperty(String propName) {
        return properties.get(propName);
    }

    public void setProperty(String name, Object value) {
        properties.put(name, value);
    }

    public void setBusinessCity(String businessCity) {
        setProperty("businessCity", businessCity);
    }

    public String getBusinessCity() {
        return (String)getProperty("businessCity");
    }

    public void setBusinessCountry(String businessCountry) {
        setProperty("businessCountry", businessCountry);
    }

    public String getBusinessCountry() {
        return (String)getProperty("businessCountry");
    }

    public void setBusinessEmail(String businessEmail) {
        setProperty("businessEmail", businessEmail);
    }

    public String getBusinessEmail() {
        return (String)getProperty("businessEmail");
    }

    public void setBusinessFax(String businessFax) {
        setProperty("businessFax", businessFax);
    }

    public String getBusinessFax() {
        return (String)getProperty("businessFax");
    }

    public void setBusinessMobile(String businessMobile) {
        setProperty("businessMobile", businessMobile);
    }

    public String getBusinessMobile() {
        return (String)getProperty("businessMobile");
    }

    public void setBusinessPager(String businessPager) {
        setProperty("businessPager", businessPager);
    }

    public String getBusinessPager() {
        return (String)getProperty("businessPager");
    }

    public void setBusinessPhone(String businessPhone) {
        setProperty("businessPhone", businessPhone);
    }

    public String getBusinessPhone() {
        return (String)getProperty("businessPhone");
    }

    public void setBusinessPOBox(String businessPOBox) {
        setProperty("businessPOBox", businessPOBox);
    }

    public String getBusinessPOBox() {
        return (String)getProperty("businessPOBox");
    }

    public void setBusinessPostalAddr(String businessPostalAddr) {
        setProperty("businessPostalAddr", businessPostalAddr);
    }

    public String getBusinessPostalAddr() {
        return (String)getProperty("businessPostalAddr");
    }

    public void setBusinessPostalCode(String businessPostalCode) {
        setProperty("businessPostalCode", businessPostalCode);
    }

    public String getBusinessPostalCode() {
        return (String)getProperty("businessPostalCode");
    }

    public void setBusinessState(String businessState) {
        setProperty("businessState", businessState);
    }

    public String getBusinessState() {
        return (String)getProperty("businessState");
    }

    public void setBusinessStreet(String businessStreet) {
        setProperty("businessStreet", businessStreet);
    }

    public String getBusinessStreet() {
        return (String)getProperty("businessStreet");
    }

    public void setDateofBirth(String dateofBirth) {
        setProperty("dateofBirth", dateofBirth);
    }

    public String getDateofBirth() {
        return (String)getProperty("dateofBirth");
    }

    public void setDateofHire(String dateofHire) {
        setProperty("dateofHire", dateofHire);
    }

    public String getDateofHire() {
        return (String)getProperty("dateofHire");
    }

    public void setDefaultGroup(String defaultGroup) {
        setProperty("defaultGroup", defaultGroup);
    }

    public String getDefaultGroup() {
        return (String)getProperty("defaultGroup");
    }

    public void setDepartment(String department) {
        setProperty("department", department);
    }

    public String getDepartment() {
        return (String)getProperty("department");
    }

    public void setDepartmentNumber(String departmentNumber) {
        setProperty("departmentNumber", departmentNumber);
    }

    public String getDepartmentNumber() {
        return (String)getProperty("departmentNumber");
    }

    public void setDescription(String description) {
        setProperty("description", description);
    }

    public String getDescription() {
        return (String)getProperty("description");
    }

    public void setDisplayName(String displayName) {
        setProperty("displayName", displayName);
    }

    public String getDisplayName() {
        return (String)getProperty("displayName");
    }

    public void setEmployeeNumber(String employeeNumber) {
        setProperty("employeeNumber", employeeNumber);
    }

    public String getEmployeeNumber() {
        return (String)getProperty("employeeNumber");
    }

    public void setEmployeeType(String employeeType) {
        setProperty("employeeType", employeeType);
    }

    public String getEmployeeType() {
        return (String)getProperty("employeeType");
    }

    public void setFirstName(String firstName) {
        setProperty("firstName", firstName);
    }

    public String getFirstName() {
        return (String)getProperty("firstName");
    }

    public void setGivenName(String givenName) {
        setProperty("givenName", givenName);
    }

    public String getGivenName() {
        return (String)getProperty("givenName");
    }

    public void setGUID(String GUID) {
        setProperty("GUID", GUID);
    }

    public String getGUID() {
        return (String)getProperty("GUID");
    }

    public void setHomeAddress(String homeAddress) {
        setProperty("homeAddress", homeAddress);
    }

    public String getHomeAddress() {
        return (String)getProperty("homeAddress");
    }

    public void setHomePhone(String homePhone) {
        setProperty("homePhone", homePhone);
    }

    public String getHomePhone() {
        return (String)getProperty("homePhone");
    }

    public void setInitials(String initials) {
        setProperty("initials", initials);
    }

    public String getInitials() {
        return (String)getProperty("initials");
    }

    public void setJpegPhoto(byte[] jpegPhoto) {
        setProperty("jpegPhoto", jpegPhoto);
    }

    public byte[] getJpegPhoto() {
        return (byte[])getProperty("jpegPhoto");
    }

    public void setLastName(String lastName) {
        setProperty("lastName", lastName);
    }

    public String getLastName() {
        return (String)getProperty("lastName");
    }

    public void setMaidenName(String maidenName) {
        setProperty("maidenName", maidenName);
    }

    public String getMaidenName() {
        return (String)getProperty("maidenName");
    }

    public void setManager(String manager) {
        setProperty("manager", manager);
    }

    public String getManager() {
        return (String)getProperty("manager");
    }

    public void setMiddleName(String middleName) {
        setProperty("middleName", middleName);
    }

    public String getMiddleName() {
        return (String)getProperty("middleName");
    }

    public void setName(String name) {
        setProperty("name", name);
    }

    public String getName() {
        return (String)getProperty("name");
    }

    public void setNameSuffix(String nameSuffix) {
        setProperty("nameSuffix", nameSuffix);
    }

    public String getNameSuffix() {
        return (String)getProperty("nameSuffix");
    }

    public void setOrganization(String organization) {
        setProperty("organization", organization);
    }

    public String getOrganization() {
        return (String)getProperty("organization");
    }

    public void setOrganizationalUnit(String organizationalUnit) {
        setProperty("organizationalUnit", organizationalUnit);
    }

    public String getOrganizationalUnit() {
        return (String)getProperty("organizationalUnit");
    }

    public void setPreferredLanguage(String preferredLanguage) {
        setProperty("preferredLanguage", preferredLanguage);
    }

    public String getPreferredLanguage() {
        return (String)getProperty("preferredLanguage");
    }

    public Principal getPrincipal() {
        Principal principal =
            FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
        return principal;
    }

    public void setProperties(HashMap properties) {
        setProperty("properties", properties);
    }

    public HashMap getProperties() {
        return (HashMap)getProperty("properties");
    }

    public void setTimeZone(String timeZone) {
        setProperty("timeZone", timeZone);
    }

    public String getTimeZone() {
        return (String)getProperty("timeZone");
    }

    public void setTitle(String title) {
        setProperty("title", title);
    }

    public String getTitle() {
        return (String)getProperty("title");
    }

    public void setUIAccessMode(String UIAccessMode) {
        setProperty("UIAccessMode", UIAccessMode);
    }

    public String getUIAccessMode() {
        return (String)getProperty("UIAccessMode");
    }

    public void setUniqueName(String uniqueName) {
        setProperty("uniqueName", uniqueName);
    }

    public String getUniqueName() {
        return (String)getProperty("uniqueName");
    }

    public void setUserID(String userID) {
        setProperty("userID", userID);
    }

    public String getUserID() {
        return (String)getProperty("userID");
    }

    public void setUserName(String userName) {
        setProperty("userName", userName);
    }

    public String getUserName() {
        return (String)getProperty("userName");
    }

    public void setWirelessAcctNumber(String wirelessAcctNumber) {
        setProperty("wirelessAcctNumber", wirelessAcctNumber);
    }

    public String getWirelessAcctNumber() {
        return (String)getProperty("wirelessAcctNumber");
    }
   
    public void saveProfile () {
        // do nothing
    }

}

Now, how to package it, and where to put it.  We need to make sure that ours is used instead of Oracle's.  We could do what Ivan did, and package it in our own jar and add the jar to the lib folder.  Well, I'm not confident that it will always use mine instead of Oracle's - depends on what the class loader does, and I'm not really that experienced with how java and Glassfish really work.  So I got out my trusty 7-Zip and replaced UserProfile in Oracle's jar, adf-share-ca.jar with my version of the class.  This works, no more Exception.

The real question - is this legal? If I started using this in production, could Oracle come after me.  Honestly, I doubt that they would bother, but could they?  On the other hand, if Oracle would fix this, I wouldn't do it.  After all, to deploy my application, I have to deploy MY version of adf-share-ca.jar, and I have to make a new version of the jar anytime Oracle's changes.

3 comments:

  1. Thanks man, this post can save so many guys of being fired! I implemented another solution but this is great as well.

    ReplyDelete
  2. hi John, thanks for your post.. this was really helpful.. i have tried your solution but when i tried to login i encounter this error.

    ADFc: No exception handler was found for an application exception.
    java.lang.NoSuchMethodError: oracle.adf.share.security.identitymanagement.UserProfile.(Ljava/lang/String;)V
    at oracle.adf.share.security.SecurityContextImpl.getUserProfile(SecurityContextImpl.java:147)
    at oracle.adfinternal.share.util.AdfShareInternalUtils.getCurrentUserProfile(AdfShareInternalUtils.java:94)
    at oracle.adfinternal.share.util.AdfShareInternalUtils.getSecurityContextUserIdentifier(AdfShareInternalUtils.java:47)
    at oracle.adf.share.config.ADFContextMDSConfigHelperImpl.createBaseSessionOptions(ADFContextMDSConfigHelperImpl.java:148)
    at oracle.adf.share.config.ADFContextMDSConfigHelperImpl.createSessionOptions(ADFContextMDSConfigHelperImpl.java:168)
    at oracle.adf.share.config.ADFContextMDSConfigHelperImpl.createMDSSession(ADFContextMDSConfigHelperImpl.java:69)
    at oracle.adf.share.ADFContext.getMDSSessionAsObject(ADFContext.java:2356)


    i use glassfish 4.1 + adf essentials 12.2.1.2 + apache shiro.
    i think i missed some method in the UserProfile Class.

    ReplyDelete
    Replies
    1. My version was based on an earlier version of UserProfile than 12.2.1.2, so maybe the later version has a new method that I don't implement. You may have to decompile the class in THAT version and compare it with my version.
      You say you are using Apache Shiro. I've tried this with plain JEE security (configured in web.xml) and with Spring Security, but I've never used Shiro - so I wonder if this is part of the problem. If you figure this out, I hope you'll share what you learn.

      Delete