/**
   * {@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);
    }
  }
Example #5
0
  /**
   * {@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);
        }
      }
    }
  }
Example #6
0
  /**
   * 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);
 }