001: /*
002: JSPWiki - a JSP-based WikiWiki clone.
003:
004: Copyright (C) 2001-2002 Janne Jalkanen (Janne.Jalkanen@iki.fi)
005:
006: This program is free software; you can redistribute it and/or modify
007: it under the terms of the GNU Lesser General Public License as published by
008: the Free Software Foundation; either version 2.1 of the License, or
009: (at your option) any later version.
010:
011: This program is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public License
017: along with this program; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package com.ecyrd.jspwiki.ui;
021:
022: import java.io.File;
023: import java.io.FileInputStream;
024: import java.io.FileOutputStream;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.io.OutputStream;
028: import java.util.Properties;
029:
030: import javax.servlet.ServletConfig;
031: import javax.servlet.ServletContext;
032: import javax.servlet.http.HttpServletRequest;
033:
034: import com.ecyrd.jspwiki.*;
035: import com.ecyrd.jspwiki.auth.AuthenticationManager;
036: import com.ecyrd.jspwiki.auth.NoSuchPrincipalException;
037: import com.ecyrd.jspwiki.auth.UserManager;
038: import com.ecyrd.jspwiki.auth.WikiPrincipal;
039: import com.ecyrd.jspwiki.auth.WikiSecurityException;
040: import com.ecyrd.jspwiki.auth.authorize.Group;
041: import com.ecyrd.jspwiki.auth.authorize.GroupManager;
042: import com.ecyrd.jspwiki.auth.user.UserDatabase;
043: import com.ecyrd.jspwiki.auth.user.UserProfile;
044: import com.ecyrd.jspwiki.providers.BasicAttachmentProvider;
045: import com.ecyrd.jspwiki.providers.FileSystemProvider;
046: import com.ecyrd.jspwiki.util.CommentedProperties;
047:
048: /**
049: * Manages JSPWiki installation on behalf of <code>admin/Install.jsp</code>.
050: * The contents of this class were previously part of <code>Install.jsp</code>.
051: * @author Janne Jalkanen
052: * @since 2.4.20
053: */
054: public class Installer {
055: public static final String ADMIN_ID = "admin";
056: public static final String ADMIN_NAME = "Administrator";
057: public static final String INSTALL_INFO = "Installer.Info";
058: public static final String INSTALL_WARNING = "Installer.Warning";
059: public static final String INSTALL_ERROR = "Installer.Error";
060: public static final String APP_NAME = WikiEngine.PROP_APPNAME;
061: public static final String BASE_URL = WikiEngine.PROP_BASEURL;
062: public static final String STORAGE_DIR = BasicAttachmentProvider.PROP_STORAGEDIR;
063: public static final String LOG_DIR = "log4j.appender.FileLog.File";
064: public static final String PAGE_DIR = FileSystemProvider.PROP_PAGEDIR;
065: public static final String WORK_DIR = WikiEngine.PROP_WORKDIR;
066: public static final String ADMIN_GROUP = "Admin";
067: private final WikiSession m_session;
068: private final File m_propertyFile;
069: private final Properties m_props;
070: private final WikiEngine m_engine;
071: private HttpServletRequest m_request;
072: private boolean m_validated;
073:
074: public Installer(HttpServletRequest request, ServletConfig config) {
075: // Get wiki session for this user
076: m_engine = WikiEngine.getInstance(config);
077: m_session = WikiSession.getWikiSession(m_engine, request);
078:
079: // Get the servlet context, and file for properties
080: ServletContext context = config.getServletContext();
081: String path = context.getRealPath("/");
082: m_propertyFile = new File(path,
083: PropertyReader.DEFAULT_PROPERTYFILE);
084: m_props = new CommentedProperties();
085:
086: // Stash the request
087: m_request = request;
088: m_validated = false;
089: }
090:
091: /**
092: * Returns <code>true</code> if the administrative user had
093: * been created previously.
094: * @return the result
095: */
096: public boolean adminExists() {
097: // See if the admin user exists already
098: UserManager userMgr = m_engine.getUserManager();
099: UserDatabase userDb = userMgr.getUserDatabase();
100:
101: try {
102: userDb.findByLoginName(ADMIN_ID);
103: return true;
104: } catch (NoSuchPrincipalException e) {
105: return false;
106: }
107: }
108:
109: /**
110: * Creates an adminstrative user and returns the new password.
111: * If the admin user exists, the password will be <code>null</code>.
112: * @return the password
113: * @throws WikiSecurityException
114: */
115: public String createAdministrator() throws WikiSecurityException {
116: if (!m_validated) {
117: throw new WikiSecurityException(
118: "Cannot create administrator because one or more of the installation settings are invalid.");
119: }
120:
121: if (adminExists()) {
122: return null;
123: }
124:
125: // See if the admin user exists already
126: UserManager userMgr = m_engine.getUserManager();
127: UserDatabase userDb = userMgr.getUserDatabase();
128: String password = null;
129:
130: try {
131: userDb.findByLoginName(ADMIN_ID);
132: } catch (NoSuchPrincipalException e) {
133: // Create a random 12-character password
134: password = TextUtil.generateRandomPassword();
135: UserProfile profile = userDb.newProfile();
136: profile.setLoginName(ADMIN_ID);
137: profile.setFullname(ADMIN_NAME);
138: profile.setPassword(password);
139: userDb.save(profile);
140: }
141:
142: // Create a new admin group
143: GroupManager groupMgr = m_engine.getGroupManager();
144: Group group = null;
145: try {
146: group = groupMgr.getGroup(ADMIN_GROUP);
147: group.add(new WikiPrincipal(ADMIN_NAME));
148: } catch (NoSuchPrincipalException e) {
149: group = groupMgr.parseGroup(ADMIN_GROUP, ADMIN_NAME, true);
150: }
151: groupMgr.setGroup(m_session, group);
152:
153: return password;
154: }
155:
156: /**
157: * Returns the properties file as a string
158: * @return the string
159: */
160: public String getProperties() {
161: return m_props.toString();
162: }
163:
164: public String getPropertiesPath() {
165: return m_propertyFile.getAbsolutePath();
166: }
167:
168: /**
169: * Returns a property from the WikiEngine's properties.
170: * @param key the property key
171: * @return the property value
172: */
173: public String getProperty(String key) {
174: return m_props.getProperty(key);
175: }
176:
177: public void parseProperties() throws Exception {
178: m_validated = false;
179:
180: // Set request encoding
181: m_request.setCharacterEncoding("UTF-8");
182:
183: try {
184: InputStream in = null;
185: try {
186: // Load old properties from disk
187: in = new FileInputStream(m_propertyFile);
188: m_props.load(in);
189: } finally {
190: if (in != null) {
191: in.close();
192: }
193: }
194: } catch (IOException e) {
195: m_session.addMessage(INSTALL_ERROR,
196: "Unable to read properties: " + e.getMessage());
197: }
198:
199: // Get application name
200: String nullValue = m_props.getProperty(APP_NAME, "MyWiki");
201: parseProperty(APP_NAME, nullValue);
202:
203: // Get/sanitize base URL
204: nullValue = m_request.getRequestURL().toString();
205: nullValue = nullValue.substring(0, nullValue.lastIndexOf('/'))
206: + "/";
207: nullValue = m_props.getProperty(BASE_URL, nullValue);
208: parseProperty(BASE_URL, nullValue);
209: sanitizeURL(BASE_URL);
210:
211: // Get/sanitize page directory
212: nullValue = m_props.getProperty(PAGE_DIR,
213: "Please configure me!");
214: parseProperty(PAGE_DIR, nullValue);
215: sanitizePath(PAGE_DIR);
216:
217: // Get/sanitize log directory
218: nullValue = m_props.getProperty(LOG_DIR, "/tmp/");
219: parseProperty(LOG_DIR, nullValue);
220: sanitizePath(LOG_DIR);
221:
222: // Get/sanitize work directory
223: nullValue = m_props.getProperty(WORK_DIR, "/tmp/");
224: parseProperty(WORK_DIR, nullValue);
225: sanitizePath(WORK_DIR);
226:
227: // Get/sanitize security property
228: nullValue = m_props.getProperty(
229: AuthenticationManager.PROP_SECURITY,
230: AuthenticationManager.SECURITY_JAAS);
231: parseProperty(AuthenticationManager.PROP_SECURITY, nullValue);
232:
233: // Set a few more default properties, for easy setup
234: m_props.setProperty(STORAGE_DIR, m_props.getProperty(PAGE_DIR));
235: m_props.setProperty(PageManager.PROP_PAGEPROVIDER,
236: "VersioningFileProvider");
237: m_props.setProperty(WikiEngine.PROP_ENCODING, "UTF-8");
238: }
239:
240: public void saveProperties() {
241: // Write the file back to disk
242: try {
243: OutputStream out = null;
244: try {
245: out = new FileOutputStream(m_propertyFile);
246: m_props.store(out, null);
247: } finally {
248: if (out != null) {
249: out.close();
250: }
251: }
252: m_session
253: .addMessage(
254: INSTALL_INFO,
255: "Your new properties have been saved. Please restart your container (unless this was your first install). Scroll down a bit to see your new jspwiki.properties.");
256: } catch (IOException e) {
257: m_session
258: .addMessage(
259: INSTALL_ERROR,
260: "Unable to write properties: "
261: + e.getMessage()
262: + ". Please copy the file below as your jspwiki.properties:\n"
263: + m_props.toString());
264: }
265: }
266:
267: public boolean validateProperties() throws Exception {
268: m_session.clearMessages(INSTALL_ERROR);
269: parseProperties();
270: validateNotNull(BASE_URL,
271: "You must define the base URL for this wiki.");
272: validateNotNull(PAGE_DIR,
273: "You must define the location where the files are stored.");
274: validateNotNull(APP_NAME,
275: "You must define the application name.");
276: validateNotNull(WORK_DIR, "You must define a work directory.");
277: validateNotNull(LOG_DIR, "You must define a log directory.");
278:
279: if (m_session.getMessages(INSTALL_ERROR).length == 0) {
280: m_validated = true;
281: }
282: return m_validated;
283: }
284:
285: /**
286: * Sets a property based on the value of an HTTP request parameter.
287: * If the parameter is not found, a default value is used instead.
288: * @param request the HTTP request
289: * @param param the parameter containing the value we will extract
290: * @param defaultValue the default to use if the parameter was not passed
291: * in the request
292: */
293: private void parseProperty(String param, String defaultValue) {
294: String value = m_request.getParameter(param);
295: if (value == null) {
296: value = defaultValue;
297: }
298: m_props.put(param, value);
299: }
300:
301: /**
302: * Simply sanitizes any path which contains backslashes (sometimes Windows
303: * users may have them) by expanding them to double-backslashes
304: * @param s the key of the property to sanitize
305: */
306: private void sanitizePath(String key) {
307: String s = m_props.getProperty(key);
308: s = TextUtil.replaceString(s, "\\", "\\\\");
309: s = s.trim();
310: m_props.put(key, s);
311: }
312:
313: /**
314: * Simply sanitizes any URL which contains backslashes (sometimes Windows
315: * users may have them)
316: * @param s the key of the property to sanitize
317: */
318: private void sanitizeURL(String key) {
319: String s = m_props.getProperty(key);
320: s = TextUtil.replaceString(s, "\\", "/");
321: s = s.trim();
322: m_props.put(key, s);
323: }
324:
325: private void validateNotNull(String key, String message) {
326: String value = m_props.getProperty(key);
327: if (value == null || value.length() == 0) {
328: m_session.addMessage(INSTALL_ERROR, message);
329: }
330: }
331:
332: }
|