I'm writing a java component which build all the pages in a wiki. What would be the best way to assign the user rights or groups which may view the page or spaces from within the java service component?
Really struggling to find details on this in the API.
You are finding no details in the API, because the Rights API is only about reading rights information, not setting rights
If you want to set permissions on pages programatically, and you can assume that the default permission handler is in place (which both the UI and the code to create new users in XWiki does, so it seems not too unreasonbale), you can create them as objects in the pages.
Permissions are set by adding objects of type "XWiki.XWikiRights" to the pages
these objects have the following attributes:
groups : a string containing a comma separated list of group references (e.g. XWiki.XWikiAdminGroup,XWiki.XWikiAllGroup for the default admin and "all members" group)
users : a string containing a comma separated list of user references (e.g. xwiki:XWiki.Admin,XWiki.Admin would describe the main wiki admin and the "Admin" account in the local wiki
levels : a string containing a comma separated list of permissions who ate affected by this entry, e.g. view,comment,edit
allow : an integer which should have two values: 1 means the entry is an "allow this right", 0 means it is a "deny these rights"
The groups and users fields can be empty, though usually one of them is filled with data. The levels and allow must be set with some values.
One example how a permission is set on a page is the (internal) method XWiki.protectUserPage which sets the permissions on a newly create user in the way this user can edit ones one profile page:
public void protectUserPage(String userName, String userRights, XWikiDocument doc, XWikiContext context)
throws XWikiException
{
DocumentReference rightClassReference = getRightsClass(context).getDocumentReference();
EntityReference relativeRightClassReference =
rightClassReference.removeParent(rightClassReference.getWikiReference());
// Allow users to edit their own profiles
BaseObject newuserrightsobject = doc.newXObject(relativeRightClassReference, context);
newuserrightsobject.setLargeStringValue("users", userName);
newuserrightsobject.setStringValue("levels", userRights);
newuserrightsobject.setIntValue("allow", 1);
}
Here the first few lines are slightly more complicated to make sure the XWiki.XWikiRights class page is present and properly initialized; without harm you should be able to do something simpler like:
BaseObject newrightsobject = doc.newObject("XWiki.XWikiRights", context);
The userRights is usually edit here (it was only that while looking for the code that I found out this is actually configurable ...); userName is the full name of the users profile page here (e.g XWiki.NewUser)
The actual full code can be viewed at github e.g. for the 7.2 release:
https://github.com/xwiki/xwiki-platform/blob/xwiki-platform-7.2/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/XWiki.java#L3366
Finally to distinguish between rights only given to a specific page, and rights given to a page and all its subpages: if you want a rights object to be valid for subpages, too, do not add it to the page itself, but create a special subpage with name WebPreferences and add an object of type XWiki.XWikiGlobalRights (with the same fields) to that page.
Some further pointers:
for more details how the access rights work, see http://platform.xwiki.org/xwiki/bin/view/AdminGuide/Access+Rights especially the reference section: "An overview of permissions"
If you have installed the "Admin Tools" extension, you can view the "ShowRights" page to see all right objects in you wiki.
Related
Is there a way to provide security and authorization for assets in AEM?
We drop assets to the pages and all of the assets are viewed by the end users after login. we want to restrict that an asset on the page should be displayed only to the specific users(even though they can login). If the user has an URL of the asset and after logging in they are able to view that asset.
The requirement is to restrict the asset to the particular users only even they can login to the page.
Appreciate in your time.
This feature is supported OOTB in AEM. You need to build right groups with right access to the assets. By default on publish instance 'anonymous' user has read rights on the /content/dam. You need to do following -
Remove anonymous user access, also check for all other groups to which you want to disable access and update them accordingly (You need to do this using /useradmin console)
Create the different groups to which you can assign different users based on the required access (its better to govern access via groups than by users)
To these each group provide access to relevant folder under /content/dam
Add users to these group as needed
Now when the user logs-in, they will have access to the assets restricted to their group only.
For more details on user management in AEM read this article.
As you said you want authors to have control over which groups can view the assets. So in author dialog of component populate all user group to make it configurable for author. Then in your code check whether the group has permission against the asset path-
UserManager userManager = resourceResolver.adaptTo(UserManager.class);
Authorizable auth = userManager.getAuthorizable(<<group configured by author>>);
JackrabbitAccessControlManager acm = (JackrabbitAccessControlManager) adminSession.getAccessControlManager();
Set<Principal> principals = new HashSet<Principal>();
principals.add(auth.getPrincipal());
Privilege[] privileges = acm.getPrivileges(<<current assets path>>, principals);
you can check the privilege array for permissions. If group has permission than only render the asset.
I see cq:lastModified in the page property which gives me the User who modified the page at the latest. Is there any way to get the list of latest 10 users who modified the page ? Does AEM stores that kind of information at all?
Thanks!
When on the page in CQ, if you open the Information tab in the Sidekick you can view the Audit log — this will show you modification actions on the page, including page activation, e.g.:
I think this stores 15 entries by default (I'm not sure if that number is editable).
Alternatively, you can view the History log under $CQ_HOME/crx-quickstart/logs/history.log — this will show entries for View/Edit/Delete on individual nodes (so for example, you can see that a component was edited rather than just a page).
It can be rotated by date or size as per other CQ logs, & will show:
Timestamp
Action
Node
Node type
For example:
28.07.2014 15:59:05 VIEW admin [/content/dam/geometrixx/travel/train_platform_boarding.jpg] [dam:Asset,mix:versionable]
There is no OOTB way to do this.
But here is how you can try to achieve it :
1) Create Custom workflow with a custom process step.
In this workflow process step copy the cq:lastModifiedBy property value to a new custom property(lets call this lastModifiedUsers, which will be an array)
2) Now create a launcher which runs on modified for cq:PageContent node type. Use this launcher to trigger workflow created in step 1.
Now everytime you modify this page, the launcher will trigger the workflow which will copy the cq:lastModifiedBy property value to this custom property which is an array and save it in the path-path/jcr:content node.
Use AuditLog Interface from com.day.cq.audit package and you can use AuditLog object to invoke getLatestEvents(String[] categories, String path, int max) here specify the max as 10 .
you will receive an array of AuditLogEntry objects and from this array you can get all user id's.
I want to add a space in the home of the administrator on alfresco but I want the space to be visible only to the administrator (adminstrator home is the Company Home).
I used this code to add the space in the admin's home but the hidden part I don't know how to use:
NodeRef personNodeRef = personService.getPerson("admin");
NodeRef homespaceNodeRef = (NodeRef) nodeService.getProperty(
personNodeRef,
ContentModel.PROP_HOMEFOLDER);
serviceRegistry.getFileFolderService().create(
homespaceNodeRef,
LOG_FOLDER_NAME,
ContentModel.TYPE_FOLDER);
I'm persuaded that I'm gonna add a property but I don't know which, can any one help ?
If you want to do it via code and looking for api
PermissionService of alfresco has API
void setInheritParentPermissions(org.alfresco.service.cmr.repository.NodeRef nodeRef, boolean inheritParentPermissions)
if you pass true in second argument it will set inheripermission as false so none of the other person able to see space create by admin(provided space is created by admin user)
otherwise as I had suggested in comment you can do it from user interface as well.
I have a template accountlist.scala.html looking like this:
#(accounts: models.domain.AccountList)
#titlebar = {<p>some html</p>}
#content = {
#for(account <- accounts) {
<p>#account.name</p>
}
}
#main(titlebar)(content)
... and another template account.scala.html like this:
#(account: models.domain.Account)
#titlebar = {<p>#account.name</p>}
#content = {
#for(transaction <- account.getTransactions()) {
<p>#transaction.detail</p>
}
}
#main(titlebar)(content)
From both of them I am invoking the template main.scala.html.
I have access to the entire Account POJO in the first view accountlist.scala.html, so really there is no need for me to invoke the server to get the details of the account when I go to the view in which I display the details. I would just like to change view on the client side. How could I call the second view account.scala.html from the view accountlist.scala.html a user clicks on an account in the list? I am ready to change the templates as needed.
I have provided a previous answer, which is still available at the end of this post. From your comments, however I understand that you are asking for something else without understanding how dangerous it is.
There are three ways of handling your use case, let's start with the worst one.
A stateful web application
If you have loaded data into a Pojo from some data source and you want to re-use the Pojo between multiple requests, what you are trying to do is to implement some kind of client-state on the server, such as a cache. Web applications have been developed in this way for long time and this was the source of major bugs and errors. What happens if the underlying account data in the database is updated between one http request and the following? Your client won't see it, because it use cached data. Additionally, what happens when the user performs a back in his browser? How do you get notified on the server side so you keep track of where the user is in his navigation flow? For these and others reasons, Play! is designed to be stateless. If you are really into web applications, you probably need to read about what is the REST architectural style.
A stateless web application
In a stateless web applications, you are not allowed to keep data between two http requests, so you have two ways to handle it:
Generate the user interface in a single shot
This is the approach which you can use when your account data is reduced. You embed all the necessary data from each account into your page and you generate the view, which you keep hidden and you show only when the user clicks. Please note that you can generate the HTML on the server side and with Javascript makes only certain part of your DOM visible, or just transfer a JSON representation of your accounts and use some kind of templating library to build the necessary UI directly on the client
Generate the user interface when required
This approach becomes necessary when the account data structure contains too many informations, and you don't want to transfer all this information for all the accounts on the client at first. For example, if you know the user is going to be interested in seeing the details only of very few accounts, you want to require the details only when the user asks for it.
For example, in your list of accounts you will have a button associated with each account, called details and you will use the account id to send a new request to the server.
#(accounts: models.domain.AccountList)
#titlebar = {<p>some html</p>}
#content = {
#for(account <- accounts) {
<p>#account.name <button class="details" href="#routes.Controllers.account(account.id)">details</button></p>
}
}
Please note that you can also generate the user interface on the client side, but you will still need to retrieve it from the server the data structures when the user clicks on the button. This will ensure that the user retrieves the last available state of the account.
Old answer
If your goal is to reuse your views, Play views are nothing else then Scala classes, so you can import them:
#import packagename._
and then you use it in another template:
#for(account <- accounts) {
#account(account)
}
The question reveals a misunderstanding of play framework templates. When compiling the play project the template code is transformed to html, css and javascript.
You can not "invoke"/link another template showing the account transactions from a href attribute of your Account row. However, you can do any of the following:
In case you have loaded all transactions from all accounts to the client in one go: extend the template to generate separate <div> sections for each account showing the transactions. Also generate javascript to 1) hide the overview div and 2) show the specific transaction div when clicking on one of the accounts in the overview. Please see the knockout library proposed by Edmondo1984 or the accordion or tabs in twitter bootstrap.
In case you only load the account overview from the server. Generate a link such as this one href="#routes.Controllers.account(account.id)" (see Edmondo1984 answer) and make another template to view this data.
Since the question concerned a case in which you got all data from the server, go by option 1.
I am working on an existing Web based application.
Now, I need to secure the application against what I think is called url hacking. For instance, if the customer with customerId 1 is logged in and viewing his profile, the following http get variable will be visible in the address field: customerId=1.
I need to prevent a customer from being able to set customerId=2 and see the profile of another customer.
The problem is that, the application is already in production and in good working condition, so the changes should be minimal with respect to this change.
How is this best achieved?
Any sugggestions/comments?
why do you give the id in the URL when the user should only be allowed to change his profile? I don't see any need for this. Rather get the current user from SecurityConext and display its profile on an URL without the id.
with the new information you gave in the comments I suggest sth. like this:
just check if the given orderid in the URL belongs to the current user.
You're saying you use "normal web based Application" so I assume Servlet/jsp based. In your servlet you would do something like this:
int orderId = Integer.parseInt(request.getParameter("orderId"));
String username = request.getUserPrincipal().getName();
/*now you need to check if username match with the username of the order e.g. by using hibernate to get the order by id and check its user and if not throw PermissionDeniedException or similiar*/
95% agree with Korgen's answer above.
5% - if you want to allow administrator access to edit user profiles using the same functionality just switch to UUID to identify edited user.