1//
2//  ========================================================================
3//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4//  ------------------------------------------------------------------------
5//  All rights reserved. This program and the accompanying materials
6//  are made available under the terms of the Eclipse Public License v1.0
7//  and Apache License v2.0 which accompanies this distribution.
8//
9//      The Eclipse Public License is available at
10//      http://www.eclipse.org/legal/epl-v10.html
11//
12//      The Apache License v2.0 is available at
13//      http://www.opensource.org/licenses/apache2.0.php
14//
15//  You may elect to redistribute this code under either of these licenses.
16//  ========================================================================
17//
18
19
20package org.eclipse.jetty.security;
21
22import java.io.IOException;
23import java.io.Serializable;
24import java.security.Principal;
25import java.util.Map;
26import java.util.concurrent.ConcurrentHashMap;
27import java.util.concurrent.ConcurrentMap;
28
29import javax.security.auth.Subject;
30
31import org.eclipse.jetty.server.UserIdentity;
32import org.eclipse.jetty.util.component.AbstractLifeCycle;
33import org.eclipse.jetty.util.log.Log;
34import org.eclipse.jetty.util.log.Logger;
35import org.eclipse.jetty.util.security.Credential;
36
37
38
39/* ------------------------------------------------------------ */
40/**
41 * A login service that keeps UserIdentities in a concurrent map
42 * either as the source or a cache of the users.
43 *
44 */
45public abstract class MappedLoginService extends AbstractLifeCycle implements LoginService
46{
47    private static final Logger LOG = Log.getLogger(MappedLoginService.class);
48
49    protected IdentityService _identityService=new DefaultIdentityService();
50    protected String _name;
51    protected final ConcurrentMap<String, UserIdentity> _users=new ConcurrentHashMap<String, UserIdentity>();
52
53    /* ------------------------------------------------------------ */
54    protected MappedLoginService()
55    {
56    }
57
58    /* ------------------------------------------------------------ */
59    /** Get the name.
60     * @return the name
61     */
62    public String getName()
63    {
64        return _name;
65    }
66
67    /* ------------------------------------------------------------ */
68    /** Get the identityService.
69     * @return the identityService
70     */
71    public IdentityService getIdentityService()
72    {
73        return _identityService;
74    }
75
76    /* ------------------------------------------------------------ */
77    /** Get the users.
78     * @return the users
79     */
80    public ConcurrentMap<String, UserIdentity> getUsers()
81    {
82        return _users;
83    }
84
85    /* ------------------------------------------------------------ */
86    /** Set the identityService.
87     * @param identityService the identityService to set
88     */
89    public void setIdentityService(IdentityService identityService)
90    {
91        if (isRunning())
92            throw new IllegalStateException("Running");
93        _identityService = identityService;
94    }
95
96    /* ------------------------------------------------------------ */
97    /** Set the name.
98     * @param name the name to set
99     */
100    public void setName(String name)
101    {
102        if (isRunning())
103            throw new IllegalStateException("Running");
104        _name = name;
105    }
106
107    /* ------------------------------------------------------------ */
108    /** Set the users.
109     * @param users the users to set
110     */
111    public void setUsers(Map<String, UserIdentity> users)
112    {
113        if (isRunning())
114            throw new IllegalStateException("Running");
115        _users.clear();
116        _users.putAll(users);
117    }
118
119    /* ------------------------------------------------------------ */
120    /**
121     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
122     */
123    @Override
124    protected void doStart() throws Exception
125    {
126        loadUsers();
127        super.doStart();
128    }
129
130    /* ------------------------------------------------------------ */
131    @Override
132    protected void doStop() throws Exception
133    {
134        super.doStop();
135    }
136
137    /* ------------------------------------------------------------ */
138    public void logout(UserIdentity identity)
139    {
140        LOG.debug("logout {}",identity);
141    }
142
143    /* ------------------------------------------------------------ */
144    @Override
145    public String toString()
146    {
147        return this.getClass().getSimpleName()+"["+_name+"]";
148    }
149
150    /* ------------------------------------------------------------ */
151    /** Put user into realm.
152     * Called by implementations to put the user data loaded from
153     * file/db etc into the user structure.
154     * @param userName User name
155     * @param info a UserIdentity instance, or a String password or Credential instance
156     * @return User instance
157     */
158    protected synchronized UserIdentity putUser(String userName, Object info)
159    {
160        final UserIdentity identity;
161        if (info instanceof UserIdentity)
162            identity=(UserIdentity)info;
163        else
164        {
165            Credential credential = (info instanceof Credential)?(Credential)info:Credential.getCredential(info.toString());
166
167            Principal userPrincipal = new KnownUser(userName,credential);
168            Subject subject = new Subject();
169            subject.getPrincipals().add(userPrincipal);
170            subject.getPrivateCredentials().add(credential);
171            subject.setReadOnly();
172            identity=_identityService.newUserIdentity(subject,userPrincipal,IdentityService.NO_ROLES);
173        }
174
175        _users.put(userName,identity);
176        return identity;
177    }
178
179    /* ------------------------------------------------------------ */
180    /** Put user into realm.
181     * @param userName The user to add
182     * @param credential The users Credentials
183     * @param roles The users roles
184     * @return UserIdentity
185     */
186    public synchronized UserIdentity putUser(String userName, Credential credential, String[] roles)
187    {
188        Principal userPrincipal = new KnownUser(userName,credential);
189        Subject subject = new Subject();
190        subject.getPrincipals().add(userPrincipal);
191        subject.getPrivateCredentials().add(credential);
192
193        if (roles!=null)
194            for (String role : roles)
195                subject.getPrincipals().add(new RolePrincipal(role));
196
197        subject.setReadOnly();
198        UserIdentity identity=_identityService.newUserIdentity(subject,userPrincipal,roles);
199        _users.put(userName,identity);
200        return identity;
201    }
202
203    /* ------------------------------------------------------------ */
204    public void removeUser(String username)
205    {
206        _users.remove(username);
207    }
208
209    /* ------------------------------------------------------------ */
210    /**
211     * @see org.eclipse.jetty.security.LoginService#login(java.lang.String, java.lang.Object)
212     */
213    public UserIdentity login(String username, Object credentials)
214    {
215        UserIdentity user = _users.get(username);
216
217        if (user==null)
218            user = loadUser(username);
219
220        if (user!=null)
221        {
222            UserPrincipal principal = (UserPrincipal)user.getUserPrincipal();
223            if (principal.authenticate(credentials))
224                return user;
225        }
226        return null;
227    }
228
229    /* ------------------------------------------------------------ */
230    public boolean validate(UserIdentity user)
231    {
232        if (_users.containsKey(user.getUserPrincipal().getName()))
233            return true;
234
235        if (loadUser(user.getUserPrincipal().getName())!=null)
236            return true;
237
238        return false;
239    }
240
241    /* ------------------------------------------------------------ */
242    protected abstract UserIdentity loadUser(String username);
243
244    /* ------------------------------------------------------------ */
245    protected abstract void loadUsers() throws IOException;
246
247
248    /* ------------------------------------------------------------ */
249    /* ------------------------------------------------------------ */
250    /* ------------------------------------------------------------ */
251    public interface UserPrincipal extends Principal,Serializable
252    {
253        boolean authenticate(Object credentials);
254        public boolean isAuthenticated();
255    }
256
257    /* ------------------------------------------------------------ */
258    /* ------------------------------------------------------------ */
259    /* ------------------------------------------------------------ */
260    public static class RolePrincipal implements Principal,Serializable
261    {
262        private static final long serialVersionUID = 2998397924051854402L;
263        private final String _roleName;
264        public RolePrincipal(String name)
265        {
266            _roleName=name;
267        }
268        public String getName()
269        {
270            return _roleName;
271        }
272    }
273
274    /* ------------------------------------------------------------ */
275    /* ------------------------------------------------------------ */
276    /* ------------------------------------------------------------ */
277    public static class Anonymous implements UserPrincipal,Serializable
278    {
279        private static final long serialVersionUID = 1097640442553284845L;
280
281        public boolean isAuthenticated()
282        {
283            return false;
284        }
285
286        public String getName()
287        {
288            return "Anonymous";
289        }
290
291        public boolean authenticate(Object credentials)
292        {
293            return false;
294        }
295
296    }
297
298    /* ------------------------------------------------------------ */
299    /* ------------------------------------------------------------ */
300    /* ------------------------------------------------------------ */
301    public static class KnownUser implements UserPrincipal,Serializable
302    {
303        private static final long serialVersionUID = -6226920753748399662L;
304        private final String _name;
305        private final Credential _credential;
306
307        /* -------------------------------------------------------- */
308        public KnownUser(String name,Credential credential)
309        {
310            _name=name;
311            _credential=credential;
312        }
313
314        /* -------------------------------------------------------- */
315        public boolean authenticate(Object credentials)
316        {
317            return _credential!=null && _credential.check(credentials);
318        }
319
320        /* ------------------------------------------------------------ */
321        public String getName()
322        {
323            return _name;
324        }
325
326        /* -------------------------------------------------------- */
327        public boolean isAuthenticated()
328        {
329            return true;
330        }
331
332        /* -------------------------------------------------------- */
333        @Override
334        public String toString()
335        {
336            return _name;
337        }
338    }
339}
340
341