001package com.hfg.security;
002
003import com.hfg.util.StringUtil;
004
005import java.io.BufferedReader;
006import java.io.File;
007import java.io.FileReader;
008import java.util.ArrayList;
009import java.util.List;
010
011//------------------------------------------------------------------------------
012/**
013 * Simple login store to keep passwords out of the source code.
014 * Unless otherwise specified the default file is called '.credMgr' and is found
015 * in the user's home directory. The file should only be readable by the account owner.
016 * The contents lines are expected to be three whitespace-separated fields: the key, the login, and the password.
017 *
018 * @author J. Alex Taylor, hairyfatguy.com
019 */
020//------------------------------------------------------------------------------
021// com.hfg XML/HTML Coding Library
022//
023// This library is free software; you can redistribute it and/or
024// modify it under the terms of the GNU Lesser General Public
025// License as published by the Free Software Foundation; either
026// version 2.1 of the License, or (at your option) any later version.
027//
028// This library is distributed in the hope that it will be useful,
029// but WITHOUT ANY WARRANTY; without even the implied warranty of
030// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
031// Lesser General Public License for more details.
032//
033// You should have received a copy of the GNU Lesser General Public
034// License along with this library; if not, write to the Free Software
035// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
036//
037// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
038// jataylor@hairyfatguy.com
039//------------------------------------------------------------------------------
040
041public class CredentialsMgr
042{
043   private File mFile;
044
045   private static String sDefaultFilename = ".credMgr";
046
047   //###########################################################################
048   // CONSTRUCTORS
049   //###########################################################################
050
051   //---------------------------------------------------------------------------
052   public CredentialsMgr()
053   {
054      this(new File(System.getProperty("user.home"), sDefaultFilename));
055   }
056
057   //---------------------------------------------------------------------------
058   public CredentialsMgr(File inFile)
059   {
060      mFile = inFile;
061   }
062
063   //###########################################################################
064   // PUBLIC METHODS
065   //###########################################################################
066
067   //---------------------------------------------------------------------------
068   public File getFile()
069   {
070      return mFile;
071   }
072
073   //---------------------------------------------------------------------------
074   public LoginCredentials get(String inKey)
075      throws SecurityException
076   {
077      LoginCredentials credentials = null;
078
079      if (! mFile.exists())
080      {
081         throw new SecurityException("The CredentialsMgr file " + StringUtil.singleQuote(mFile.getPath()) + " doesn't exist!");
082      }
083      else if (! mFile.canRead())
084      {
085         throw new SecurityException("No permissions for reading the CredentialsMgr file " + StringUtil.singleQuote(mFile.getPath()) + "!");
086      }
087
088      BufferedReader reader = null;
089      try
090      {
091         try
092         {
093            reader = new BufferedReader(new FileReader(getFile()));
094            String line;
095            while ((line = reader.readLine()) != null)
096            {
097               line = line.trim();
098               if (line.startsWith("#"))
099               {
100                  continue; // Allow comment lines
101               }
102
103               String fields[] = parseLine(line);
104               if (fields.length >= 3
105                   && fields[0].equals(inKey))
106               {
107                  credentials = new LoginCredentials(fields[1], fields[2].toCharArray());
108                  break;
109               }
110            }
111         }
112         finally
113         {
114            if (reader != null)
115            {
116               reader.close();
117            }
118         }
119      }
120      catch (Exception e)
121      {
122         throw new SecurityException("Problem accessing the CredentialsMgr file " + StringUtil.singleQuote(mFile.getPath()) + "!", e);
123      }
124
125      if (null == credentials)
126      {
127         throw new SecurityException("The key " + StringUtil.singleQuote(inKey)
128                                     + " couldn't be found in the CredentialsMgr file " + StringUtil.singleQuote(mFile.getPath()) + "!");
129      }
130
131      return credentials;
132   }
133
134   //---------------------------------------------------------------------------
135   private String[] parseLine(String inLine)
136   {
137      List<String> fields = new ArrayList<>();
138
139      boolean inQuotedValue = false;
140      int quoteCount = 0;
141      char currentQuoteChar = ' ';
142      StringBuilder currentValue = new StringBuilder();
143
144      int index = 0;
145      while (index < inLine.length())
146      {
147         int theChar = inLine.charAt(index);
148
149         if (inQuotedValue)
150         {
151            if (theChar == currentQuoteChar)
152            {
153               quoteCount++;
154
155               if (2 == quoteCount)
156               {
157                  // Skip
158                  quoteCount = 0;
159               }
160               else if ((index == inLine.length() - 1 || inLine.charAt(index + 1) != currentQuoteChar)
161                     && (0 == currentValue.length() || currentValue.charAt(currentValue.length() - 1) != '\\'))
162               {
163                  inQuotedValue = false;
164                  String unescapedValue = StringUtil.replaceAll(currentValue, "\\" + currentQuoteChar, currentQuoteChar + "");
165                  currentValue.setLength(0);
166                  currentValue.append(unescapedValue);
167               }
168               else
169               {
170                  currentValue.append((char) theChar);
171               }
172            }
173            else
174            {
175               currentValue.append((char) theChar);
176               quoteCount = 0;
177            }
178         }
179         else if (Character.isWhitespace(theChar))
180         {
181            if (currentValue.length() > 0)
182            {
183               fields.add(currentValue.toString().trim());
184               currentValue.setLength(0);
185            }
186         }
187         else if ((theChar == '\''
188               || theChar == '\"')
189               && 0 == currentValue.length())
190         {  // Start of a quoted value
191            inQuotedValue = true;
192            quoteCount = 0;
193            currentQuoteChar = (char) theChar;
194         }
195         else
196         {
197            currentValue.append((char) theChar);
198         }
199
200         index++;
201      }
202
203      fields.add(currentValue.length() > 0 ? currentValue.toString().trim() : null);
204
205      return fields.toArray(new String[] {});
206   }
207}