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
021 package org.apache.geronimo.openejb;
022
023 import java.io.IOException;
024 import java.net.URI;
025 import java.util.Arrays;
026 import java.util.Collections;
027 import java.util.List;
028 import java.util.Map;
029
030 import javax.security.auth.Destroyable;
031 import javax.security.auth.Subject;
032 import javax.security.auth.callback.Callback;
033 import javax.security.auth.callback.CallbackHandler;
034 import javax.security.auth.callback.NameCallback;
035 import javax.security.auth.callback.PasswordCallback;
036 import javax.security.auth.callback.UnsupportedCallbackException;
037 import javax.security.auth.login.LoginException;
038 import javax.security.auth.spi.LoginModule;
039
040 import org.apache.commons.logging.Log;
041 import org.apache.commons.logging.LogFactory;
042 import org.apache.geronimo.security.SubjectId;
043 import org.apache.openejb.client.ClientSecurity;
044 import org.apache.openejb.client.ServerMetaData;
045
046 /**
047 * OpenejbRemoteLoginModule uses the openejb protocol to communicate with the server to be used for ejbs and try to
048 * login on that server. If login succeeds an identity token is added to the private credentials of the Subject
049 * that can be used on further calls to identify the client. Note this should only be used on secure networks or
050 * with secured communication with openejb, as sniffing the identity token gives you all the permissions of the user you
051 * sniffed.
052 * <p/>
053 * This login module checks security credentials so the lifecycle methods must return true to indicate success
054 * or throw LoginException to indicate failure.
055 *
056 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
057 */
058 public class OpenejbRemoteLoginModule implements LoginModule {
059 private static Log log = LogFactory.getLog(OpenejbRemoteLoginModule.class);
060
061
062 private static final String SECURITY_REALM_KEY = "RemoteSecurityRealm";
063 private static final String SECURITY_REALM_KEY_LONG = OpenejbRemoteLoginModule.class.getName() + "." + SECURITY_REALM_KEY;
064 private static final String SERVER_URI_KEY = "ServerURI";
065 private static final String SERVER_URI_KEY_LONG = OpenejbRemoteLoginModule.class.getName() + "." + SERVER_URI_KEY;
066 public final static List<String> supportedOptions = Collections.unmodifiableList(Arrays.asList(SECURITY_REALM_KEY, SERVER_URI_KEY, SECURITY_REALM_KEY_LONG, SERVER_URI_KEY_LONG));
067
068 private Subject subject;
069 private CallbackHandler callbackHandler;
070 private String securityRealm;
071 private URI serverURI;
072 private SubjectId identity;
073 private boolean loginSucceeded;
074 private ServerIdentityToken sit;
075
076 public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
077 this.subject = subject;
078 this.callbackHandler = callbackHandler;
079 for (Object option : options.keySet()) {
080 if (!supportedOptions.contains(option)) {
081 log.warn("Ignoring option: " + option + ". Not supported.");
082 }
083 }
084 securityRealm = (String) options.get(SECURITY_REALM_KEY);
085 if (securityRealm == null) {
086 securityRealm = (String) options.get(SECURITY_REALM_KEY_LONG);
087 }
088
089 String serverURIstring = (String) options.get(SERVER_URI_KEY);
090 if (serverURIstring == null) {
091 serverURIstring = (String) options.get(SERVER_URI_KEY_LONG);
092 }
093 serverURI = URI.create(serverURIstring);
094
095 }
096
097 public boolean login() throws LoginException {
098 loginSucceeded = false;
099 Callback[] callbacks = new Callback[]{new NameCallback("username"), new PasswordCallback("passsword", false)};
100 try {
101 callbackHandler.handle(callbacks);
102 } catch (IOException e) {
103 throw (LoginException) new LoginException("Could not execute callbacks").initCause(e);
104 } catch (UnsupportedCallbackException e) {
105 throw (LoginException) new LoginException("Could not execute callbacks").initCause(e);
106 }
107 String userName = ((NameCallback) callbacks[0]).getName();
108 String password = new String(((PasswordCallback) callbacks[1]).getPassword());
109 identity = (SubjectId) ClientSecurity.directAuthentication(securityRealm, userName, password, new ServerMetaData(serverURI));
110 loginSucceeded = true;
111 return true;
112 }
113
114 /*
115 * @exception LoginException if login succeeded but commit failed.
116 *
117 * @return true if login succeeded and commit succeeded, or false if login failed but commit succeeded.
118 */
119 public boolean commit() throws LoginException {
120 if (loginSucceeded) {
121 if (identity != null) {
122 sit = new ServerIdentityToken(serverURI, identity);
123 subject.getPrivateCredentials().add(sit);
124 }
125 }
126 // Clear out the private state
127 identity = null;
128 return loginSucceeded;
129 }
130
131 public boolean abort() throws LoginException {
132 if (loginSucceeded) {
133 // Clear out the private state
134 identity = null;
135 sit = null;
136 }
137 return loginSucceeded;
138 }
139
140 public boolean logout() throws LoginException {
141 // Clear out the private state
142 loginSucceeded = false;
143 identity = null;
144 if (sit != null) {
145 if (!subject.isReadOnly()) {
146 subject.getPrivateCredentials().remove(sit);
147 } else {
148 try {
149 if (sit instanceof Destroyable) {
150 // Try to destroy the credential
151 try {
152 ((Destroyable) sit).destroy();
153 } catch (Exception e) {
154 throw new LoginException();
155 }
156 } else {
157 throw new LoginException();
158 }
159 } finally {
160 sit = null;
161 }
162 }
163 }
164 sit = null;
165 return true;
166 }
167 }