/** * {@inheritDoc} * * @see ch.entwine.weblounge.common.security.DirectoryService#loadUser(java.lang.String, * ch.entwine.weblounge.common.site.Site) */ public User loadUser(String login, Site site) { JpaAccount jpaAccount = null; // Load the user account and the user try { jpaAccount = persistence.getAccount(site.getIdentifier(), login, true); } catch (Throwable e) { logger.error("Error loading user '{}' from the database: {}", login, e.getMessage()); return null; } // Is that user known if (jpaAccount == null) { logger.debug("User '{}' is not known in site '{}'", login, site.getIdentifier()); return null; } // Create the weblounge user WebloungeUser user = new WebloungeUserImpl(login, site.getIdentifier()); // Standard attributes like first name, name, ... if (StringUtils.isNotBlank(jpaAccount.getFirstname())) user.setFirstName(jpaAccount.getFirstname()); if (StringUtils.isNotBlank(jpaAccount.getLastname())) user.setLastName(jpaAccount.getLastname()); if (StringUtils.isNotBlank(jpaAccount.getEmail())) user.setEmail(jpaAccount.getEmail()); if (StringUtils.isNotBlank(jpaAccount.getInitials())) user.setInitials(jpaAccount.getInitials()); // Password user.addPrivateCredentials(new PasswordImpl(jpaAccount.getPassword(), DigestType.md5)); // Roles for (JpaRole r : jpaAccount.getRoles()) { // Make sure weblounge roles get special treatment in order // to support role inheritance. Other directories will need // to implement this through a LoginListener implementation if (Security.SYSTEM_CONTEXT.equals(r.getContext())) { if (SYSTEMADMIN.getIdentifier().equals(r.getRolename())) { user.addPublicCredentials(SYSTEMADMIN); } else if (SITEADMIN.getIdentifier().equals(r.getRolename())) { user.addPublicCredentials(SITEADMIN); } else if (PUBLISHER.getIdentifier().equals(r.getRolename())) { user.addPublicCredentials(PUBLISHER); } else if (EDITOR.getIdentifier().equals(r.getRolename())) { user.addPublicCredentials(EDITOR); } else if (GUEST.getIdentifier().equals(r.getRolename())) { user.addPublicCredentials(GUEST); } } else { user.addPublicCredentials(new RoleImpl(r.getContext(), r.getRolename())); } } return user; }
/** * {@inheritDoc} * * @see * ch.entwine.weblounge.security.sql.SQLDirectoryProvider#activateAccount(ch.entwine.weblounge.common.site.Site, * java.lang.String, java.lang.String) */ public boolean activateAccount(Site site, String login, String activationCode) throws Exception { if (StringUtils.isBlank(login)) throw new IllegalArgumentException("Login must not be blank"); if (StringUtils.isBlank(activationCode)) throw new IllegalArgumentException("Activation code must not be blank"); JpaAccount account = persistence.getAccount(site.getIdentifier(), login, true); if (account == null) return false; if (!activationCode.equals(account.getActivationCode())) return false; account.setEnabled(true); account.setActivationCode(null); persistence.updateAccount(account); logger.info("Account '{}' has been activated", login); return true; }
/** * {@inheritDoc} * * @see * ch.entwine.weblounge.security.sql.SQLDirectoryProvider#addAccount(ch.entwine.weblounge.common.site.Site, * java.lang.String, String) */ public JpaAccount addAccount(Site site, String user, String password) throws Exception { // Check for existing administrative accounts with the same login ServiceReference userDirectoryRef = bundleCtx.getServiceReference(DirectoryService.class.getName()); if (userDirectoryRef != null) { DirectoryService systemDirectory = (DirectoryService) bundleCtx.getService(userDirectoryRef); logger.debug("Checking new site '{}' user '{}' for shadowing of site or system account"); User shadowedUser = systemDirectory.loadUser(user, site); if (shadowedUser != null) { if (SecurityUtils.userHasRole(shadowedUser, SYSTEMADMIN)) throw new UserShadowedException( "Site '" + site.getIdentifier() + "' account '" + user + "' is shadowing the system account"); else if (SecurityUtils.userHasRole(shadowedUser, SITEADMIN)) throw new UserShadowedException( "Site '" + site.getIdentifier() + "' account '" + user + "' is shadowing the site account"); else throw new UserExistsException( "Site '" + site.getIdentifier() + "' account '" + user + "' already exists"); } } else { logger.warn( "Directory service not found, site '{}' user '{}' cannot be checked for user shadowing", site.getIdentifier(), user); } return persistence.addAccount(site.getIdentifier(), user, password); }
/** * {@inheritDoc} * * @see * ch.entwine.weblounge.common.content.PreviewGenerator#createPreview(ch.entwine.weblounge.common.content.Resource, * ch.entwine.weblounge.common.site.Environment, * ch.entwine.weblounge.common.language.Language, * ch.entwine.weblounge.common.content.image.ImageStyle, String, java.io.InputStream, * java.io.OutputStream) */ public void createPreview( Resource<?> resource, Environment environment, Language language, ImageStyle style, String format, InputStream is, OutputStream os) throws IOException { // We don't need the input stream IOUtils.closeQuietly(is); // Find a suitable image preview generator for scaling ImagePreviewGenerator imagePreviewGenerator = null; synchronized (previewGenerators) { for (ImagePreviewGenerator generator : previewGenerators) { if (generator.supports(format)) { imagePreviewGenerator = generator; break; } } if (imagePreviewGenerator == null) { logger.debug("Unable to generate page previews since no image renderer is available"); return; } } // Find the relevant metadata to start the request ResourceURI uri = resource.getURI(); long version = resource.getVersion(); Site site = uri.getSite(); // Create the url URL pageURL = new URL( UrlUtils.concat( site.getHostname(environment).toExternalForm(), PAGE_HANDLER_PREFIX, uri.getIdentifier())); if (version == Resource.WORK) { pageURL = new URL( UrlUtils.concat( pageURL.toExternalForm(), "work_" + language.getIdentifier() + ".html")); } else { pageURL = new URL( UrlUtils.concat( pageURL.toExternalForm(), "index_" + language.getIdentifier() + ".html")); } // Create a temporary file final File rendererdFile = File.createTempFile("phantomjs-", "." + format, phantomTmpDir); final URL finalPageURL = pageURL; final AtomicBoolean success = new AtomicBoolean(); // Call PhantomJS to render the page try { final PhantomJsProcessExecutor phantomjs = new PhantomJsProcessExecutor( scriptFile.getAbsolutePath(), pageURL.toExternalForm(), rendererdFile.getAbsolutePath()) { @Override protected void onProcessFinished(int exitCode) throws IOException { super.onProcessFinished(exitCode); switch (exitCode) { case 0: if (rendererdFile.length() > 0) { success.set(true); logger.debug( "Page preview of {} created at {}", finalPageURL, rendererdFile.getAbsolutePath()); } else { logger.warn("Error creating page preview of {}", finalPageURL); success.set(false); FileUtils.deleteQuietly(rendererdFile); } break; default: success.set(false); logger.warn("Error creating page preview of {}", finalPageURL); FileUtils.deleteQuietly(rendererdFile); } } }; // Finally have PhantomJS create the preview logger.debug("Creating preview of {}", finalPageURL); phantomjs.execute(); } catch (ProcessExcecutorException e) { logger.warn("Error creating page preview of {}: {}", pageURL, e.getMessage()); throw new IOException(e); } finally { // If page preview rendering failed, there is no point in scaling the // images if (!success.get()) { logger.debug("Skipping scaling of failed preview rendering {}", pageURL); FileUtils.deleteQuietly(rendererdFile); return; } } FileInputStream imageIs = null; // Scale the image to the correct size try { imageIs = new FileInputStream(rendererdFile); imagePreviewGenerator.createPreview( resource, environment, language, style, PREVIEW_FORMAT, imageIs, os); } catch (IOException e) { logger.error("Error reading original page preview from " + rendererdFile, e); throw e; } catch (Throwable t) { logger.warn("Error scaling page preview at " + uri + ": " + t.getMessage(), t); throw new IOException(t); } finally { IOUtils.closeQuietly(imageIs); FileUtils.deleteQuietly(rendererdFile); } }
/** * {@inheritDoc} * * @see ch.entwine.weblounge.common.scheduler.JobWorker#execute(java.lang.String, * java.util.Dictionary) */ public void execute(String name, Dictionary<String, Serializable> ctx) throws JobException { Site site = (Site) ctx.get(Site.class.getName()); // Make sure the site is ready to accept content if (site.getContentRepository().isReadOnly()) { logger.warn("Unable to publish e-mail messages to site '{}': repository is read only", site); return; } WritableContentRepository repository = (WritableContentRepository) site.getContentRepository(); // Extract the configuration from the job properties String provider = (String) ctx.get(OPT_PROVIDER); Account account = null; try { if (StringUtils.isBlank(provider)) { provider = DEFAULT_PROVIDER; } account = new Account(ctx); } catch (IllegalArgumentException e) { throw new JobException(this, e); } // Connect to the server Properties sessionProperties = new Properties(); Session session = Session.getDefaultInstance(sessionProperties, null); Store store = null; Folder inbox = null; try { // Connect to the server try { store = session.getStore(provider); store.connect(account.getHost(), account.getLogin(), account.getPassword()); } catch (NoSuchProviderException e) { throw new JobException( this, "Unable to connect using unknown e-mail provider '" + provider + "'", e); } catch (MessagingException e) { throw new JobException(this, "Error connecting to " + provider + " account " + account, e); } // Open the account's inbox try { inbox = store.getFolder(INBOX); if (inbox == null) throw new JobException(this, "No inbox found at " + account); inbox.open(Folder.READ_WRITE); } catch (MessagingException e) { throw new JobException(this, "Error connecting to inbox at " + account, e); } // Get the messages from the server try { for (Message message : inbox.getMessages()) { if (!message.isSet(Flag.SEEN)) { try { Page page = aggregate(message, site); message.setFlag(Flag.DELETED, true); repository.put(page, true); logger.info("E-Mail message published at {}", page.getURI()); } catch (Exception e) { logger.info("E-Mail message discarded: {}", e.getMessage()); message.setFlag(Flag.SEEN, true); // TODO: Reply to sender if the "from" field exists } } } } catch (MessagingException e) { throw new JobException(this, "Error loading e-mail messages from inbox", e); } // Close the connection // but don't remove the messages from the server } finally { if (inbox != null) { try { inbox.close(true); } catch (MessagingException e) { throw new JobException(this, "Error closing inbox", e); } } if (store != null) { try { store.close(); } catch (MessagingException e) { throw new JobException(this, "Error closing connection to e-mail server", e); } } } }
/** * Aggregates the e-mail message by reading it and turning it either into a page or a file upload. * * @param message the e-mail message * @param site the site to publish to * @throws MessagingException if fetching the message data fails * @throws IOException if writing the contents to the output stream fails */ protected Page aggregate(Message message, Site site) throws IOException, MessagingException, IllegalArgumentException { ResourceURI uri = new PageURIImpl(site, UUID.randomUUID().toString()); Page page = new PageImpl(uri); Language language = site.getDefaultLanguage(); // Extract title and subject. Without these two, creating a page is not // feasible, therefore both messages throw an IllegalArgumentException if // the fields are not present. String title = getSubject(message); String author = getAuthor(message); // Collect default settings PageTemplate template = site.getDefaultTemplate(); if (template == null) throw new IllegalStateException("Missing default template in site '" + site + "'"); String stage = template.getStage(); if (StringUtils.isBlank(stage)) throw new IllegalStateException( "Missing stage definition in template '" + template.getIdentifier() + "'"); // Standard fields page.setTitle(title, language); page.setTemplate(template.getIdentifier()); page.setPublished(new UserImpl(site.getAdministrator()), message.getReceivedDate(), null); // TODO: Translate e-mail "from" into site user and throw if no such // user can be found page.setCreated(site.getAdministrator(), message.getSentDate()); // Start looking at the message body String contentType = message.getContentType(); if (StringUtils.isBlank(contentType)) throw new IllegalArgumentException("Message content type is unspecified"); // Text body if (contentType.startsWith("text/plain")) { // TODO: Evaluate charset String body = null; if (message.getContent() instanceof String) body = (String) message.getContent(); else if (message.getContent() instanceof InputStream) body = IOUtils.toString((InputStream) message.getContent()); else throw new IllegalArgumentException("Message body is of unknown type"); return handleTextPlain(body, page, language); } // HTML body if (contentType.startsWith("text/html")) { // TODO: Evaluate charset return handleTextHtml((String) message.getContent(), page, null); } // Multipart body else if ("mime/multipart".equalsIgnoreCase(contentType)) { Multipart mp = (Multipart) message.getContent(); for (int i = 0, n = mp.getCount(); i < n; i++) { Part part = mp.getBodyPart(i); String disposition = part.getDisposition(); if (disposition == null) { MimeBodyPart mbp = (MimeBodyPart) part; if (mbp.isMimeType("text/plain")) { return handleTextPlain((String) mbp.getContent(), page, null); } else { // TODO: Implement special non-attachment cases here of // image/gif, text/html, ... throw new UnsupportedOperationException( "Multipart message bodies of type '" + mbp.getContentType() + "' are not yet supported"); } } else if (disposition.equals(Part.ATTACHMENT) || disposition.equals(Part.INLINE)) { logger.info("Skipping message attachment " + part.getFileName()); // saveFile(part.getFileName(), part.getInputStream()); } } throw new IllegalArgumentException( "Multipart message did not contain any recognizable content"); } // ? else { throw new IllegalArgumentException("Message body is of unknown type '" + contentType + "'"); } }
/** * {@inheritDoc} * * @see * ch.entwine.weblounge.security.sql.SQLDirectoryProvider#isAccountEnabled(ch.entwine.weblounge.common.site.Site, * java.lang.String) */ @Override public boolean isAccountEnabled(Site site, String user) throws Exception { return persistence.isAccountEnabled(site.getIdentifier(), user); }
/** * {@inheritDoc} * * @see * ch.entwine.weblounge.security.sql.SQLDirectoryProvider#disableAccount(ch.entwine.weblounge.common.site.Site, * java.lang.String) */ public void disableAccount(Site site, String user) throws Exception { persistence.disableAccount(site.getIdentifier(), user); logger.info("Logins into account '{}@{}' have been disabled", user, site.getIdentifier()); }
/** * {@inheritDoc} * * @see * ch.entwine.weblounge.security.sql.SQLDirectoryProvider#isSiteEnabled(ch.entwine.weblounge.common.site.Site) */ @Override public boolean isSiteEnabled(Site site) throws Exception { return persistence.isSiteEnabled(site.getIdentifier()); }
/** * {@inheritDoc} * * @see * ch.entwine.weblounge.security.sql.SQLDirectoryProvider#disableSite(ch.entwine.weblounge.common.site.Site) */ public void disableSite(Site site) throws Exception { persistence.disableSite(site.getIdentifier()); logger.info("Logins into site '{}' have been disabled", site.getIdentifier()); }
/** * {@inheritDoc} * * @see ch.entwine.weblounge.security.sql.SQLDirectoryProvider#getAccounts(Site) */ @Override public List<JpaAccount> getAccounts(Site site) throws Exception { return persistence.getAccounts(site.getIdentifier()); }
/** * {@inheritDoc} * * @see * ch.entwine.weblounge.security.sql.SQLDirectoryProvider#getAccount(ch.entwine.weblounge.common.site.Site, * java.lang.String, boolean) */ @Override public JpaAccount getAccount(Site site, String login, boolean enabledOnly) throws Exception { return persistence.getAccount(site.getIdentifier(), login, enabledOnly); }
/** * {@inheritDoc} * * @see * ch.entwine.weblounge.security.sql.SQLDirectoryProvider#removeAccount(ch.entwine.weblounge.common.site.Site, * java.lang.String) */ public void removeAccount(Site site, String login) throws Exception { persistence.removeAccount(site.getIdentifier(), login); }