Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Authentication & Authorization of NetBeans Platform Applications

DZone's Guide to

Authentication & Authorization of NetBeans Platform Applications

· Java Zone
Free Resource

Microservices! They are everywhere, or at least, the term is. When should you use a microservice architecture? What factors should be considered when making that decision? Do the benefits outweigh the costs? Why is everyone so excited about them, anyway?  Brought to you in partnership with IBM.

Authentication and authorization for NetBeans Platform applications had me going for a while, until I thought of it as a non-linear service. (I.e., it doesn't need to happen at application start up, but anywhere once a TopComponent is opened). I haven't found anything on other sites and believe this to be a headache for many NetBeans Platform Newbies like me.
 
So I rolled my own implementation that looks like this.

Service Interface

public interface AppUserService {

    //Authenticate the user and store him/her in the implementation's private member
    public void authenticateAppUser(String userName,char[] password);
 
    //Checks if the implementation's private member is authenticated
    public boolean isAppUserAuthenticated();
 
    //Checks if user in the implementation class's private member is authorized
    public boolean isAppUserAuthorized(String appFunction);
 
    //If the user requires 2 or more roles to access a function
    public boolean isAppUserAuthorized(String[] appFunctions);
 
    //Some users (i.e. regional managers) must be able to access more than one AppEntity (office/company etc.)
    public List<AppEntityService> getAppEntities();
 
    //Uses a custom JPanel and a NotifyDescriptor implementation
    public void displayAppUserLogin();
 
    //Same as display login
    public void displayNotAuthorized();
 
    //Primary key: MySQL Type BINARY(16) for GUIDs
    public byte[] getUserGUID();
 
    //Hack to get a singleton, the implementation calls: private static synchronized AppUserService getSingleton()
    public AppUserService getDefault();
 
    //Load the AppUser's Authorized functions from the database
    public void loadAuthorizedFunctions();
 
    //Some Candy (that is, not really needed): A central place in the implementation to create Application Specific functions. Typically like "User Editor","Customer Viewer" etc.
    public void checkCreateAppFunctions();
 
    //More Candy: Create a default user with a complicated password that can open the application on a fresh install
    public void checkCreateDefaultAdministrator();
  
    //Log the user off. That is, set the implementation's private members to null
    public void revokeAuthentication();
}
 
On the implementation (only some of the methods below):
 
    // The hack that was mentioned (perhaps a lack of knowledge about Lookups)
    @Override
    public AppUserService getDefault() {
        return getSingleton();
    }
 
    private static synchronized AppUserService getSingleton() {
        if (instance == null) {
            instance = new AppUserProvider();
        }
        return instance;
    }
 
    @Override
    public void displayAppUserLogin() {
        LoginDialogJPanel loginPanel = new LoginDialogJPanel();
        NotifyDescriptor nd;
        // Let's give the user 3 tries
        for (int i = 0; i < 3; i++) {
            nd = new NotifyDescriptor(
                    loginPanel,
                    "Authentication Required",
                    NotifyDescriptor.OK_CANCEL_OPTION,
                    NotifyDescriptor.PLAIN_MESSAGE,
                    null,
                    NotifyDescriptor.YES_OPTION
                    );
 
            if (DialogDisplayer.getDefault().notify(nd) == NotifyDescriptor.OK_OPTION) {
                authenticateAppUser(loginPanel.getAppUserID(), loginPanel.getPassword());
                if (isAppUserAuthenticated){
                    break;
                }
            } else {
                break;
            }
        }
    }
 
    @Override
    public void checkCreateAppFunctions() {
        //Call a function to check/create these in the database
        appFunctionCheckCreate("Customer Viewer");
        appFunctionCheckCreate("Customer Editor");
    }
 
    //This function uses the jasypt library for password encryption
    //The following MUST always be included in the about box's credit section.
    //Java Simplified Encryption Library found at http://www.jasypt.org/
    //To review the license goto http://www.jasypt.org/license.html
    @Override
    public void authenticateAppUser(String userName, char[] password) {
        checkCreateDefaultAdministrator();
        EntityManagerFactory emf = null;
        EntityManager em = null;
 
        Appuser usr = null;
        try{
            emf = Persistence.createEntityManagerFactory("CUSTOMERPU");
            em = emf.createEntityManager();
            usr = (Appuser) em.createNamedQuery("Appuser.findByAppUserID").setParameter("appUserID", userName).getSingleResult();
        } catch (Exception ex){
            Logger logger = Logger.getLogger(this.getClass().getName());
            logger.log(Level.WARNING, "Database exception", ex);
        }
        if (usr != null) {
            //jasypt class instance:
            StrongPasswordEncryptor passwordEncryptor = new StrongPasswordEncryptor();
            String pass = new String(password);
            if (passwordEncryptor.checkPassword(pass, usr.getPassword())) {
                this.isAppUserAuthenticated = true;
                appUserGUID = usr.getAppUserGUID();
                loadAuthorizedFunctions();
                loadEntities();
            } else {
                this.isAppUserAuthenticated = false;
            }
 
        }
    }
 
And finally, usage in a TopComponent:
    @Override
    protected void componentActivated() {
        AppUserService aus = Lookup.getDefault().lookup(AppUserService.class).getDefault();
        this.setVisible(false);
        if (!aus.isAppUserAuthenticated()){
           aus.displayAppUserLogin();
        }
        if (!aus.isAppUserAuthenticated()){
            this.close();
            return;
        }
        if (!aus.isAppUserAuthorized("Customer Editor")){
            aus.displayNotAuthorized();
            this.close();
            return;
        }
        this.setVisible(true);
        super.componentActivated();
    }
 
 
    //To avoid weird (Unauthorized Dialogue screen shows when the app loads) behavior at start up or for multiple users on the PC log in, use:
    @Override
    public int getPersistenceType() {
        return TopComponent.PERSISTENCE_NEVER;
    }

Since the form can be closed from the "componentActivated" method, null checks must be done for any code assuming initialized variables:

    @Override
    public void componentClosed() {
        if (result != null){
            result.removeLookupListener(this);
            result = null;
        }
    }

Diagram

 

 
Conclusion
 
One thing that would have been nice would be to dynamically set the "userdir" once the user has logged on. So far it doesn't seem plausible since log in happens after the folder system has been initialized. Therefore the PERSISTENCE_NEVER part actually play in the design's favor.

Warm Regards from a sunny South Africa. :-D

 

Discover how the Watson team is further developing SDKs in Java, Node.js, Python, iOS, and Android to access these services and make programming easy. Brought to you in partnership with IBM.

Topics:

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}