1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package org.apache.harmony.auth.tests.module;
19
20import java.io.IOException;
21import java.security.Principal;
22import java.util.HashMap;
23import java.util.Set;
24
25import javax.security.auth.Subject;
26import javax.security.auth.callback.Callback;
27import javax.security.auth.callback.CallbackHandler;
28import javax.security.auth.callback.NameCallback;
29import javax.security.auth.callback.PasswordCallback;
30import javax.security.auth.callback.UnsupportedCallbackException;
31import javax.security.auth.login.LoginException;
32
33import junit.framework.TestCase;
34
35import org.apache.harmony.auth.module.LdapLoginModule;
36import org.apache.harmony.auth.UserPrincipal;
37
38
39public class LdapLoginModuleTest extends TestCase {
40
41    //  module options
42    private HashMap<String, String> options = new HashMap<String, String>();
43
44    private final String USER_PROVIDER_URL = "ldap://9.181.106.121:389/ou=People,o=JNDITutorial,dc=my-domain,dc=com";
45
46    protected void setUp() throws Exception {
47        options.put("userProvider", USER_PROVIDER_URL);
48        options.put("useSSL", "false");
49    }
50
51    @Override
52    protected void tearDown() throws Exception {
53        options.clear();
54    }
55
56    /**
57     * Test method for {@link org.apache.harmony.auth.module.LdapLoginModule#abort()}.
58     */
59    public void test_abort() throws LoginException{
60        LdapLoginModule jlm = new LdapLoginModule();
61        try {
62            assertFalse("Should return false if login failed or no login", jlm
63                    .abort());
64        } catch (LoginException e) {
65            fail("Abort failed");
66        }
67        Subject subject = new Subject();
68        subject.setReadOnly();
69        jlm.initialize(subject, null, null, options);
70        try {
71            assertFalse("Should return false if login failed or no login", jlm
72                    .abort());
73        } catch (Exception e) {
74            fail("Not any exception here");
75        }
76        subject = new Subject();
77        jlm.initialize(subject, new FaultCallbackHandler(), null, options);
78        try {
79            jlm.login();
80            fail("login should fail");
81        } catch (LoginException e) {
82            assertFalse("Should return false because of login failure", jlm
83                    .abort());
84        }
85        subject = new Subject();
86        options.put("authIdentity","cn=Manager,dc=my-domain,dc=com");
87        jlm.initialize(subject, new MockCallbackHandler(), null, options);
88        jlm.login();
89        assertTrue("Should return true if login was successful", jlm
90                .abort());
91    }
92
93    /**
94     * Test method for {@link org.apache.harmony.auth.module.LdapLoginModule#commit()}.
95     */
96    public void test_commit() {
97        LdapLoginModule module = new LdapLoginModule();
98        Subject subject = new Subject();
99        options.put("authIdentity","cn=Manager,dc=my-domain,dc=com");
100        module.initialize(subject, new MockCallbackHandler(), null, options);
101        try {
102            assertTrue("Login should be successful", module.login());
103            module.commit();
104        } catch (LoginException e) {
105            fail("Login shouldn't fail");
106        }
107        Set<Principal> principals = subject.getPrincipals();
108        assertFalse("Should get at least one principal", principals.isEmpty());
109        subject = new Subject();
110        subject.setReadOnly();
111        module.initialize(subject, new MockCallbackHandler(), null, options);
112        try {
113            assertFalse("Commit shouldn't be successful", module.commit());
114            fail("Should throw LoginException here because of trying to clear read-only subject");
115        } catch (LoginException e) {
116            // expected LoginException here
117        }
118    }
119
120    /**
121     * Test method for {@link org.apache.harmony.auth.module.LdapLoginModule#initialize(javax.security.auth.Subject, javax.security.auth.callback.CallbackHandler, java.util.Map, java.util.Map)}.
122     */
123    public void test_initialize() {
124        LdapLoginModule module = new LdapLoginModule();
125        try {
126            module.initialize(null, null, null, null);
127            fail("Should throw NullPointerException here.");
128        } catch (NullPointerException e) {
129            // expected NullPointerException
130        }
131    }
132
133    /**
134     * Test method for {@link org.apache.harmony.auth.module.LdapLoginModule#login()}.
135     */
136    public void test_login() {
137        LdapLoginModule module = new LdapLoginModule();
138        HashMap<String, String> emptyOptions = new HashMap<String, String>();
139        module.initialize(null, new MockCallbackHandler(), null, emptyOptions);
140        try {
141            module.login();
142            fail("Should throw LoginException here.");
143        } catch (LoginException e) {
144            // expected LoginException
145        }
146
147        options.put("authIdentity","cn=Manager,dc=my-domain,dc=com");
148        Subject subject = new Subject();
149        module.initialize(subject, new MockCallbackHandler(), null, options);
150        try {
151            assertTrue("Login should be successful", module.login());
152        } catch (LoginException e) {
153            fail("Login shouldn't fail");
154        }
155        module.initialize(subject, new FaultCallbackHandler(), null, options);
156        try {
157            assertFalse("Login shouldn't be successful", module.login());
158            fail("Login should fail");
159        } catch (LoginException e) {
160            // expected Loginexception here
161        }
162    }
163
164    /**
165     * Test method for {@link org.apache.harmony.auth.module.LdapLoginModule#logout()}.
166     */
167    public void test_logout() {
168        LdapLoginModule module = new LdapLoginModule();
169        Subject subject = new Subject();
170        options.put("authIdentity","cn=Manager,dc=my-domain,dc=com");
171        module.initialize(subject, new MockCallbackHandler(), null, options);
172        try {
173            assertTrue("Login should be successful", module.login());
174            module.commit();
175        } catch (LoginException e) {
176            fail("Login shouldn't fail");
177        }
178        Set<Principal> principals = subject.getPrincipals();
179        assertFalse("Should get at least one principal", principals.isEmpty());
180        try {
181            assertTrue("Should be true", module.logout());
182        } catch (LoginException e) {
183            fail("Logout failed");
184        }
185        principals = subject.getPrincipals();
186        assertTrue("Principals should be cleared", principals.isEmpty());
187    }
188
189    public void test_optionsAndSharedStatus() throws LoginException{
190        options.put("authIdentity","cn=Manager,dc=my-domain,dc=com");
191        options.put("authzIdentity","testAuthzIdentityOption");
192        LdapLoginModule module = new LdapLoginModule();
193        Subject subject = new Subject();
194        module.initialize(subject, new MockCallbackHandler(), null, options);
195        try {
196            module.login();
197            module.commit();
198            assertTrue("Should get a principal from authzIdentity option",subject.getPrincipals().contains(new UserPrincipal("testAuthzIdentityOption")));
199        }
200        catch(LoginException e){
201            fail("Login failed");
202        }
203        finally{
204            module.logout();
205        }
206
207        options.put("debug", "true");
208        options.put("useFirstPass", "true");
209        HashMap<String, Object> status = new HashMap<String,Object>();
210        status.put("javax.security.auth.login.name", "leo");
211        status.put("javax.security.auth.login.password", "faultPass".toCharArray());
212        subject = new Subject();
213        module.initialize(subject, new MockCallbackHandler(), status, options);
214        try {
215            module.login();
216            fail("Should be failed for using password from shared state");
217        }
218        catch(LoginException e){
219            //expected LoginException here
220        }
221
222        options.remove("useFirstPass");
223        options.put("tryFirstPass", "true");
224        module.initialize(subject, new MockCallbackHandler(), status, options);
225        try {
226            module.login();
227            module.commit();
228        }
229        catch(LoginException e){
230            fail("Login should be failed");
231        }
232        finally{
233            module.logout();
234        }
235
236        options.remove("tryFirstPass");
237        options.put("clearPass", "true");
238        status.put("javax.security.auth.login.name", "leo");
239        status.put("javax.security.auth.login.password", "passw0rd".toCharArray());
240        module.initialize(subject, new MockCallbackHandler(), status, options);
241        try {
242            module.login();
243            module.commit();
244            assertNull("javax.security.auth.login.name in shared state should be null when clearPass switch on",status.get("javax.security.auth.login.name"));
245            assertNull("javax.security.auth.login.password in shared state should be null when clearPass switch on",status.get("javax.security.auth.login.password"));
246        } catch (LoginException e) {
247            fail("Login shouldn't fail");
248        }
249        finally{
250            module.logout();
251        }
252
253        status = new HashMap<String,Object>();
254        options.remove("clearPass");
255        options.put("storePass", "true");
256        module.initialize(subject, new FaultCallbackHandler(), status, options);
257        try {
258            module.login();
259            module.commit();
260        } catch (LoginException e) {
261            assertNull("javax.security.auth.login.name in shared state should be null when login failed",status.get("javax.security.auth.login.name"));
262            assertNull("javax.security.auth.login.password in shared state should be null when login failed",status.get("javax.security.auth.login.password"));
263        }
264        finally{
265            module.logout();
266        }
267
268        module.initialize(subject, new MockCallbackHandler(), status, options);
269        try {
270            module.login();
271            module.commit();
272        } catch (LoginException e) {
273            fail("Login failed");
274        }
275        finally{
276            module.logout();
277        }
278        assertNotNull("javax.security.auth.login.name should be stored in shared state when storePass switch on",status.get("javax.security.auth.login.name"));
279        assertNotNull("javax.security.auth.login.password should be stored in shared state when storePass switch on",status.get("javax.security.auth.login.password"));
280
281        status.put("javax.security.auth.login.name", "tester");
282        status.put("javax.security.auth.login.password", "testerPass");
283        module.initialize(subject, new MockCallbackHandler(), status, options);
284        try {
285            module.login();
286            module.commit();
287        } catch (LoginException e) {
288            fail("Login failed");
289        }
290        finally{
291            module.logout();
292        }
293        assertEquals("Should't override the username value in sharedState",status.get("javax.security.auth.login.name"),"tester");
294        assertEquals("Should't override the password value in sharedState",status.get("javax.security.auth.login.password"),"testerPass");
295    }
296
297    static private class MockCallbackHandler implements CallbackHandler{
298
299        public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
300            for(int i=0;i<callbacks.length;i++){
301                if(callbacks[i] instanceof NameCallback){
302                    NameCallback nc = (NameCallback)callbacks[i];
303                    nc.setName("leo");
304                }
305                else if(callbacks[i] instanceof PasswordCallback){
306                    PasswordCallback pc = (PasswordCallback)callbacks[i];
307                    pc.setPassword("secret".toCharArray());
308                }
309                else
310                {
311                    throw new Error(callbacks[i].getClass().toString());
312                }
313            }
314        }
315    }
316
317    static private class FaultCallbackHandler implements CallbackHandler{
318
319        public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
320            for(int i=0;i<callbacks.length;i++){
321                if(callbacks[i] instanceof NameCallback){
322                    NameCallback nc = (NameCallback)callbacks[i];
323                    nc.setName("leo");
324                }
325                else if(callbacks[i] instanceof PasswordCallback){
326                    PasswordCallback pc = (PasswordCallback)callbacks[i];
327                    pc.setPassword("password".toCharArray());
328                }
329                else
330                {
331                    throw new Error(callbacks[i].getClass().toString());
332                }
333            }
334        }
335    }
336}
337