001/*
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: AdminTokenAction.java,v 1.14 2009/06/19 02:35:11 bigfatrat Exp $
026 *
027 * Portions Copyrighted 2010-2015 ForgeRock AS.
028 */
029
030package com.sun.identity.security;
031
032import com.iplanet.am.util.AdminUtils;
033import com.iplanet.am.util.SystemProperties;
034import com.iplanet.services.util.Crypt;
035import com.iplanet.sso.SSOException;
036import com.iplanet.sso.SSOToken;
037import com.iplanet.sso.SSOTokenManager;
038import com.sun.identity.authentication.internal.AuthContext;
039import com.sun.identity.authentication.internal.AuthPrincipal;
040import com.sun.identity.common.ShutdownManager;
041import com.sun.identity.shared.debug.Debug;
042import org.forgerock.util.thread.listener.ShutdownListener;
043
044import java.security.PrivilegedAction;
045
046/**
047 * The class is used to perform privileged operations using
048 * <code>java.security.AccessController.doPrivileged()
049 * </code> when trying to
050 * get Application single sign on token. There are four approaches to get single
051 * sign on token. 1. Return the single sign on token of the administrator
052 * configured in <code>serverconfig.xml</code> if the code runs on server
053 * site. 2. If #1 fails, it implies the client is using remote SDK. If
054 * <code>com.sun.identity.security.AdminToken</code> is specified in
055 * <code>AMConfig.properties</code>, we will call this application token
056 * provider plug-in to retrieve the single sign on token. 3. If #2 fails, we
057 * look for <code>com.sun.identity.agents.app.username</code> and
058 * <code>com.iplanet.am.service.password</code> in
059 * <code>AMConfig.properties</code>, if so, we will generate single sign
060 * token of administrator based on the user name and password. 4. If #3 fails,
061 * we look for <code>com.sun.identity.agents.app.username</code> and
062 * <code>com.iplanet.am.service.secret</code> in
063 * <code>AMConfig.properties</code>. If so, we will generate single sign on
064 * token based on the user name and secret.
065 *
066 * Note: Java security permissions check for OpenAM can be enabled
067 * by setting the property <code>com.sun.identity.security.checkcaller</code> to
068 * true in <code>AMConfig.properties</code> file.
069 *
070 * </PRE>
071 *
072 * @supported.all.api
073 */
074public class AdminTokenAction implements PrivilegedAction<SSOToken> {
075    public static final String AMADMIN_MODE = "com.sun.identity.security.amadmin";
076
077    //OPENAM-1109 admin token doesn't get cleared
078    //because SSOTokenManager#isValidToken() doesn't get
079    //real session status. This flag makes AdminTokenAction
080    //to refresh session status and get the true status.
081    public static final String VALIDATE_SESSION = "openam.identity.security.validateSession";
082
083    static final Debug debug = Debug.getInstance("amSecurity");
084
085    private static final String ADMIN_TOKEN_PROVIDER = "com.sun.identity.security.AdminToken";
086    private static final String APP_USERNAME = "com.sun.identity.agents.app.username";
087    private static final String APP_SECRET = "com.iplanet.am.service.secret";
088    private static final String APP_PASSWORD = "com.iplanet.am.service.password";
089
090    /**
091     * Singleton instance.
092     */
093    private static volatile AdminTokenAction instance;
094
095    private final SSOTokenManager tokenManager;
096    private SSOToken appSSOToken;
097    private SSOToken internalAppSSOToken;
098
099    private boolean authInitialized;
100    private final boolean validateSession;
101
102    /**
103     * Returns a cached instance <code>AdminTokenAction</code>.
104     *
105     * @return instance of <code>AdminTokenAction</code>.
106     */
107    public static AdminTokenAction getInstance() {
108        // Safe double-checked locking pattern (instance is volatile):
109        if (instance == null) {
110            synchronized (AdminTokenAction.class) {
111                if (instance == null) {
112                    try {
113                        instance = new AdminTokenAction();
114                    } catch (SSOException e) {
115                        debug.error("AdminTokenAction::init Unable to get SSOTokenManager", e);
116                    }
117                }
118            }
119        }
120        return instance;
121    }
122
123    /**
124     * Default constructor
125     */
126    private AdminTokenAction() throws SSOException {
127        tokenManager = SSOTokenManager.getInstance();
128        ShutdownManager.getInstance().addApplicationSSOTokenDestroyer(new ShutdownListener() {
129            @Override
130            public void shutdown() {
131                AdminTokenAction.reset();
132            }
133        });
134        validateSession = SystemProperties.getAsBoolean(VALIDATE_SESSION);
135    }
136
137    /**
138     * Informs AdminTokenAction that Authentication has been initialized
139     * This class will start using Authentication service to obtain
140     * SSOToken for admin users
141     */
142    public void authenticationInitialized() {
143        authInitialized = true;
144        // Generate the DPro's SSOToken
145        appSSOToken = getSSOToken();
146        if (debug.messageEnabled()) {
147            debug.message("AdminTokenAction:authenticationInit " +
148                    "called. AppSSOToken className=" + (String)
149                    ((appSSOToken == null) ? "null" :
150                            appSSOToken.getClass().getName()));
151        }
152        // Clear internalAppSSOToken
153        internalAppSSOToken = null;
154    }
155
156    /**
157     * Resets cached SSOToken. WITHOUT destroying.  Called when we know the 
158     * token is invalid
159     */
160    public static void invalid() {
161        getInstance().invalidate();
162
163        if (debug.messageEnabled()) {
164            debug.message("AdminTokenAction:invalid called");
165        }
166    }
167
168    private void invalidate() {
169        appSSOToken = null;
170    }
171
172    /**
173     * Resets cached SSOToken.
174     */
175    public static void reset() {
176        getInstance().resetInstance();
177    }
178
179    private void resetInstance() {
180        if (appSSOToken != null) {
181            try {
182                getInstance().tokenManager.destroyToken(appSSOToken);
183            } catch (SSOException ssoe) {
184                debug.error("AdminTokenAction.reset: cannot destroy appSSOToken.", ssoe);
185            }
186            appSSOToken = null;
187        }
188        internalAppSSOToken = null;
189    }
190
191    /* (non-Javadoc)
192     * @see java.security.PrivilegedAction#run()
193     */
194    public SSOToken run() {
195        // Check if we have a valid cached SSOToken
196        if (appSSOToken != null && tokenManager.isValidToken(appSSOToken)) {
197            try {
198                if (validateSession) {
199                    tokenManager.refreshSession(appSSOToken);
200                }
201                if (tokenManager.isValidToken(appSSOToken)) {
202                    return appSSOToken;
203                }
204            } catch (SSOException ssoe) {
205                debug.error("AdminTokenAction.reset: couldn't retrieve valid token.", ssoe);
206            }
207        }
208
209        // Check if internalAppSSOToken is present
210        if (internalAppSSOToken != null && tokenManager.isValidToken(internalAppSSOToken)) {
211            return internalAppSSOToken;
212        }
213
214        // Try getting the token from serverconfig.xml
215        SSOToken answer = getSSOToken();
216        if (answer != null) {
217            if (!SystemProperties.isServerMode() || authInitialized) {
218                appSSOToken = answer;
219            }
220            return answer;
221        } else if (debug.messageEnabled()) {
222            debug.message("AdminTokenAction::run Unable to get SSOToken from serverconfig.xml");
223        }
224
225        // Check for configured Application Token Provider in AMConfig.properties
226        String appTokenProviderName = SystemProperties.get(ADMIN_TOKEN_PROVIDER);
227        if (appTokenProviderName != null) {
228            try {
229                AppSSOTokenProvider appSSOTokenProvider =
230                        Class.forName(appTokenProviderName).asSubclass(AppSSOTokenProvider.class).newInstance();
231
232                answer = appSSOTokenProvider.getAppSSOToken();
233            } catch (Throwable ce) {
234                debug.error("AdminTokenAction: Exception while calling appSSOToken provider plugin.", ce);
235            }
236        } else {
237            String appUserName = SystemProperties.get(APP_USERNAME);
238            String encryptedPassword = SystemProperties.get(APP_SECRET);
239            String password = SystemProperties.get(APP_PASSWORD);
240            String appPassword = null;
241
242            if (password != null && !password.isEmpty()) {
243                appPassword = password;
244            } else if (encryptedPassword != null && !encryptedPassword.isEmpty()) {
245                try {
246                    appPassword = Crypt.decode(encryptedPassword);
247                } catch (Throwable t) {
248                    debug.error("AdminTokenAction::run Unable to decrypt secret password", t);
249                }
250            }
251
252            if (appUserName == null || appUserName.isEmpty() || appPassword == null || appPassword.isEmpty()) {
253                debug.error("AdminTokenAction: App user name or password is empty");
254            } else {
255                if (debug.messageEnabled()) {
256                    debug.message("App user name: " + appUserName);
257                }
258                SystemAppTokenProvider tokenProd =
259                        new SystemAppTokenProvider(appUserName, appPassword);
260                answer = tokenProd.getAppSSOToken();
261            }
262        }
263
264        // If SSOToken is NULL, AM would not bootstrap: fatal error
265        if (answer == null) {
266            final String errorMessage = "AdminTokenAction: FATAL ERROR: Cannot obtain Application SSO token.";
267            debug.error(errorMessage);
268            throw new AMSecurityPropertiesException(errorMessage);
269        } else if (!SystemProperties.isServerMode() || authInitialized) {
270            // Cache the SSOToken if not in server mode (i.e., in the
271            // case of client sdk) or if the authN has been initialized
272            appSSOToken = answer;
273        }
274        return answer;
275    }
276
277    private SSOToken getSSOToken() {
278        // Please NEVER make this method public!!!!!!!!!!
279        // This can only be used in server site. 
280        SSOToken ssoAuthToken = null;
281
282        try {
283            //call method directly
284            if (AdminUtils.getAdminPassword() != null) {
285                String adminDN = AdminUtils.getAdminDN();
286                String adminPassword = new String(AdminUtils.getAdminPassword());
287                if (!authInitialized && (SystemProperties.isServerMode() ||
288                        SystemProperties.get(AMADMIN_MODE) != null)) {
289                    // Use internal auth context to get the SSOToken
290                    AuthContext ac = new AuthContext(new AuthPrincipal(adminDN),
291                            adminPassword.toCharArray());
292                    internalAppSSOToken = ssoAuthToken = ac.getSSOToken();
293                } else {
294                    // Copy the authentication state
295                    boolean authInit = authInitialized;
296                    if (authInit) {
297                        authInitialized = false;
298                    }
299
300                    // Obtain SSOToken using AuthN service
301                    ssoAuthToken = new SystemAppTokenProvider(adminDN, adminPassword).getAppSSOToken();
302
303                    // Restore the authentication state
304                    if (authInit && ssoAuthToken != null) {
305                        authInitialized = true;
306                    }
307                }
308            }
309        } catch (NoClassDefFoundError ne) {
310            debug.error("AdminTokenAction::getSSOToken Not found AdminDN and AdminPassword.", ne);
311
312        } catch (Throwable t) {
313            debug.error("AdminTokenAction::getSSOToken Exception reading from serverconfig.xml", t);
314        }
315        return ssoAuthToken;
316    }
317}