View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  
21  package org.waarp.gateway.ftp.file;
22  
23  import org.waarp.common.command.NextCommandReply;
24  import org.waarp.common.command.ReplyCode;
25  import org.waarp.common.command.exception.Reply421Exception;
26  import org.waarp.common.command.exception.Reply530Exception;
27  import org.waarp.common.database.DbConstant;
28  import org.waarp.common.file.DirInterface;
29  import org.waarp.common.logging.WaarpLogger;
30  import org.waarp.common.logging.WaarpLoggerFactory;
31  import org.waarp.ftp.core.command.FtpCommandCode;
32  import org.waarp.ftp.core.session.FtpSession;
33  import org.waarp.ftp.filesystembased.FilesystemBasedFtpAuth;
34  import org.waarp.ftp.filesystembased.FilesystemBasedFtpRestart;
35  import org.waarp.gateway.ftp.config.FileBasedConfiguration;
36  import org.waarp.gateway.ftp.exec.AbstractExecutor.CommandExecutor;
37  import org.waarp.gateway.kernel.session.HttpAuthInterface;
38  
39  import java.io.File;
40  
41  /**
42   * FtpAuth implementation based on a list of (user/password/account) stored in a
43   * xml file load at startup from
44   * configuration.
45   */
46  public class FileBasedAuth extends FilesystemBasedFtpAuth
47      implements HttpAuthInterface {
48    /**
49     * Internal Logger
50     */
51    private static final WaarpLogger logger =
52        WaarpLoggerFactory.getLogger(FileBasedAuth.class);
53  
54    /**
55     * Current authentication
56     */
57    private SimpleAuth currentAuth;
58  
59    /**
60     * Special Id for the current transfer
61     */
62    private long specialId = DbConstant.ILLEGALVALUE;
63  
64    /**
65     * @param session
66     */
67    public FileBasedAuth(final FtpSession session) {
68      super(session);
69    }
70  
71    @Override
72    protected final void businessClean() {
73      currentAuth = null;
74    }
75  
76    /**
77     * @param user the user to set
78     *
79     * @return (NOOP, 230) if the user is OK, else return the following command
80     *     that must follow (usually PASS) and
81     *     the associated reply
82     *
83     * @throws Reply421Exception if there is a problem during the
84     *     authentication
85     * @throws Reply530Exception if there is a problem during the
86     *     authentication
87     */
88    @Override
89    protected final NextCommandReply setBusinessUser(final String user)
90        throws Reply530Exception {
91      final SimpleAuth auth =
92          ((FileBasedConfiguration) ((FtpSession) getSession()).getConfiguration()).getSimpleAuth(
93              user);
94      if (auth == null) {
95        setIsIdentified(false);
96        currentAuth = null;
97        throw new Reply530Exception("User name not allowed");
98      }
99      currentAuth = auth;
100     return new NextCommandReply(FtpCommandCode.PASS,
101                                 ReplyCode.REPLY_331_USER_NAME_OKAY_NEED_PASSWORD,
102                                 null);
103   }
104 
105   /**
106    * Set the password according to any implementation and could set the
107    * rootFromAuth. If NOOP is returned,
108    * isIdentifed must be TRUE. A special case is implemented for test user.
109    *
110    * @param password
111    *
112    * @return (NOOP, 230) if the Password is OK, else return the following
113    *     command that must follow (usually ACCT)
114    *     and the associated reply
115    *
116    * @throws Reply421Exception if there is a problem during the
117    *     authentication
118    * @throws Reply530Exception if there is a problem during the
119    *     authentication
120    */
121   @Override
122   protected final NextCommandReply setBusinessPassword(final String password)
123       throws Reply530Exception {
124     if (currentAuth == null) {
125       setIsIdentified(false);
126       throw new Reply530Exception("PASS needs a USER first");
127     }
128     if (currentAuth.isPasswordValid(password)) {
129       return new NextCommandReply(FtpCommandCode.ACCT,
130                                   ReplyCode.REPLY_332_NEED_ACCOUNT_FOR_LOGIN,
131                                   null);
132     }
133     throw new Reply530Exception("Password is not valid");
134   }
135 
136   /**
137    * Set the account according to any implementation and could set the
138    * rootFromAuth. If NOOP is returned,
139    * isIdentifed must be TRUE.
140    *
141    * @param account
142    *
143    * @return (NOOP, 230) if the Account is OK, else return the following
144    *     command
145    *     that must follow and the
146    *     associated reply
147    *
148    * @throws Reply421Exception if there is a problem during the
149    *     authentication
150    * @throws Reply530Exception if there is a problem during the
151    *     authentication
152    */
153   @Override
154   protected final NextCommandReply setBusinessAccount(final String account)
155       throws Reply530Exception {
156     if (currentAuth == null) {
157       throw new Reply530Exception("ACCT needs a USER first");
158     }
159     if (currentAuth.isAccountValid(account)) {
160       setIsIdentified(true);
161       logger.info("User {} is authentified with account {}", user, account);
162       return new NextCommandReply(FtpCommandCode.NOOP,
163                                   ReplyCode.REPLY_230_USER_LOGGED_IN, null);
164     }
165     throw new Reply530Exception("Account is not valid");
166   }
167 
168   @Override
169   public final boolean isBusinessPathValid(final String newPath) {
170     if (newPath == null) {
171       return false;
172     }
173     return newPath.startsWith(getBusinessPath());
174   }
175 
176   @Override
177   protected final String setBusinessRootFromAuth() throws Reply421Exception {
178     final String path;
179     if (account == null) {
180       path = DirInterface.SEPARATOR + user;
181     } else {
182       path = DirInterface.SEPARATOR + user + DirInterface.SEPARATOR + account;
183     }
184     final String fullpath = getAbsolutePath(path);
185     final File file = new File(fullpath);
186     if (!file.isDirectory()) {
187       throw new Reply421Exception("Filesystem not ready");
188     }
189     return path;
190   }
191 
192   @Override
193   public final boolean isAdmin() {
194     if (currentAuth == null) {
195       return false;
196     }
197     return currentAuth.isAdmin();
198   }
199 
200   /**
201    * Special Authentication for local execution
202    *
203    * @param hostid
204    */
205   public final void specialNoSessionAuth(final String hostid) {
206     isIdentified = true;
207     final SimpleAuth auth =
208         new SimpleAuth(hostid, hostid, null, null, 0, null, 0);
209     currentAuth = auth;
210     setIsIdentified(true);
211     user = auth.getUser();
212     account = auth.getUser();
213     ((FtpSession) getSession()).setSpecialInit(this, new FileBasedDir(
214         (FtpSession) getSession()), new FilesystemBasedFtpRestart(
215         (FtpSession) getSession()));
216     try {
217       setBusinessRootFromAuth();
218     } catch (final Reply421Exception ignored) {
219       // nothing
220     }
221     getSession().getDir().initAfterIdentification();
222     currentAuth.setAdmin(true);
223   }
224 
225   /**
226    * @return the specialId
227    */
228   public final long getSpecialId() {
229     return specialId;
230   }
231 
232   /**
233    * @param specialId the specialId to set
234    */
235   public final void setSpecialId(final long specialId) {
236     this.specialId = specialId;
237   }
238 
239   /**
240    * @return the associated Command Executor
241    */
242   @Override
243   public final CommandExecutor getCommandExecutor() {
244     return currentAuth.getCommandExecutor();
245   }
246 }