PlainPasswordFilePrincipalDatabase.java
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.qpid.server.security.auth.database;
022 
023 import org.apache.log4j.Logger;
024 import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser;
025 import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
026 import org.apache.qpid.server.security.auth.sasl.amqplain.AmqPlainInitialiser;
027 import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5Initialiser;
028 import org.apache.qpid.server.security.auth.sasl.plain.PlainInitialiser;
029 
030 import javax.security.auth.callback.PasswordCallback;
031 import javax.security.auth.login.AccountNotFoundException;
032 import java.io.BufferedReader;
033 import java.io.File;
034 import java.io.FileNotFoundException;
035 import java.io.FileReader;
036 import java.io.IOException;
037 import java.security.Principal;
038 import java.util.HashMap;
039 import java.util.LinkedList;
040 import java.util.List;
041 import java.util.Map;
042 import java.util.regex.Pattern;
043 
044 /**
045  * Represents a user database where the account information is stored in a simple flat file.
046  *
047  * The file is expected to be in the form: username:password username1:password1 ... usernamen:passwordn
048  *
049  * where a carriage return separates each username/password pair. Passwords are assumed to be in plain text.
050  */
051 public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase
052 {
053     private static final Logger _logger = Logger.getLogger(PlainPasswordFilePrincipalDatabase.class);
054 
055     protected File _passwordFile;
056 
057     protected Pattern _regexp = Pattern.compile(":");
058 
059     protected Map<String, AuthenticationProviderInitialiser> _saslServers;
060 
061     public PlainPasswordFilePrincipalDatabase()
062     {
063         _saslServers = new HashMap<String, AuthenticationProviderInitialiser>();
064 
065         /**
066          *  Create Authenticators for Plain Password file.
067          */
068 
069         // Accept AMQPlain incomming and compare it to the file.
070         AmqPlainInitialiser amqplain = new AmqPlainInitialiser();
071         amqplain.initialise(this);
072 
073         // Accept Plain incomming and compare it to the file.
074         PlainInitialiser plain = new PlainInitialiser();
075         plain.initialise(this);
076 
077         //  Accept MD5 incomming and Hash file value for comparison
078         CRAMMD5Initialiser cram = new CRAMMD5Initialiser();
079         cram.initialise(this);
080 
081         _saslServers.put(amqplain.getMechanismName(), amqplain);
082         _saslServers.put(plain.getMechanismName(), plain);
083         _saslServers.put(cram.getMechanismName(), cram);
084     }
085 
086     public void setPasswordFile(String passwordFilethrows FileNotFoundException
087     {
088         File f = new File(passwordFile);
089         _logger.info("PlainPasswordFile using file " + f.getAbsolutePath());
090         _passwordFile = f;
091         if (!f.exists())
092         {
093             throw new FileNotFoundException("Cannot find password file " + f);
094         }
095         if (!f.canRead())
096         {
097             throw new FileNotFoundException("Cannot read password file " + f +
098                                             ". Check permissions.");
099         }
100     }
101 
102     public void setPassword(Principal principal, PasswordCallback callbackthrows IOException,
103                                                                                    AccountNotFoundException
104     {
105         if (_passwordFile == null)
106         {
107             throw new AccountNotFoundException("Unable to locate principal since no password file was specified during initialisation");
108         }
109         if (principal == null)
110         {
111             throw new IllegalArgumentException("principal must not be null");
112         }
113         char[] pwd = lookupPassword(principal.getName());
114         if (pwd != null)
115         {
116             callback.setPassword(pwd);
117         }
118         else
119         {
120             throw new AccountNotFoundException("No account found for principal " + principal);
121         }
122     }
123 
124     public boolean verifyPassword(String principal, char[] passwordthrows AccountNotFoundException
125     {
126         try
127         {
128             char[] pwd = lookupPassword(principal);
129 
130             return compareCharArray(pwd, password);
131         }
132         catch (IOException e)
133         {
134             return false;
135         }
136     }
137 
138     public boolean updatePassword(Principal principal, char[] passwordthrows AccountNotFoundException
139     {
140         return false// updates denied
141     }
142 
143     public boolean createPrincipal(Principal principal, char[] password)
144     {
145         return false// updates denied
146     }
147 
148     public boolean deletePrincipal(Principal principalthrows AccountNotFoundException
149     {
150         return false// updates denied
151     }
152 
153     public Map<String, AuthenticationProviderInitialiser> getMechanisms()
154     {
155         return _saslServers;
156     }
157 
158     public List<Principal> getUsers()
159     {
160         return new LinkedList<Principal>()//todo
161     }
162 
163     public Principal getUser(String username)
164     {
165         try
166         {
167             if (lookupPassword(username!= null)
168             {
169                 return new UsernamePrincipal(username);
170             }
171         }
172         catch (IOException e)
173         {
174             //fall through to null return
175         }
176         return null;
177     }
178 
179     private boolean compareCharArray(char[] a, char[] b)
180     {
181         boolean equal = false;
182         if (a.length == b.length)
183         {
184             equal = true;
185             int index = 0;
186             while (equal && index < a.length)
187             {
188                 equal = a[index== b[index];
189                 index++;
190             }
191         }
192         return equal;
193     }
194 
195 
196     /**
197      * Looks up the password for a specified user in the password file. Note this code is <b>not</b> secure since it
198      * creates strings of passwords. It should be modified to create only char arrays which get nulled out.
199      *
200      @param name the name of the principal to lookup
201      *
202      @return char[] of the password
203      *
204      @throws java.io.IOException whilst accessing the file
205      */
206     private char[] lookupPassword(String namethrows IOException
207     {
208         BufferedReader reader = null;
209         try
210         {
211             reader = new BufferedReader(new FileReader(_passwordFile));
212             String line;
213 
214             while ((line = reader.readLine()) != null)
215             {
216                 if (!line.startsWith("#"))
217                 {
218                     String[] result = _regexp.split(line);
219                     if (result == null || result.length < 2)
220                     {
221                         continue;
222                     }
223 
224                     if (name.equals(result[0]))
225                     {
226                         return result[1].toCharArray();
227                     }
228                 }
229             }
230             return null;
231         }
232         finally
233         {
234             if (reader != null)
235             {
236                 reader.close();
237             }
238         }
239     }
240 
241     public void reload() throws IOException
242     {
243         //This PD is not cached, so do nothing.
244     }
245 }