001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019:
020: package org.apache.geronimo.openejb;
021:
022: import java.io.IOException;
023: import java.net.URI;
024: import java.util.Arrays;
025: import java.util.Collections;
026: import java.util.List;
027: import java.util.Map;
028:
029: import javax.security.auth.Destroyable;
030: import javax.security.auth.Subject;
031: import javax.security.auth.callback.Callback;
032: import javax.security.auth.callback.CallbackHandler;
033: import javax.security.auth.callback.NameCallback;
034: import javax.security.auth.callback.PasswordCallback;
035: import javax.security.auth.callback.UnsupportedCallbackException;
036: import javax.security.auth.login.LoginException;
037: import javax.security.auth.spi.LoginModule;
038:
039: import org.apache.commons.logging.Log;
040: import org.apache.commons.logging.LogFactory;
041: import org.apache.geronimo.security.SubjectId;
042: import org.apache.openejb.client.ClientSecurity;
043: import org.apache.openejb.client.ServerMetaData;
044:
045: /**
046: * OpenejbRemoteLoginModule uses the openejb protocol to communicate with the server to be used for ejbs and try to
047: * login on that server. If login succeeds an identity token is added to the private credentials of the Subject
048: * that can be used on further calls to identify the client. Note this should only be used on secure networks or
049: * with secured communication with openejb, as sniffing the identity token gives you all the permissions of the user you
050: * sniffed.
051: * <p/>
052: * This login module checks security credentials so the lifecycle methods must return true to indicate success
053: * or throw LoginException to indicate failure.
054: *
055: * @version $Rev: 616465 $ $Date: 2008-01-29 10:27:49 -0800 (Tue, 29 Jan 2008) $
056: */
057: public class OpenejbRemoteLoginModule implements LoginModule {
058: private static Log log = LogFactory
059: .getLog(OpenejbRemoteLoginModule.class);
060:
061: private static final String SECURITY_REALM_KEY = "RemoteSecurityRealm";
062: private static final String SECURITY_REALM_KEY_LONG = OpenejbRemoteLoginModule.class
063: .getName()
064: + "." + SECURITY_REALM_KEY;
065: private static final String SERVER_URI_KEY = "ServerURI";
066: private static final String SERVER_URI_KEY_LONG = OpenejbRemoteLoginModule.class
067: .getName()
068: + "." + SERVER_URI_KEY;
069: public final static List<String> supportedOptions = Collections
070: .unmodifiableList(Arrays.asList(SECURITY_REALM_KEY,
071: SERVER_URI_KEY, SECURITY_REALM_KEY_LONG,
072: SERVER_URI_KEY_LONG));
073:
074: private Subject subject;
075: private CallbackHandler callbackHandler;
076: private String securityRealm;
077: private URI serverURI;
078: private SubjectId identity;
079: private boolean loginSucceeded;
080: private ServerIdentityToken sit;
081:
082: public void initialize(Subject subject,
083: CallbackHandler callbackHandler,
084: Map<String, ?> sharedState, Map<String, ?> options) {
085: this .subject = subject;
086: this .callbackHandler = callbackHandler;
087: for (Object option : options.keySet()) {
088: if (!supportedOptions.contains(option)) {
089: log.warn("Ignoring option: " + option
090: + ". Not supported.");
091: }
092: }
093: securityRealm = (String) options.get(SECURITY_REALM_KEY);
094: if (securityRealm == null) {
095: securityRealm = (String) options
096: .get(SECURITY_REALM_KEY_LONG);
097: }
098:
099: String serverURIstring = (String) options.get(SERVER_URI_KEY);
100: if (serverURIstring == null) {
101: serverURIstring = (String) options.get(SERVER_URI_KEY_LONG);
102: }
103: serverURI = URI.create(serverURIstring);
104:
105: }
106:
107: public boolean login() throws LoginException {
108: loginSucceeded = false;
109: Callback[] callbacks = new Callback[] {
110: new NameCallback("username"),
111: new PasswordCallback("passsword", false) };
112: try {
113: callbackHandler.handle(callbacks);
114: } catch (IOException e) {
115: throw (LoginException) new LoginException(
116: "Could not execute callbacks").initCause(e);
117: } catch (UnsupportedCallbackException e) {
118: throw (LoginException) new LoginException(
119: "Could not execute callbacks").initCause(e);
120: }
121: String userName = ((NameCallback) callbacks[0]).getName();
122: String password = new String(((PasswordCallback) callbacks[1])
123: .getPassword());
124: identity = (SubjectId) ClientSecurity.directAuthentication(
125: securityRealm, userName, password, new ServerMetaData(
126: serverURI));
127: loginSucceeded = true;
128: return true;
129: }
130:
131: /*
132: * @exception LoginException if login succeeded but commit failed.
133: *
134: * @return true if login succeeded and commit succeeded, or false if login failed but commit succeeded.
135: */
136: public boolean commit() throws LoginException {
137: if (loginSucceeded) {
138: if (identity != null) {
139: sit = new ServerIdentityToken(serverURI, identity);
140: subject.getPrivateCredentials().add(sit);
141: }
142: }
143: // Clear out the private state
144: identity = null;
145: return loginSucceeded;
146: }
147:
148: public boolean abort() throws LoginException {
149: if (loginSucceeded) {
150: // Clear out the private state
151: identity = null;
152: sit = null;
153: }
154: return loginSucceeded;
155: }
156:
157: public boolean logout() throws LoginException {
158: // Clear out the private state
159: loginSucceeded = false;
160: identity = null;
161: if (sit != null) {
162: if (!subject.isReadOnly()) {
163: subject.getPrivateCredentials().remove(sit);
164: } else {
165: try {
166: if (sit instanceof Destroyable) {
167: // Try to destroy the credential
168: try {
169: ((Destroyable) sit).destroy();
170: } catch (Exception e) {
171: throw new LoginException();
172: }
173: } else {
174: throw new LoginException();
175: }
176: } finally {
177: sit = null;
178: }
179: }
180: }
181: sit = null;
182: return true;
183: }
184: }
|