View Javadoc

1   /*
2    * Copyright (c) 2006 The University of Reading
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    * 1. Redistributions of source code must retain the above copyright
9    *    notice, this list of conditions and the following disclaimer.
10   * 2. Redistributions in binary form must reproduce the above copyright
11   *    notice, this list of conditions and the following disclaimer in the
12   *    documentation and/or other materials provided with the distribution.
13   * 3. Neither the name of the University of Reading, nor the names of the
14   *    authors or contributors may be used to endorse or promote products
15   *    derived from this software without specific prior written permission.
16   *
17   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20   * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27   */
28  
29  package uk.ac.rdg.resc.jstyx.server;
30  
31  import javax.net.ssl.SSLContext;
32  import java.util.Iterator;
33  
34  import org.apache.log4j.Logger;
35  
36  import org.dom4j.io.SAXReader;
37  import org.dom4j.Document;
38  import org.dom4j.DocumentException;
39  import org.dom4j.Node;
40  
41  import uk.ac.rdg.resc.jstyx.StyxUtils;
42  
43  /***
44   * Describes security information for a Styx server.  This is used for finding
45   * valid users and groups and whether or not anonymous logins are allowed.
46   *
47   * @author Jon Blower
48   * $Revision: 604 $
49   * $Date: 2006-03-21 14:58:42 +0000 (Tue, 21 Mar 2006) $
50   * $Log$
51   * Revision 1.3  2006/03/21 14:58:42  jonblower
52   * Implemented clear-text password-based authentication and did some simple tests
53   *
54   * Revision 1.2  2006/03/21 09:06:15  jonblower
55   * Still implementing authentication
56   *
57   * Revision 1.1  2006/03/20 17:51:50  jonblower
58   * Adding authentication to base JStyx system
59   *
60   */
61  public class StyxSecurityContext
62  {
63      private static final Logger log = Logger.getLogger(StyxSecurityContext.class);
64      
65      private String securityFile;
66      private boolean allowAnonymousLogin;
67      private boolean supportAuthentication;
68      private SSLContext sslContext;
69      
70      /***
71       * Creates a security context in which anonymous logins are allowed, 
72       * SSL is not used and authentication is not supported
73       */
74      public StyxSecurityContext()
75      {
76          this.allowAnonymousLogin = true;
77          this.supportAuthentication = false;
78          this.sslContext = null;
79      }
80      
81      /***
82       * Creates a new StyxSecurityContext from a configuration file
83       * @param securityFile XML File from which security information is to be read.
84       * If this is null, access to the server will be anonymous and unsecured.
85       * @throws StyxSecurityException if the security config file could not be read
86       */
87      public StyxSecurityContext(String securityFile) throws StyxSecurityException
88      {
89          try
90          {
91              this.securityFile = securityFile;
92              if (this.securityFile == null)
93              {
94                  throw new IllegalArgumentException("securityFile cannot be null");
95              }
96              // Validate the input file with the DTD
97              SAXReader reader = new SAXReader(true);
98              Document doc = reader.read(securityFile);
99              Node serverNode = doc.selectSingleNode("security/server");
100             this.allowAnonymousLogin =
101                 serverNode.valueOf("@allowAnonymousLogin").equalsIgnoreCase("true");
102             this.supportAuthentication = true;
103             if (serverNode.valueOf("useSSL").equalsIgnoreCase("true"))
104             {
105                 // TODO Set up SSL
106             }
107         }
108         catch (DocumentException de)
109         {
110             throw new StyxSecurityException("Error reading security config file "
111                 + securityFile + ": " + de.getMessage());
112         }
113     }
114     
115     /***
116      * @return true if this security context allows users to login anonymously
117      */
118     public boolean allowsAnonymousLogin()
119     {
120         return this.allowAnonymousLogin;
121     }
122     
123     /***
124      * @return true if this server supports authentication
125      */
126     public boolean supportsAuthentication()
127     {
128         return this.supportAuthentication;
129     }
130     
131     /***
132      * @return the SSLContext for this server, or null if we are not using SSL
133      */
134     public SSLContext getSSLContext()
135     {
136         return this.sslContext;
137     }
138     
139     /***
140      * @return a User object for the anonymous user
141      */
142     public User getAnonymousUser()
143     {
144         return new User(this, StyxUtils.ANONYMOUS_USER, "", "Anonymous User");
145     }
146     
147     /***
148      * @return an object describing the user with the given username
149      * @throws StyxSecurityException if a user with the given name does not
150      * exist in this security context or if this server does not support authentication
151      * or if there was an error finding the user's details
152      */
153     public User getUser(String username) throws StyxSecurityException
154     {
155         if (this.supportAuthentication)
156         {
157             try
158             {
159                 SAXReader reader = new SAXReader(true);
160                 Document doc = reader.read(securityFile);
161                 Node userNode =
162                     doc.selectSingleNode("security/users/user[@name='" +
163                     username + "']");
164                 if (userNode == null)
165                 {
166                     throw new StyxSecurityException("User " + username + " not found");
167                 }
168                 String password = userNode.selectSingleNode("password").getText();
169                 String fullName = userNode.selectSingleNode("fullName").getText();
170                 return new User(this, username, password, fullName);
171             }
172             catch(DocumentException de)
173             {
174                 throw new StyxSecurityException("Error reading security config file "
175                     + securityFile + ": " + de.getMessage());
176             }
177         }
178         else
179         {
180             throw new StyxSecurityException("Server does not support authentication");
181         }
182     }
183     
184     /***
185      * @return true if the given user is a member of the given group.  If the
186      * group is the default group (StyxUtils.DEFAULT_GROUP) this will return true
187      * as all users are members of this group.  If the user is the anonymous user
188      * (StyxUtils.ANONYMOUS_USER) this will return false unless the group is the
189      * default group.
190      */
191     public boolean isMember(String username, String group)
192     {
193         log.debug("Checking to see if " + username + " is a member of " + group);
194         if (group.equals(StyxUtils.DEFAULT_GROUP))
195         {
196             // All users belong to the default group
197             return true;
198         }
199         else if (username.equals(StyxUtils.ANONYMOUS_USER))
200         {
201             // User is not authenticated
202             return false;
203         }
204         else
205         {
206             try
207             {
208                 // Look inside the config file
209                 SAXReader reader = new SAXReader(true);
210                 Document doc = reader.read(this.securityFile);
211                 Node groupNode =
212                     doc.selectSingleNode("security/groups/group[@name='" +
213                     group + "']");
214                 if (groupNode == null)
215                 {
216                     log.debug("Group " + group + " not found");
217                     return false;
218                 }
219                 else
220                 {
221                     Iterator usernames = groupNode.selectNodes("username").iterator();
222                     while (usernames.hasNext())
223                     {
224                         Node usernameNode = (Node)usernames.next();
225                         if (usernameNode.getText().trim().equals(username))
226                         {
227                             // This user is a member of the group
228                             return true;
229                         }
230                     }
231                     // We haven't found the user's name in this group
232                     return false;
233                 }
234             }
235             catch(DocumentException de)
236             {
237                 // We can't check the user's group credentials
238                 log.error("Error occurred getting details for group " + group
239                     + " from " + this.securityFile);
240                 return false;
241             }
242         }
243     }
244     
245     /***
246      * Simple test routine
247      */
248     public static void main(String[] args) throws Exception
249     {
250         StyxSecurityContext context = new StyxSecurityContext("E://Jon's Documents" +
251             "//work//java//JStyx//core//conf//styxSecurity.xml");
252         User jdb = context.getUser("jdb");
253     }
254 }