diff --git a/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/BaseAPIHandler.java b/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/BaseAPIHandler.java index 642692415..b34b8aabd 100644 --- a/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/BaseAPIHandler.java +++ b/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/BaseAPIHandler.java @@ -28,9 +28,12 @@ import org.apache.roller.weblogger.config.WebloggerRuntimeConfig; import org.apache.roller.weblogger.business.WebloggerFactory; import org.apache.roller.weblogger.business.UserManager; +import org.apache.roller.weblogger.business.WeblogEntryManager; import org.apache.roller.weblogger.business.WeblogManager; import org.apache.roller.weblogger.pojos.User; import org.apache.roller.weblogger.pojos.Weblog; +import org.apache.roller.weblogger.pojos.WeblogEntry; +import org.apache.roller.weblogger.pojos.WeblogPermission; import org.apache.roller.weblogger.ui.core.RollerContext; import org.apache.roller.weblogger.util.cache.CacheManager; import org.apache.xmlrpc.common.XmlRpcNotAuthorizedException; @@ -102,97 +105,133 @@ public BaseAPIHandler() { //------------------------------------------------------------------------ /** - * Returns website, but only if user authenticates and is authorized to edit. - * @param blogid Blogid sent in request (used as website's handle) + * Returns a weblog only when the authenticated user has the requested + * permission and XML-RPC access is enabled for that weblog. + */ + protected Weblog validate(String blogid, String username, String password, + String requiredAction) throws Exception { + User user = validateUser(username, password); + return validateWeblog(blogid, user, requiredAction); + } + + /** + * Validate a weblog for an already authenticated user. + */ + protected Weblog validateWeblog(String blogid, User user, + String requiredAction) throws Exception { + try { + WeblogManager weblogMgr = WebloggerFactory.getWeblogger() + .getWeblogManager(); + Weblog website = weblogMgr.getWeblogByHandle(blogid); + + // Use one response for missing, unavailable, and inaccessible weblogs. + if (!isWeblogAvailable(website) + || !website.hasUserPermission(user, requiredAction)) { + throw new XmlRpcNotAuthorizedException(WEBLOG_DISABLED_MSG); + } + return website; + } catch (XmlRpcNotAuthorizedException e) { + throw e; + } catch (Exception e) { + mLogger.error("ERROR internal error validating weblog", e); + throw new XmlRpcNotAuthorizedException(WEBLOG_DISABLED_MSG); + } + } + + //------------------------------------------------------------------------ + /** + * Returns the authenticated user if username/password are valid and the + * user is not disabled. * @param username Username sent in request * @param password Password sent in request */ - protected Weblog validate(String blogid, String username, String password) - throws Exception { + protected User validateUser(String username, String password) + throws Exception { + User user = null; boolean authenticated = false; - boolean userEnabled = false; - boolean weblogEnabled = false; - boolean apiEnabled = false; - boolean weblogFound = false; - Weblog website = null; try { UserManager userMgr = WebloggerFactory.getWeblogger().getUserManager(); - WeblogManager weblogMgr = WebloggerFactory.getWeblogger().getWeblogManager(); - User user = userMgr.getUserByUserName(username); - - website = weblogMgr.getWeblogByHandle(blogid); - if (website != null) { - weblogFound = true; - weblogEnabled = website.getVisible(); - apiEnabled = website.getEnableBloggerApi() - && WebloggerRuntimeConfig.getBooleanProperty("webservices.enableXmlRpc"); - } - - if (user != null) { - userEnabled = user.getEnabled(); - authenticated = RollerContext.getPasswordEncoder().matches(password, user.getPassword()); + user = userMgr.getUserByUserName(username); + if (user != null && RollerContext.getPasswordEncoder() != null) { + authenticated = RollerContext.getPasswordEncoder().matches( + password, user.getPassword()); } } catch (Exception e) { mLogger.error("ERROR internal error validating user", e); } - - if ( !authenticated ) { + + if (!authenticated) { throw new XmlRpcNotAuthorizedException(AUTHORIZATION_EXCEPTION_MSG); } - if ( !userEnabled ) { + + if (!Boolean.TRUE.equals(user.getEnabled())) { throw new XmlRpcNotAuthorizedException(USER_DISABLED_MSG); } - if ( !weblogEnabled ) { - throw new XmlRpcNotAuthorizedException(WEBLOG_DISABLED_MSG); + + if (!WebloggerRuntimeConfig.getBooleanProperty("webservices.enableXmlRpc")) { + throw new XmlRpcNotAuthorizedException(BLOGGERAPI_DISABLED_MSG); } - if ( !weblogFound ) { - throw new XmlRpcException(WEBLOG_NOT_FOUND, WEBLOG_NOT_FOUND_MSG); + + return user; + } + + /** + * Returns an entry only when it belongs to an available XML-RPC weblog and + * the user may edit it. An optional additional weblog action can be + * required for transitions such as publishing. + */ + protected WeblogEntry validateEntry(String postid, User user, + String additionalAction) throws Exception { + WeblogEntry entry = getEntryForWrite(postid, user); + if (entry == null) { + throw new XmlRpcException(INVALID_POSTID, INVALID_POSTID_MSG); } - if ( !apiEnabled ) { - throw new XmlRpcNotAuthorizedException(BLOGGERAPI_DISABLED_MSG); + if (additionalAction != null) { + try { + if (!entry.getWebsite().hasUserPermission(user, additionalAction)) { + throw new XmlRpcNotAuthorizedException( + AUTHORIZATION_EXCEPTION_MSG); + } + } catch (XmlRpcNotAuthorizedException e) { + throw e; + } catch (Exception e) { + mLogger.error("ERROR internal error validating entry action", e); + throw new XmlRpcNotAuthorizedException( + AUTHORIZATION_EXCEPTION_MSG); + } } - return website; + return entry; } - - //------------------------------------------------------------------------ + /** - * Returns true if username/password are valid and user is not disabled. - * @param username Username sent in request - * @param password Password sent in request + * Nullable form used by Blogger.deletePost(), whose public contract + * returns false when the entry is unavailable. */ - protected boolean validateUser(String username, String password) - throws Exception { - boolean authenticated = false; - boolean enabled = false; - boolean apiEnabled = false; + protected WeblogEntry getEntryForWrite(String postid, User user) + throws Exception { try { - - UserManager userMgr = WebloggerFactory.getWeblogger().getUserManager(); - User user = userMgr.getUserByUserName(username); - - if (user != null) { - enabled = user.getEnabled(); - authenticated = RollerContext.getPasswordEncoder().matches(password, user.getPassword()); - - apiEnabled = WebloggerRuntimeConfig.getBooleanProperty("webservices.enableXmlRpc"); + WeblogEntryManager entryMgr = WebloggerFactory.getWeblogger() + .getWeblogEntryManager(); + WeblogEntry entry = entryMgr.getWeblogEntry(postid); + if (entry == null || !isWeblogAvailable(entry.getWebsite()) + || !entry.getWebsite().hasUserPermission( + user, WeblogPermission.EDIT_DRAFT) + || !entry.hasWritePermissions(user)) { + return null; } + return entry; } catch (Exception e) { - mLogger.error("ERROR internal error validating user", e); - } - - if ( !authenticated ) { + mLogger.error("ERROR internal error validating weblog entry", e); throw new XmlRpcNotAuthorizedException(AUTHORIZATION_EXCEPTION_MSG); } - - if ( !enabled ) { - throw new XmlRpcNotAuthorizedException(USER_DISABLED_MSG); + } + + private boolean isWeblogAvailable(Weblog website) { + if (website == null) { + return false; } - - if ( !apiEnabled ) { - throw new XmlRpcNotAuthorizedException(BLOGGERAPI_DISABLED_MSG); - } - - return authenticated; + return Boolean.TRUE.equals(website.getVisible()) + && Boolean.TRUE.equals(website.getEnableBloggerApi()); } //------------------------------------------------------------------------ diff --git a/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/BloggerAPIHandler.java b/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/BloggerAPIHandler.java index ee7d40ab0..32fa5b551 100644 --- a/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/BloggerAPIHandler.java +++ b/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/BloggerAPIHandler.java @@ -19,10 +19,11 @@ package org.apache.roller.weblogger.webservices.xmlrpc; import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Comparator; import java.util.Date; import java.util.Hashtable; import java.util.List; -import java.util.Map; import java.util.StringTokenizer; import java.util.Vector; @@ -33,7 +34,6 @@ import org.apache.roller.weblogger.WebloggerException; import org.apache.roller.weblogger.business.Weblogger; import org.apache.roller.weblogger.business.WebloggerFactory; -import org.apache.roller.weblogger.business.UserManager; import org.apache.roller.weblogger.business.WeblogEntryManager; import org.apache.roller.weblogger.pojos.CustomTemplateRendition; import org.apache.roller.weblogger.pojos.TemplateRendition.RenditionType; @@ -43,6 +43,8 @@ import org.apache.roller.weblogger.pojos.WeblogEntrySearchCriteria; import org.apache.roller.weblogger.pojos.WeblogTemplate; import org.apache.roller.weblogger.pojos.Weblog; +import org.apache.roller.weblogger.pojos.WeblogPermission; +import org.apache.roller.weblogger.pojos.ThemeTemplate.ComponentType; import org.apache.roller.weblogger.util.Utilities; import org.apache.xmlrpc.XmlRpcException; @@ -59,7 +61,10 @@ public class BloggerAPIHandler extends BaseAPIHandler { static final long serialVersionUID = 2398898776655115019L; private static Log mLogger = LogFactory.getLog(BloggerAPIHandler.class); - + + /** Upper bound for a positive recent-post request. */ + protected static final int DRAFT_SCAN_CAP = 200; + public BloggerAPIHandler() { super(); } @@ -86,15 +91,14 @@ public boolean deletePost(String appkey, String postid, String userid, Weblogger roller = WebloggerFactory.getWeblogger(); WeblogEntryManager weblogMgr = roller.getWeblogEntryManager(); - WeblogEntry entry = weblogMgr.getWeblogEntry(postid); - - // Return false if entry not found + User user = validateUser(userid, password); + WeblogEntry entry = getEntryForWrite(postid, user); + + // Return false if entry is not available to this user. if (entry == null) { return false; } - validate(entry.getWebsite().getHandle(), userid, password); - try { // notify cache flushPageCache(entry.getWebsite()); @@ -138,7 +142,8 @@ public boolean setTemplate(String appkey, String blogid, String userid, mLogger.debug(" Template: " + templateData); mLogger.debug(" Type: " + templateType); - validate(blogid, userid, password); + Weblog website = validate(blogid, userid, password, + WeblogPermission.ADMIN); if (!templateType.equals("main")) { throw new XmlRpcException( @@ -146,7 +151,13 @@ public boolean setTemplate(String appkey, String blogid, String userid, } try { - WeblogTemplate template = WebloggerFactory.getWeblogger().getWeblogManager().getTemplate(templateType); + WeblogTemplate template = WebloggerFactory.getWeblogger() + .getWeblogManager().getTemplateByAction( + website, ComponentType.WEBLOG); + if (template == null) { + throw new XmlRpcException(UNKNOWN_EXCEPTION, + "Standard rendering of template not found"); + } CustomTemplateRendition ctr = template.getTemplateRendition(RenditionType.STANDARD); if (ctr != null) { ctr.setTemplate(templateData); @@ -186,11 +197,19 @@ public String getTemplate(String appkey, String blogid, String userid, mLogger.debug(" UserId: " + userid); mLogger.debug(" Type: " + templateType); - validate(blogid, userid,password); + Weblog website = validate(blogid, userid, password, + WeblogPermission.ADMIN); try { CustomTemplateRendition ctr = null; - WeblogTemplate template = WebloggerFactory.getWeblogger().getWeblogManager().getTemplate(templateType); + WeblogTemplate template; + if ("main".equals(templateType)) { + template = WebloggerFactory.getWeblogger().getWeblogManager() + .getTemplateByAction(website, ComponentType.WEBLOG); + } else { + template = WebloggerFactory.getWeblogger().getWeblogManager() + .getTemplateByName(website, templateType); + } if (template != null) { ctr = template.getTemplateRendition(RenditionType.STANDARD); } @@ -223,43 +242,33 @@ public Object getUserInfo(String appkey, String userid, String password) mLogger.debug(" Appkey: " + appkey); mLogger.debug(" UserId: " + userid); - validateUser(userid, password); + User user = validateUser(userid, password); - try { - Weblogger roller = WebloggerFactory.getWeblogger(); - UserManager userMgr = roller.getUserManager(); - User user = userMgr.getUserByUserName(userid); - - // parses full name into two strings, firstname and lastname - String firstname = "", lastname = ""; - StringTokenizer toker = new StringTokenizer(user.getFullName()); - - if (toker.hasMoreTokens()) { - firstname = toker.nextToken(); - } - - while (toker.hasMoreTokens()) { - if ( !lastname.isEmpty() ) { - lastname += " "; - } - lastname += toker.nextToken(); - } + // parses full name into two strings, firstname and lastname + String firstname = "", lastname = ""; + StringTokenizer toker = new StringTokenizer(user.getFullName()); - // TODO: Should screen name be renamed nickname and used here? - // populates user information to return as a result - Hashtable result = new Hashtable<>(); - result.put("nickname", user.getUserName()); - result.put("userid", user.getUserName()); - result.put("email", ""); - result.put("lastname", lastname); - result.put("firstname", firstname); - - return result; - } catch (WebloggerException e) { - String msg = "ERROR in BlooggerAPIHander.getInfo"; - mLogger.error(msg,e); - throw new XmlRpcException(UNKNOWN_EXCEPTION,msg); + if (toker.hasMoreTokens()) { + firstname = toker.nextToken(); } + + while (toker.hasMoreTokens()) { + if (!lastname.isEmpty()) { + lastname += " "; + } + lastname += toker.nextToken(); + } + + // TODO: Should screen name be renamed nickname and used here? + // populates user information to return as a result + Hashtable result = new Hashtable<>(); + result.put("nickname", user.getUserName()); + result.put("userid", user.getUserName()); + result.put("email", ""); + result.put("lastname", lastname); + result.put("firstname", firstname); + + return result; } @@ -280,28 +289,25 @@ public Object getUsersBlogs(String appkey, String userid, String password) mLogger.debug(" UserId: " + userid); Vector result = new Vector<>(); - if (validateUser(userid, password)) { - try { - UserManager umgr = WebloggerFactory.getWeblogger().getUserManager(); - User user = umgr.getUserByUserName(userid); - - // get list of user's enabled websites - List websites = WebloggerFactory.getWeblogger().getWeblogManager().getUserWeblogs(user, true); - for (Weblog website : websites) { - // only include weblog's that have client API support enabled - if (Boolean.TRUE.equals(website.getEnableBloggerApi())) { - Hashtable blog = new Hashtable<>(3); - blog.put("url", website.getURL()); - blog.put("blogid", website.getHandle()); - blog.put("blogName", website.getName()); - result.add(blog); - } + User user = validateUser(userid, password); + try { + // get list of user's enabled websites + List websites = WebloggerFactory.getWeblogger() + .getWeblogManager().getUserWeblogs(user, true); + for (Weblog website : websites) { + // only include weblogs that have client API support enabled + if (Boolean.TRUE.equals(website.getEnableBloggerApi())) { + Hashtable blog = new Hashtable<>(3); + blog.put("url", website.getURL()); + blog.put("blogid", website.getHandle()); + blog.put("blogName", website.getName()); + result.add(blog); } - } catch (Exception e) { - String msg = "ERROR in BlooggerAPIHander.getUsersBlogs"; - mLogger.error(msg,e); - throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); } + } catch (Exception e) { + String msg = "ERROR in BlooggerAPIHander.getUsersBlogs"; + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); } return result; } @@ -330,36 +336,35 @@ public boolean editPost(String appkey, String postid, String userid, mLogger.debug(" Publish: " + publish); mLogger.debug(" Content:\n " + content); - if (validateUser(userid, password)) { - try { - Timestamp current = new Timestamp(System.currentTimeMillis()); - - Weblogger roller = WebloggerFactory.getWeblogger(); - WeblogEntryManager weblogMgr = roller.getWeblogEntryManager(); - WeblogEntry entry = weblogMgr.getWeblogEntry(postid); - entry.setText(content); - entry.setUpdateTime(current); - if (publish) { - entry.setStatus(PubStatus.PUBLISHED); - } else { - entry.setStatus(PubStatus.DRAFT); - } - - // save the entry - weblogMgr.saveWeblogEntry(entry); - roller.flush(); - - // notify cache - flushPageCache(entry.getWebsite()); - - return true; - } catch (Exception e) { - String msg = "ERROR in BlooggerAPIHander.editPost"; - mLogger.error(msg,e); - throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); + User user = validateUser(userid, password); + WeblogEntry entry = validateEntry(postid, user, + publish ? WeblogPermission.POST : null); + try { + Timestamp current = new Timestamp(System.currentTimeMillis()); + + Weblogger roller = WebloggerFactory.getWeblogger(); + WeblogEntryManager weblogMgr = roller.getWeblogEntryManager(); + entry.setText(content); + entry.setUpdateTime(current); + if (publish) { + entry.setStatus(PubStatus.PUBLISHED); + } else { + entry.setStatus(PubStatus.DRAFT); } + + // save the entry + weblogMgr.saveWeblogEntry(entry); + roller.flush(); + + // notify cache + flushPageCache(entry.getWebsite()); + + return true; + } catch (Exception e) { + String msg = "ERROR in BlooggerAPIHander.editPost"; + mLogger.error(msg,e); + throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); } - return false; } @@ -386,7 +391,9 @@ public String newPost(String appkey, String blogid, String userid, mLogger.debug(" Publish: " + publish); mLogger.debug(" Content:\n " + content); - Weblog website = validate(blogid, userid, password); + User user = validateUser(userid, password); + Weblog website = validateWeblog(blogid, user, + publish ? WeblogPermission.POST : WeblogPermission.EDIT_DRAFT); // extract the title from the content String title = ""; @@ -413,7 +420,6 @@ public String newPost(String appkey, String blogid, String userid, entry.setLocale(website.getLocale()); entry.setPubTime(current); entry.setUpdateTime(current); - User user = roller.getUserManager().getUserByUserName(userid); entry.setCreatorUserName(user.getUserName()); entry.setWebsite(website); entry.setCategory(website.getBloggerCategory()); @@ -463,30 +469,28 @@ public Object getRecentPosts(String appkey, String blogid, String userid, mLogger.debug(" UserId: " + userid); mLogger.debug(" Number: " + numposts); - Weblog weblog = validate(blogid, userid,password); + User user = validateUser(userid, password); + Weblog weblog = validateWeblog(blogid, user, + WeblogPermission.EDIT_DRAFT); try { Vector results = new Vector<>(); - - Weblogger roller = WebloggerFactory.getWeblogger(); - WeblogEntryManager weblogMgr = roller.getWeblogEntryManager(); if (weblog != null) { - WeblogEntrySearchCriteria wesc = new WeblogEntrySearchCriteria(); - wesc.setWeblog(weblog); - wesc.setEndDate(new Date()); - Map> entries = weblogMgr.getWeblogEntryObjectMap(wesc); - - for (List weList : entries.values()) { - for (WeblogEntry entry : weList) { - Hashtable result = new Hashtable<>(); - if (entry.getPubTime() != null) { - result.put("dateCreated", entry.getPubTime()); - } - result.put("userid", userid); - result.put("postid", entry.getId()); - result.put("content", entry.getText()); - results.add(result); + List entries = getRecentEntries(weblog, user, + numposts, WeblogEntrySearchCriteria.SortBy.PUBLICATION_TIME, + new Date()); + for (WeblogEntry entry : entries) { + if (!entry.hasWritePermissions(user)) { + continue; + } + Hashtable result = new Hashtable<>(); + if (entry.getPubTime() != null) { + result.put("dateCreated", entry.getPubTime()); } + result.put("userid", userid); + result.put("postid", entry.getId()); + result.put("content", entry.getText()); + results.add(result); } } return results; @@ -496,5 +500,51 @@ public Object getRecentPosts(String appkey, String blogid, String userid, throw new XmlRpcException(UNKNOWN_EXCEPTION, msg); } } + + protected List getRecentEntries(Weblog weblog, User user, + int requestedLimit, WeblogEntrySearchCriteria.SortBy sortBy, + Date endDate) throws Exception { + int effectiveLimit = requestedLimit <= 0 ? -1 + : Math.min(requestedLimit, DRAFT_SCAN_CAP); + WeblogEntryManager entryManager = WebloggerFactory.getWeblogger() + .getWeblogEntryManager(); + + if (weblog.hasUserPermission(user, WeblogPermission.POST)) { + return entryManager.getWeblogEntries(recentCriteria(weblog, null, + sortBy, endDate, effectiveLimit)); + } + + List entries = new ArrayList<>(); + entries.addAll(entryManager.getWeblogEntries(recentCriteria(weblog, + PubStatus.DRAFT, sortBy, endDate, effectiveLimit))); + entries.addAll(entryManager.getWeblogEntries(recentCriteria(weblog, + PubStatus.PENDING, sortBy, endDate, effectiveLimit))); + + Comparator newestFirst = + Comparator.nullsLast(Comparator.reverseOrder()); + if (sortBy == WeblogEntrySearchCriteria.SortBy.UPDATE_TIME) { + entries.sort(Comparator.comparing( + WeblogEntry::getUpdateTime, newestFirst)); + } else { + entries.sort(Comparator.comparing( + WeblogEntry::getPubTime, newestFirst)); + } + if (effectiveLimit > 0 && entries.size() > effectiveLimit) { + return new ArrayList<>(entries.subList(0, effectiveLimit)); + } + return entries; + } + + private WeblogEntrySearchCriteria recentCriteria(Weblog weblog, + PubStatus status, WeblogEntrySearchCriteria.SortBy sortBy, + Date endDate, int maxResults) { + WeblogEntrySearchCriteria criteria = new WeblogEntrySearchCriteria(); + criteria.setWeblog(weblog); + criteria.setStatus(status); + criteria.setSortBy(sortBy); + criteria.setEndDate(endDate); + criteria.setMaxResults(maxResults); + return criteria; + } } diff --git a/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/MetaWeblogAPIHandler.java b/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/MetaWeblogAPIHandler.java index dc8ce3f69..2172191fa 100644 --- a/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/MetaWeblogAPIHandler.java +++ b/app/src/main/java/org/apache/roller/weblogger/webservices/xmlrpc/MetaWeblogAPIHandler.java @@ -43,6 +43,7 @@ import org.apache.roller.weblogger.pojos.WeblogEntry; import org.apache.roller.weblogger.pojos.WeblogEntry.PubStatus; import org.apache.roller.weblogger.pojos.WeblogEntrySearchCriteria; +import org.apache.roller.weblogger.pojos.WeblogPermission; import org.apache.roller.weblogger.util.RollerMessages; import org.apache.roller.weblogger.util.Utilities; import org.apache.xmlrpc.XmlRpcException; @@ -82,7 +83,8 @@ public Object getCategories(String blogid, String userid, String password) mLogger.debug(" BlogId: " + blogid); mLogger.debug(" UserId: " + userid); - Weblog website = validate(blogid, userid,password); + Weblog website = validate(blogid, userid, password, + WeblogPermission.EDIT_DRAFT); Weblogger roller = WebloggerFactory.getWeblogger(); try { Hashtable result = new Hashtable<>(); @@ -129,9 +131,9 @@ public boolean editPost(String postid, String userid, String password, Weblogger roller = WebloggerFactory.getWeblogger(); WeblogEntryManager weblogMgr = roller.getWeblogEntryManager(); - WeblogEntry entry = weblogMgr.getWeblogEntry(postid); - - validate(entry.getWebsite().getHandle(), userid,password); + User user = validateUser(userid, password); + WeblogEntry entry = validateEntry(postid, user, + publish ? WeblogPermission.POST : null); Hashtable postcontent = struct; String description = (String)postcontent.get("description"); @@ -227,7 +229,9 @@ public String newPost(String blogid, String userid, String password, mLogger.debug(" UserId: " + userid); mLogger.debug(" Publish: " + publish); - Weblog website = validate(blogid, userid, password); + User user = validateUser(userid, password); + Weblog website = validateWeblog(blogid, user, + publish ? WeblogPermission.POST : WeblogPermission.EDIT_DRAFT); Hashtable postcontent = struct; String description = (String)postcontent.get("description"); @@ -252,7 +256,6 @@ public String newPost(String blogid, String userid, String password, try { Weblogger roller = WebloggerFactory.getWeblogger(); WeblogEntryManager weblogMgr = roller.getWeblogEntryManager(); - User user = roller.getUserManager().getUserByUserName(userid); Timestamp current = new Timestamp(System.currentTimeMillis()); WeblogEntry entry = new WeblogEntry(); @@ -330,14 +333,8 @@ public Object getPost(String postid, String userid, String password) mLogger.debug(" PostId: " + postid); mLogger.debug(" UserId: " + userid); - Weblogger roller = WebloggerFactory.getWeblogger(); - WeblogEntryManager weblogMgr = roller.getWeblogEntryManager(); - WeblogEntry entry = weblogMgr.getWeblogEntry(postid); - - if (entry == null) { - throw new XmlRpcException(INVALID_POSTID, INVALID_POSTID_MSG); - } - validate(entry.getWebsite().getHandle(), userid, password); + User user = validateUser(userid, password); + WeblogEntry entry = validateEntry(postid, user, null); try { return createPostStruct(entry, userid); @@ -362,7 +359,8 @@ public Object newMediaObject(String blogid, String userid, String password, mLogger.debug(" UserId: " + userid); mLogger.debug(" Password: *********"); - Weblog website = validate(blogid, userid, password); + Weblog website = validate(blogid, userid, password, + WeblogPermission.POST); try { String name = (String) struct.get("name"); name = name.replace("/","_"); @@ -425,21 +423,21 @@ public Object getRecentPosts(String blogid, String userid, String password, mLogger.debug(" UserId: " + userid); mLogger.debug(" Number: " + numposts); - Weblog website = validate(blogid, userid,password); + User user = validateUser(userid, password); + Weblog website = validateWeblog(blogid, user, + WeblogPermission.EDIT_DRAFT); try { Vector results = new Vector<>(); - - Weblogger roller = WebloggerFactory.getWeblogger(); - WeblogEntryManager weblogMgr = roller.getWeblogEntryManager(); if (website != null) { - WeblogEntrySearchCriteria wesc = new WeblogEntrySearchCriteria(); - wesc.setWeblog(website); - wesc.setSortBy(WeblogEntrySearchCriteria.SortBy.UPDATE_TIME); - wesc.setMaxResults(numposts); - List entries = weblogMgr.getWeblogEntries(wesc); + List entries = getRecentEntries(website, user, + numposts, WeblogEntrySearchCriteria.SortBy.UPDATE_TIME, + null); for (WeblogEntry entry : entries) { + if (!entry.hasWritePermissions(user)) { + continue; + } results.addElement(createPostStruct(entry, userid)); } } diff --git a/app/src/test/java/org/apache/roller/weblogger/webservices/xmlrpc/BaseAPIHandlerTest.java b/app/src/test/java/org/apache/roller/weblogger/webservices/xmlrpc/BaseAPIHandlerTest.java new file mode 100644 index 000000000..c46dae428 --- /dev/null +++ b/app/src/test/java/org/apache/roller/weblogger/webservices/xmlrpc/BaseAPIHandlerTest.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. For additional information regarding + * copyright in this work, please see the NOTICE file in the top level + * directory of this distribution. + */ + +package org.apache.roller.weblogger.webservices.xmlrpc; + +import org.apache.roller.weblogger.WebloggerException; +import org.apache.roller.weblogger.business.Weblogger; +import org.apache.roller.weblogger.business.WebloggerFactory; +import org.apache.roller.weblogger.business.WeblogEntryManager; +import org.apache.roller.weblogger.business.WeblogManager; +import org.apache.roller.weblogger.pojos.User; +import org.apache.roller.weblogger.pojos.WeblogPermission; +import org.apache.xmlrpc.common.XmlRpcNotAuthorizedException; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +public class BaseAPIHandlerTest { + + @Test + public void testWeblogLookupFailureReturnsNeutralAuthorizationFault() + throws Exception { + Weblogger weblogger = mock(Weblogger.class); + WeblogManager manager = mock(WeblogManager.class); + when(weblogger.getWeblogManager()).thenReturn(manager); + when(manager.getWeblogByHandle("unavailable")) + .thenThrow(new WebloggerException("database details")); + + try (MockedStatic factory = + mockStatic(WebloggerFactory.class)) { + factory.when(WebloggerFactory::getWeblogger).thenReturn(weblogger); + XmlRpcNotAuthorizedException fault = assertThrows( + XmlRpcNotAuthorizedException.class, + () -> new TestHandler().validateWeblogForTest( + "unavailable", mock(User.class))); + assertFalse(fault.getMessage().contains("database details")); + } + } + + @Test + public void testEntryLookupFailureReturnsNeutralAuthorizationFault() + throws Exception { + Weblogger weblogger = mock(Weblogger.class); + WeblogEntryManager manager = mock(WeblogEntryManager.class); + when(weblogger.getWeblogEntryManager()).thenReturn(manager); + when(manager.getWeblogEntry("unavailable")) + .thenThrow(new WebloggerException("database details")); + + try (MockedStatic factory = + mockStatic(WebloggerFactory.class)) { + factory.when(WebloggerFactory::getWeblogger).thenReturn(weblogger); + XmlRpcNotAuthorizedException fault = assertThrows( + XmlRpcNotAuthorizedException.class, + () -> new TestHandler().getEntryForWriteForTest( + "unavailable", mock(User.class))); + assertFalse(fault.getMessage().contains("database details")); + } + } + + private static class TestHandler extends BaseAPIHandler { + private static final long serialVersionUID = 1L; + + void validateWeblogForTest(String handle, User user) throws Exception { + validateWeblog(handle, user, WeblogPermission.EDIT_DRAFT); + } + + void getEntryForWriteForTest(String id, User user) throws Exception { + getEntryForWrite(id, user); + } + } +} diff --git a/app/src/test/java/org/apache/roller/weblogger/webservices/xmlrpc/XMLRPCWeblogPermissionTest.java b/app/src/test/java/org/apache/roller/weblogger/webservices/xmlrpc/XMLRPCWeblogPermissionTest.java new file mode 100644 index 000000000..4e775e3fe --- /dev/null +++ b/app/src/test/java/org/apache/roller/weblogger/webservices/xmlrpc/XMLRPCWeblogPermissionTest.java @@ -0,0 +1,521 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.roller.weblogger.webservices.xmlrpc; + +import java.lang.reflect.Field; +import java.sql.Timestamp; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import org.apache.roller.weblogger.TestUtils; +import org.apache.roller.weblogger.business.PropertiesManager; +import org.apache.roller.weblogger.business.UserManager; +import org.apache.roller.weblogger.business.WeblogEntryManager; +import org.apache.roller.weblogger.business.WeblogManager; +import org.apache.roller.weblogger.business.Weblogger; +import org.apache.roller.weblogger.business.WebloggerFactory; +import org.apache.roller.weblogger.pojos.CustomTemplateRendition; +import org.apache.roller.weblogger.pojos.RuntimeConfigProperty; +import org.apache.roller.weblogger.pojos.TemplateRendition.RenditionType; +import org.apache.roller.weblogger.pojos.TemplateRendition.TemplateLanguage; +import org.apache.roller.weblogger.pojos.ThemeTemplate.ComponentType; +import org.apache.roller.weblogger.pojos.User; +import org.apache.roller.weblogger.pojos.Weblog; +import org.apache.roller.weblogger.pojos.WeblogCategory; +import org.apache.roller.weblogger.pojos.WeblogEntry; +import org.apache.roller.weblogger.pojos.WeblogEntry.PubStatus; +import org.apache.roller.weblogger.pojos.WeblogPermission; +import org.apache.roller.weblogger.pojos.WeblogTemplate; +import org.apache.roller.weblogger.ui.core.RollerContext; +import org.apache.xmlrpc.XmlRpcException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.crypto.password.DelegatingPasswordEncoder; +import org.springframework.security.crypto.password.NoOpPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Integration coverage for the Blogger and MetaWeblog permission matrix. + */ +public class XMLRPCWeblogPermissionTest { + + private static final String PASSWORD = "password"; + + private final BloggerAPIHandler blogger = new BloggerAPIHandler(); + private final MetaWeblogAPIHandler metaWeblog = new MetaWeblogAPIHandler(); + + private User owner; + private User author; + private User limited; + private User outsider; + private Weblog weblog; + private Weblog otherWeblog; + private WeblogTemplate otherTemplate; + private WeblogEntry draftEntry; + private WeblogEntry secondDraftEntry; + private WeblogEntry pendingEntry; + private WeblogEntry publishedEntry; + private WeblogEntry deleteEntry; + private String oldXmlRpcValue; + private Object oldPasswordEncoder; + + @BeforeEach + public void setUp() throws Exception { + TestUtils.setupWeblogger(); + installTestPasswordEncoder(); + + Weblogger roller = WebloggerFactory.getWeblogger(); + PropertiesManager propertiesManager = roller.getPropertiesManager(); + RuntimeConfigProperty xmlRpcProperty = propertiesManager + .getProperty("webservices.enableXmlRpc"); + oldXmlRpcValue = xmlRpcProperty.getValue(); + xmlRpcProperty.setValue("true"); + propertiesManager.saveProperty(xmlRpcProperty); + + owner = TestUtils.setupUser("xmlrpcOwner"); + author = TestUtils.setupUser("xmlrpcAuthor"); + limited = TestUtils.setupUser("xmlrpcLimited"); + outsider = TestUtils.setupUser("xmlrpcOutsider"); + + weblog = TestUtils.setupWeblog("xmlrpcPermissionWeblog", owner); + otherWeblog = TestUtils.setupWeblog("xmlrpcOtherWeblog", owner); + enableXmlRpc(weblog); + enableXmlRpc(otherWeblog); + + UserManager userManager = roller.getUserManager(); + userManager.grantWeblogPermission(weblog, author, + Collections.singletonList(WeblogPermission.POST)); + userManager.grantWeblogPermission(weblog, limited, + Collections.singletonList(WeblogPermission.EDIT_DRAFT)); + + createMainTemplate(weblog, "main-template-code"); + otherTemplate = createMainTemplate(otherWeblog, "other-template-code"); + TestUtils.endSession(true); + + draftEntry = TestUtils.setupWeblogEntry("xmlrpc-draft", + weblog.getWeblogCategories().iterator().next(), PubStatus.DRAFT, + weblog, owner); + secondDraftEntry = TestUtils.setupWeblogEntry("xmlrpc-second-draft", + weblog.getWeblogCategories().iterator().next(), PubStatus.DRAFT, + weblog, owner); + pendingEntry = TestUtils.setupWeblogEntry("xmlrpc-pending", + weblog.getWeblogCategories().iterator().next(), PubStatus.PENDING, + weblog, owner); + publishedEntry = TestUtils.setupWeblogEntry("xmlrpc-published", + weblog.getWeblogCategories().iterator().next(), PubStatus.PUBLISHED, + weblog, owner); + deleteEntry = TestUtils.setupWeblogEntry("xmlrpc-delete", + weblog.getWeblogCategories().iterator().next(), PubStatus.DRAFT, + weblog, owner); + TestUtils.endSession(true); + } + + @AfterEach + public void tearDown() throws Exception { + TestUtils.teardownWeblog(weblog.getId()); + TestUtils.teardownWeblog(otherWeblog.getId()); + TestUtils.teardownUser(owner.getUserName()); + TestUtils.teardownUser(author.getUserName()); + TestUtils.teardownUser(limited.getUserName()); + TestUtils.teardownUser(outsider.getUserName()); + + PropertiesManager propertiesManager = WebloggerFactory.getWeblogger() + .getPropertiesManager(); + RuntimeConfigProperty xmlRpcProperty = propertiesManager + .getProperty("webservices.enableXmlRpc"); + xmlRpcProperty.setValue(oldXmlRpcValue); + propertiesManager.saveProperty(xmlRpcProperty); + TestUtils.endSession(true); + restorePasswordEncoder(); + } + + @Test + public void testAuthenticationAndWeblogDiscovery() throws Exception { + @SuppressWarnings("unchecked") + Hashtable userInfo = (Hashtable) blogger + .getUserInfo("", outsider.getUserName(), PASSWORD); + assertEquals(outsider.getUserName(), userInfo.get("userid")); + + @SuppressWarnings("unchecked") + Vector ownerBlogs = (Vector) blogger.getUsersBlogs("", + owner.getUserName(), PASSWORD); + assertEquals(2, ownerBlogs.size()); + + Weblog managedOtherWeblog = WebloggerFactory.getWeblogger() + .getWeblogManager().getWeblog(otherWeblog.getId()); + managedOtherWeblog.setEnableBloggerApi(false); + WebloggerFactory.getWeblogger().getWeblogManager() + .saveWeblog(managedOtherWeblog); + TestUtils.endSession(true); + + @SuppressWarnings("unchecked") + Vector enabledOwnerBlogs = (Vector) blogger.getUsersBlogs( + "", owner.getUserName(), PASSWORD); + assertEquals(1, enabledOwnerBlogs.size()); + assertEquals(weblog.getHandle(), postField(enabledOwnerBlogs.get(0), + "blogid")); + + @SuppressWarnings("unchecked") + Vector outsiderBlogs = (Vector) blogger.getUsersBlogs("", + outsider.getUserName(), PASSWORD); + assertTrue(outsiderBlogs.isEmpty()); + + assertThrows(XmlRpcException.class, () -> blogger.getUserInfo("", + outsider.getUserName(), "wrong-password")); + + User managedOutsider = WebloggerFactory.getWeblogger().getUserManager() + .getUserByUserName(outsider.getUserName()); + managedOutsider.setEnabled(false); + WebloggerFactory.getWeblogger().getUserManager().saveUser(managedOutsider); + TestUtils.endSession(true); + assertThrows(XmlRpcException.class, () -> blogger.getUserInfo("", + outsider.getUserName(), PASSWORD)); + } + + @Test + public void testGlobalXmlRpcSettingIsRequired() throws Exception { + PropertiesManager propertiesManager = WebloggerFactory.getWeblogger() + .getPropertiesManager(); + RuntimeConfigProperty xmlRpcProperty = propertiesManager + .getProperty("webservices.enableXmlRpc"); + xmlRpcProperty.setValue("false"); + propertiesManager.saveProperty(xmlRpcProperty); + + assertThrows(XmlRpcException.class, () -> blogger.getUserInfo("", + owner.getUserName(), PASSWORD)); + } + + @Test + public void testWeblogOperationPermissionMatrix() throws Exception { + String ownerName = owner.getUserName(); + String authorName = author.getUserName(); + String limitedName = limited.getUserName(); + String outsiderName = outsider.getUserName(); + String handle = weblog.getHandle(); + + assertEquals("main-template-code", blogger.getTemplate("", handle, + ownerName, PASSWORD, "main")); + assertTrue(blogger.setTemplate("", handle, ownerName, PASSWORD, + "updated-template-code", "main")); + assertEquals("updated-template-code", blogger.getTemplate("", handle, + ownerName, PASSWORD, "main")); + assertThrows(XmlRpcException.class, () -> blogger.getTemplate("", handle, + authorName, PASSWORD, "main")); + assertThrows(XmlRpcException.class, () -> blogger.setTemplate("", handle, + authorName, PASSWORD, "unapproved", "main")); + + // Supplying another weblog's template ID must not escape the requested weblog. + XmlRpcException foreignTemplate = assertThrows(XmlRpcException.class, + () -> blogger.getTemplate("", handle, ownerName, PASSWORD, + otherTemplate.getId())); + XmlRpcException missingTemplate = assertThrows(XmlRpcException.class, + () -> blogger.getTemplate("", handle, ownerName, PASSWORD, + "missing-template")); + assertEquals(missingTemplate.code, foreignTemplate.code); + assertEquals(missingTemplate.getMessage(), foreignTemplate.getMessage()); + + assertNotNull(metaWeblog.getCategories(handle, limitedName, PASSWORD)); + assertThrows(XmlRpcException.class, () -> metaWeblog.getCategories(handle, + outsiderName, PASSWORD)); + + assertNotNull(blogger.newPost("", handle, limitedName, PASSWORD, + "draft from limited member", false)); + assertThrows(XmlRpcException.class, () -> blogger.newPost("", handle, + limitedName, PASSWORD, "published by limited member", true)); + assertNotNull(blogger.newPost("", handle, authorName, PASSWORD, + "published by author", true)); + + Hashtable draft = postStruct("meta draft"); + assertNotNull(metaWeblog.newPost(handle, limitedName, PASSWORD, draft, + false)); + assertThrows(XmlRpcException.class, () -> metaWeblog.newPost(handle, + limitedName, PASSWORD, postStruct("meta published"), true)); + + // Invalid media input stops after authorization without writing a file. + Hashtable media = new Hashtable<>(); + XmlRpcException authorMediaError = assertThrows(XmlRpcException.class, + () -> metaWeblog.newMediaObject(handle, authorName, PASSWORD, + media)); + assertEquals(BaseAPIHandler.UNKNOWN_EXCEPTION, authorMediaError.code); + XmlRpcException limitedMediaError = assertThrows(XmlRpcException.class, + () -> metaWeblog.newMediaObject(handle, limitedName, PASSWORD, + media)); + assertTrue(limitedMediaError.code != BaseAPIHandler.UNKNOWN_EXCEPTION); + } + + @Test + public void testEntryOperationPermissionMatrix() throws Exception { + String authorName = author.getUserName(); + String limitedName = limited.getUserName(); + String outsiderName = outsider.getUserName(); + + assertTrue(blogger.editPost("", draftEntry.getId(), limitedName, + PASSWORD, "limited draft edit", false)); + XmlRpcException bloggerPublishDenied = assertThrows(XmlRpcException.class, + () -> blogger.editPost("", + secondDraftEntry.getId(), limitedName, PASSWORD, + "limited publish", true)); + assertTrue(bloggerPublishDenied.code != BaseAPIHandler.INVALID_POSTID); + assertTrue(blogger.editPost("", secondDraftEntry.getId(), authorName, + PASSWORD, "author publish", true)); + assertThrows(XmlRpcException.class, () -> blogger.editPost("", + draftEntry.getId(), outsiderName, PASSWORD, "foreign edit", false)); + + assertNotNull(metaWeblog.getPost(draftEntry.getId(), limitedName, PASSWORD)); + assertThrows(XmlRpcException.class, () -> metaWeblog.getPost( + publishedEntry.getId(), limitedName, PASSWORD)); + assertNotNull(metaWeblog.getPost(publishedEntry.getId(), authorName, + PASSWORD)); + + assertTrue(metaWeblog.editPost(draftEntry.getId(), limitedName, PASSWORD, + postStruct("limited meta edit"), false)); + XmlRpcException metaPublishDenied = assertThrows(XmlRpcException.class, + () -> metaWeblog.editPost( + draftEntry.getId(), limitedName, PASSWORD, + postStruct("limited meta publish"), true)); + assertTrue(metaPublishDenied.code != BaseAPIHandler.INVALID_POSTID); + + assertFalse(blogger.deletePost("", publishedEntry.getId(), outsiderName, + PASSWORD, false)); + assertTrue(blogger.deletePost("", deleteEntry.getId(), limitedName, + PASSWORD, false)); + + XmlRpcException missing = assertThrows(XmlRpcException.class, + () -> metaWeblog.getPost("missing-entry", outsiderName, PASSWORD)); + XmlRpcException inaccessible = assertThrows(XmlRpcException.class, + () -> metaWeblog.getPost(publishedEntry.getId(), outsiderName, + PASSWORD)); + assertEquals(missing.code, inaccessible.code); + assertEquals(missing.getMessage(), inaccessible.getMessage()); + } + + @Test + public void testRecentPostsAreFilteredForLimitedMembers() throws Exception { + String handle = weblog.getHandle(); + String limitedName = limited.getUserName(); + String authorName = author.getUserName(); + + @SuppressWarnings("unchecked") + Vector bloggerLimited = (Vector) blogger.getRecentPosts( + "", handle, limitedName, PASSWORD, 50); + assertContainsPost(bloggerLimited, draftEntry.getId()); + assertContainsPost(bloggerLimited, pendingEntry.getId()); + assertDoesNotContainPost(bloggerLimited, publishedEntry.getId()); + + @SuppressWarnings("unchecked") + Vector metaLimited = (Vector) metaWeblog.getRecentPosts( + handle, limitedName, PASSWORD, 50); + assertContainsPost(metaLimited, draftEntry.getId()); + assertContainsPost(metaLimited, pendingEntry.getId()); + assertDoesNotContainPost(metaLimited, publishedEntry.getId()); + + @SuppressWarnings("unchecked") + Vector metaAuthor = (Vector) metaWeblog.getRecentPosts( + handle, authorName, PASSWORD, 50); + assertContainsPost(metaAuthor, publishedEntry.getId()); + + addNewerPublishedEntries(205); + + @SuppressWarnings("unchecked") + Vector bloggerAfterPublished = + (Vector) blogger.getRecentPosts( + "", handle, limitedName, PASSWORD, 50); + assertContainsPost(bloggerAfterPublished, draftEntry.getId()); + + @SuppressWarnings("unchecked") + Vector metaAfterPublished = + (Vector) metaWeblog.getRecentPosts( + handle, limitedName, PASSWORD, 50); + assertContainsPost(metaAfterPublished, draftEntry.getId()); + + @SuppressWarnings("unchecked") + Vector bloggerBounded = (Vector) blogger.getRecentPosts( + "", handle, authorName, PASSWORD, 1000000); + assertEquals(BloggerAPIHandler.DRAFT_SCAN_CAP, bloggerBounded.size()); + + @SuppressWarnings("unchecked") + Vector metaBounded = (Vector) metaWeblog.getRecentPosts( + handle, authorName, PASSWORD, 1000000); + assertEquals(BloggerAPIHandler.DRAFT_SCAN_CAP, metaBounded.size()); + + @SuppressWarnings("unchecked") + Vector bloggerLegacy = (Vector) blogger.getRecentPosts( + "", handle, limitedName, PASSWORD, 0); + assertContainsPost(bloggerLegacy, draftEntry.getId()); + + @SuppressWarnings("unchecked") + Vector metaLegacy = (Vector) metaWeblog.getRecentPosts( + handle, limitedName, PASSWORD, -1); + assertContainsPost(metaLegacy, draftEntry.getId()); + + assertThrows(XmlRpcException.class, () -> blogger.getRecentPosts("", + handle, outsider.getUserName(), PASSWORD, 50)); + assertThrows(XmlRpcException.class, () -> metaWeblog.getRecentPosts( + handle, outsider.getUserName(), PASSWORD, 50)); + } + + @Test + public void testDisabledWeblogAndApiAreRejected() throws Exception { + WeblogManager weblogManager = WebloggerFactory.getWeblogger() + .getWeblogManager(); + Weblog managedWeblog = weblogManager.getWeblog(weblog.getId()); + managedWeblog.setEnableBloggerApi(false); + weblogManager.saveWeblog(managedWeblog); + TestUtils.endSession(true); + + assertThrows(XmlRpcException.class, () -> metaWeblog.getCategories( + weblog.getHandle(), owner.getUserName(), PASSWORD)); + + managedWeblog = WebloggerFactory.getWeblogger().getWeblogManager() + .getWeblog(weblog.getId()); + managedWeblog.setEnableBloggerApi(true); + managedWeblog.setVisible(true); + managedWeblog.setActive(false); + WebloggerFactory.getWeblogger().getWeblogManager() + .saveWeblog(managedWeblog); + TestUtils.endSession(true); + + assertNotNull(metaWeblog.getCategories( + weblog.getHandle(), owner.getUserName(), PASSWORD)); + + managedWeblog = WebloggerFactory.getWeblogger().getWeblogManager() + .getWeblog(weblog.getId()); + managedWeblog.setEnableBloggerApi(true); + managedWeblog.setVisible(false); + WebloggerFactory.getWeblogger().getWeblogManager() + .saveWeblog(managedWeblog); + TestUtils.endSession(true); + + assertThrows(XmlRpcException.class, () -> metaWeblog.getCategories( + weblog.getHandle(), owner.getUserName(), PASSWORD)); + } + + private void enableXmlRpc(Weblog target) throws Exception { + target.setEnableBloggerApi(true); + WebloggerFactory.getWeblogger().getWeblogManager().saveWeblog(target); + } + + private void addNewerPublishedEntries(int count) throws Exception { + Weblogger roller = WebloggerFactory.getWeblogger(); + WeblogEntryManager entryManager = roller.getWeblogEntryManager(); + Weblog managedWeblog = roller.getWeblogManager().getWeblog(weblog.getId()); + User managedOwner = roller.getUserManager() + .getUserByUserName(owner.getUserName()); + WeblogCategory category = managedWeblog.getWeblogCategories() + .iterator().next(); + + WeblogEntry managedDraft = entryManager.getWeblogEntry(draftEntry.getId()); + Timestamp oldTime = new Timestamp(946684800000L); + managedDraft.setPubTime(oldTime); + managedDraft.setUpdateTime(oldTime); + entryManager.saveWeblogEntry(managedDraft); + + long now = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + WeblogEntry entry = new WeblogEntry(); + entry.setTitle("newer published " + i); + entry.setText("newer published entry"); + entry.setAnchor("xmlrpc-newer-published-" + i); + entry.setPubTime(new Timestamp(now - count + i)); + entry.setUpdateTime(new Timestamp(now - count + i)); + entry.setStatus(PubStatus.PUBLISHED); + entry.setWebsite(managedWeblog); + entry.setCreatorUserName(managedOwner.getUserName()); + entry.setCategory(category); + entryManager.saveWeblogEntry(entry); + } + roller.flush(); + TestUtils.endSession(true); + } + + @SuppressWarnings("deprecation") + private void installTestPasswordEncoder() throws Exception { + Field encoderField = RollerContext.class.getDeclaredField("encoder"); + encoderField.setAccessible(true); + oldPasswordEncoder = encoderField.get(null); + + PasswordEncoder noOp = NoOpPasswordEncoder.getInstance(); + Map encoders = new HashMap<>(); + encoders.put(null, noOp); + encoders.put("noop", noOp); + encoderField.set(null, new DelegatingPasswordEncoder("noop", encoders)); + } + + private void restorePasswordEncoder() throws Exception { + Field encoderField = RollerContext.class.getDeclaredField("encoder"); + encoderField.setAccessible(true); + encoderField.set(null, oldPasswordEncoder); + } + + private WeblogTemplate createMainTemplate(Weblog target, String code) + throws Exception { + WeblogTemplate template = new WeblogTemplate(); + template.setAction(ComponentType.WEBLOG); + template.setName("Weblog"); + template.setDescription("Test weblog template"); + template.setLink("Weblog"); + template.setLastModified(new Date()); + template.setWeblog(target); + + WeblogManager weblogManager = WebloggerFactory.getWeblogger() + .getWeblogManager(); + weblogManager.saveTemplate(template); + CustomTemplateRendition rendition = new CustomTemplateRendition( + template, RenditionType.STANDARD); + rendition.setTemplate(code); + rendition.setTemplateLanguage(TemplateLanguage.VELOCITY); + weblogManager.saveTemplateRendition(rendition); + return template; + } + + private Hashtable postStruct(String title) { + Hashtable struct = new Hashtable<>(); + struct.put("title", title); + struct.put("description", title + " body"); + return struct; + } + + private void assertContainsPost(List posts, String postId) { + assertTrue(posts.stream().map(this::postId).anyMatch(postId::equals)); + } + + private void assertDoesNotContainPost(List posts, String postId) { + assertFalse(posts.stream().map(this::postId).anyMatch(postId::equals)); + } + + @SuppressWarnings("unchecked") + private String postId(Object post) { + return ((Hashtable) post).get("postid").toString(); + } + + @SuppressWarnings("unchecked") + private String postField(Object post, String field) { + return ((Hashtable) post).get(field); + } +}