How should I get and set a folder's group and user permission settings via Core Service? - tridion

I can get strings representing group and user permissions for a given folder with the following.
Code
// assumes Core Service client "client"
var folderData = client.Read("tcm:5-26-2", new ReadOptions()) as FolderData;
var accessControlEntryDataArray =
folderData.AccessControlList.AccessControlEntries;
Console.WriteLine(folderData.Title);
foreach (var accessControlEntryData in accessControlEntryDataArray)
{
Console.WriteLine("{0} has {1}",
accessControlEntryData.Trustee.Title,
accessControlEntryData.AllowedPermissions.ToString());
}
Output
Some Folder
Everyone has Read
Editor has None
Chief Editor has None
Publication Manager has None
Interaction Manager has None
T2011-CB-R2\areyes has All
[scope] Editor 020 Create has Read, Write
T2011-CB-R2\local1 has Read, Write, Delete
[rights] Author - Content has None
Seems like the four possible values for `AllowedPermissions are:
None
Read
Read, Write
Read, Write, Delete
All
This works great for my use case to create a folder permissions report. I can .Replace() these to a familiar notation (e.g. rw-- or rwdl).
But is manipulating these string values the right approach to set permissions as well? I'd imagine I'd want objects or maybe enums instead. Could someone point me in the right direction?
Also I noticed I get some, but not all non-applicable groups set as None. I don't specifically need them here, but I'm curious at what determines whether those get returned--did I miss something in my code?

Rights and Permissions are enums, indeed. You can set using the method below. If you want to set multiple rights you should do something like "Rights.Read | Rights.Write"
Keep in mind that this method will return you object that you have to save \ update \ create after
public static OrganizationalItemData SetPermissionsOnOrganizationalItem(
OrganizationalItemData organizationalItem,
TrusteeData trustee,
Permissions allowedPermissions,
Permissions deniedPermissions = Permissions.None)
{
if (organizationalItem.AccessControlList == null)
{
organizationalItem.AccessControlList
= new AccessControlListData
{AccessControlEntries = new AccessControlEntryData[0]};
}
var entries = organizationalItem.AccessControlList
.AccessControlEntries.ToList();
// First check if this trustee already has some permissions
var entry = entries.SingleOrDefault(
ace => ace.Trustee.IdRef == trustee.Id);
if (entry != null)
{
// Remove this entry
entries.Remove(entry);
}
entries.Add(new AccessControlEntryData
{
AllowedPermissions = allowedPermissions,
DeniedPermissions = deniedPermissions,
Trustee = new LinkToTrusteeData { IdRef = trustee.Id }
});
organizationalItem.AccessControlList.AccessControlEntries
= entries.ToArray();
return organizationalItem;
}

Related

How do we get the rights of a user in Sailpoint?

I'm trying to write a policy violation in Sailpoint. But I don't know how to get the rights of the user who is asking a new right. I've tried this:
List links1 = null;
IdentityService service = new IdentityService(context);
Application app1 = context.getObjectByName(Application.class, "Autres");
try {
links1 = service.getLinks(identity, app1);
} catch (Exception e)
{
System.out.println(e.getMessage());
e.printStackTrace();
}
List DUList1 = new ArrayList();
if(links1.size() != 0){
Object DUObj = links1.get(0).getAttribute("DU");
if(DUObj != null){
if (DUObj instanceof String)
DUList1.add((String) DUObj);
else
DUList1.addAll((List) DUObj);
}
}
It was supposed to return the list of the rights that the user has and the rights that he is asking. But it doesn't work.
"Links" in IIQ are accounts, which are entities that you get from IIQ Applications.
IIQ works with 2 types of "rights", which are entitlements and roles.
Entitlements are usually aggregated from IIQ Applications as "groups", while Roles are manually defined as pure IIQ entities. You may think of Roles as groups of entitlements (roughly speaking).
So in order to check what "rights" an IIQ identity has, you must check entitlements and roles.
Since you're working on a policy, I am assuming you're trying something for the access request. In this case, you can consider that roles will be translated into a set of entitlements, so all we have to check are, in the end of the day, entitlements.
I suppose your application "Autres" is aggregating both accounts and entitlements, right? So in your application schema, there is at least 1 account attribute that is marked as "entitlement". Let's say you have something like this
Account schema
login (correlates to your identity, identifies your account uniquely)
fullname
groupsXYZ (correlates to your entitlements)
Then your rule will get the entitlements using something like this
Application app = context.getObjectByName(Application.class, "Autres");
Link account = identity.getLink(app);
Object groups = account.getAttribute("groupsXYZ");
if (groups == null){
...
}else if (groups instanceof List){
...
}else if (groups instanceof String){
...
}
Now, notice that groups can be a List or a String. It depends if your account has one or more entitlements associated to it. Because of that, you need to check the type first (I really don't know why IIQ does not use List always, probably because the way they map their XMLs internally), but you must be careful with it and provide the appropriate typecast.
So in the case you're showing, does "DU" maps to an application account attribute which represents the entitlement? You can debug and print all the attributes for example in order to see what's associated to your account (link).
Now, if we're not talking about entitlements, but about IIQ Roles, then we're talking about something like this
Bundle someRole = context.getObjectByName(Bundle.class,"My IIQ Role ABC");
boolean hasRole = identity.hasRole(someRole);

how to read additional parameters in alfresco 5.1.1- aikau faceted search

Custom Search UI will be populated when user selects Complex asset in the Advance search screen drop down(apart from Folders,Contents) where 12 fields will be displayed .So when user clicks search button ,need to read those values and redirect to the alfresco repo files(org/alfresco/slingshot/search/search.get.js).We have already customized these files(search.get.js,search.lib.js) existed in the repository to suit out logic and working fine in 4.2.2;As we are migrating to 511,so we need to change this logic in customized faceted-search.get.js to read these values.How to write this logic in customized facted-search.get.js?
It's not actually possible to read those URL hash attributes in the faceted-search.get.js file because the JavaScript controller of the WebScript does not have access to that part of the URL (it only has information about the URL and the request parameters, not the hash parameters).
The hash parameters are actually handled on the client-side by the AlfSearchList widget.
Maybe you could explain what you're trying to achieve so that I can suggest an alternative - i.e. the end goal for the user, not the specifics of the coding you're trying to achieve.
We will be reading the querystring values something like below in the .get.js file.
function getNodeRef(){
var queryString = page.url.getQueryString();
var nodeRef = "NOT FOUND";
var stringArray = queryString.split("&");
for (var t = 0; t < stringArray.length; t++) {
if (stringArray[t].indexOf('nodeRef=') > -1) {
nodeRef = stringArray[t].split('=')[1];
break;
}
}
if (nodeRef !== "NOT FOUND") {
nodeRef = nodeRef.replace("://", "/");
return nodeRef;
}
else {
throw new Error("Node Reference is not found.");
}
}
It may be help you and we will wait for Dave Drapper suggestion also.

Google Apps Script To Copy Entire Google Drive File Structure; How To Avoid Timeouts?

My organization is switching to a Google Business account, and everyone needs to transfer their Drive files to their new accounts. Drive will not allow transfer of ownership between these accounts, so I've created a script to copy files and folders from the old account to the new account. (The old account's contents have been moved into a folder shared with the new account.)
Here's what I have so far:
function copyDrive() {
var originFolder = DriveApp.getFolderById(originFolderID);
var destination = DriveApp.getFolderById(destinationID);
copyFiles(originFolder, destination);
};
function copyFiles(passedFolder, targetFolder) {
var fileContents = passedFolder.getFiles();
var file;
var fileName;
while(fileContents.hasNext()) {
file = fileContents.next();
fileName = file.getName();
file.makeCopy(fileName, targetFolder);
}
copySubFolders(passedFolder, targetFolder);
};
function copySubFolders(passedFolder, targetFolder) {
var folderContents = passedFolder.getFolders();
var folder;
var folderName;
while(folderContents.hasNext()) {
folder = folderContents.next();
folderName = folder.getName();
var subFolderCopy = targetFolder.createFolder(folderName);
copyFiles(folder, subFolderCopy);
}
};
Please pardon any inelegance; I am new at this. The script actually works great and preserves the folder structure, but it times out after copying ~150 files and folders. I've been looking into how to use continuation tokens, and I've read this post closely. I think I'm stuck on a conceptual level, because I'm not sure how the continuation tokens will interact with the recursive functions I've set up. It seems like I will end up with a stack of my copySubFolders function, and they will each need their own continuation tokens. Of course they all use the same variable name for their iterators, so I really have no idea how to set that up.
Any thoughts? Sorry for posting such a helpless newbie question; I hope it will at least be an interesting problem for someone.
I think I have solved the conceptual problem, though I am getting
We're sorry, a server error occurred. Please wait a bit and try again. (line 9, file "Code")
when I try to execute it.
Basically, I set it up to only try to copy one top-level folder at a time, and for each one of those it uses the recursive functions I had before. It should save continuation tokens for that first level of folders and any files in the root folder so it can pick up in the next execution where it left off. This way, the tokens are not involved in my recursive stack of functions.
function copyDrive() {
var originFolder = DriveApp.getFolderById(originFolderID);
var destination = DriveApp.getFolderById(destinationID);
var scriptProperties = PropertiesService.getScriptProperties();
var fileContinuationToken = scriptProperties.getProperty('FILE_CONTINUATION_TOKEN');
var fileIterator = fileContinuationToken == null ?
originFolder.getFiles() : DriveApp.continueFileIterator(fileContinuationToken);
var folderContinuationToken = scriptProperties.getProperty('FOLDER_CONTINUATION_TOKEN');
var folderIterator = folderContinuationToken == null ?
originFolder.getFolders() : DriveApp.continueFolderIterator(folderContinuationToken);
try {
var rootFileName;
while(fileIterator.hasNext()) {
var rootFile = fileIterator.next();
rootFileName = rootFile.getName();
rootFile.makeCopy(rootFileName, destination);
}
var folder = folderIterator.next();
var folderName = folder.getName();
var folderCopy = folder.makeCopy(folderName, destination);
copyFiles(folder, folderCopy);
} catch(err) {
Logger.log(err);
}
if(fileIterator.hasNext()) {
scriptProperties.setProperty('FILE_CONTINUATION_TOKEN', fileIterator.getContinuationToken());
} else {
scriptProperties.deleteProperty('FILE_CONTINUATION_TOKEN');
}
if(folderIterator.hasNext()) {
scriptProperties.setProperty('FOLDER_CONTINUATION_TOKEN', folderIterator.getContinuationToken());
} else {
scriptProperties.deleteProperty('FOLDER_CONTINUATION_TOKEN');
}
};
function copyFiles(passedFolder, targetFolder) {
var fileContents = passedFolder.getFiles();
var file;
var fileName;
while(fileContents.hasNext()) {
file = fileContents.next();
fileName = file.getName();
file.makeCopy(fileName, targetFolder);
}
copySubFolders(passedFolder, targetFolder);
};
function copySubFolders(passedFolder, targetFolder) {
var subFolderContents = passedFolder.getFolders();
var subFolder;
var subFolderName;
while(folderContents.hasNext()) {
subFolder = subFolderContents.next();
subFolderName = subFolder.getName();
var subFolderCopy = targetFolder.createFolder(folderName);
copyFiles(subFolder, subFolderCopy);
}
};
I know you would like a easy, programmatic way to do this, but it may be easiest to install Google Drive for Desktop and have them right-click, copy, paste.
The idea:
Create a single folder in which the user puts every item of their Drive. (I see you have already done that.)
Share that folder with their new account. (I see you have already done that, as well.)
Sign into their new account with Drive for Desktop.
Copy the folder in Drive for Desktop and paste it right back in. Ownership gets transferred to the new account.
Just a thought.
You're going to need to store an array of folder iterators and file iterators since each folder could have a nested array of folders. If you're reusing the same folder iterator as in the accepted solution, you won't be able to resume on more top level folders.
Take a look at my answer here for a template that you can use to recursively iterate over all the files in a drive with resume functionality built-in.

setting IsEditable=false for item disables save/close button but not save button?

I developed a data extender class that acts on GetItem and CheckOutItem commands to do some business-specific validation to determine whether the user should have access to modify the item or not (basically if it's past the initial "author" task in workflow, no one can edit it. by default Tridion allows "reviewers" in workflow to edit the item, which is a no-no in our business).
I am relatively certain this worked at one point, but now does not. I'm exploring what might have changed, but I thought I'd ask here in case anyone has an idea.
If the item can't be modified, I'm setting the IsEditable attribute to false. This does in fact disable the Save and Close button and Save and New button, but for some reason the Save button is enabled. I don't quite understand why there could be a difference. (I'm looking to see if someone extended the save button somehow, but I don't see that being done). Any thoughts on how the Save button would enable when the others aren't?
thanks for any suggestions,
~Warner
public override XmlTextReader ProcessResponse(XmlTextReader reader, PipelineContext context)
{
using (new Tridion.Logging.Tracer())
{
string command = context.Parameters["command"].ToString();
if (command == CHECKOUT_COMMAND || command == GETITEM_COMMAND)
{
XmlDocument xmlDoc = ExtenderUtil.GetExtenderAsXmlDocument(reader);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
nsmgr.AddNamespace("tcm", Constants.TcmNamespace);
try
{
//is this a page or component?
XmlNode thisItemNode = null;
thisItemNode = xmlDoc.SelectSingleNode("//tcm:Component", nsmgr) ?? xmlDoc.SelectSingleNode("//tcm:Page", nsmgr);
if (thisItemNode == null) return ExtenderUtil.GetExtenderAsXmlTextReader(xmlDoc);
// need to impersonate system admin in order to get workflow version of item later
Session sessionSystemAdmin = Util.SystemAdminSession;
XmlAttribute idAttribute = thisItemNode.Attributes.GetNamedItem("ID") as XmlAttribute;
//if ID attribute is null, we don't have the actual object being used (just a referenced item. so, we'll ignore it)
if (idAttribute != null)
{
string itemId = idAttribute.Value;
VersionedItem tridionObject = Util.ObtainValidTridionIdentifiableObject(sessionSystemAdmin, itemId) as VersionedItem;
//logic has been moved to separate method, just for maintainablility...
//the logic may change when workflow code is finished.
bool allowSave = IsItemValidForEdit(tridionObject, nsmgr);
if (!allowSave)
{
//not the WIP ("author") task... make item read-only
Logger.WriteVerbose("setting iseditable to false for item: " + itemId);
XmlAttribute isEditableAttribute = thisItemNode.Attributes.GetNamedItem("IsEditable") as XmlAttribute;
isEditableAttribute.Value = "false";
}
}
}
catch (Exception e)
{
Logger.WriteError("problem with get item data extender", ErrorCode.CMS_DATAEXTENDER_GETITEM_FAILURE, e);
}
return ExtenderUtil.GetExtenderAsXmlTextReader(xmlDoc);
}
else
{
return reader;
}
}
}
Most of the Tridion GUI probably bases the options it presents on the so-called Allowed Actions. This is a combination of the Allow and Deny attributes that are present in list-calls (if requested) and item XML.
So at the very least you will have to remove the CheckIn and Edit action from the Allow attribute (and probably add them to the Deny attribute). If you look at the Core Service documentation (or any other Tridion API documentation: these values haven't changed in a long time) you can find an Enum called Actions that hold the possible actions and their corresponding values. The Allow and Deny attributes are simply additions of these numbers.
The CheckIn action I mention is number 2, Edit is 2048.
Update:
I have a little command line program to decode the AllowedActions for me. To celebrate your question, I quickly converted it into a web page that you can find here. The main work horse is below and shows both how you can decode the number and how you can manipulate it. In this case it's all subtraction, but you can just as easily add an allowed action by adding a number to it.
var AllowedActionsEnum = {
AbortAction: 134217728,
ExecuteAction: 67108864,
FinishProcessAction: 33554432,
RestartActivityAction: 16777216,
FinishActivityAction: 8388608,
StartActivityAction: 4194304,
BlueprintManagedAction: 2097152,
WorkflowManagedAction: 1048576,
PermissionManagedAction: 524288,
EnableAction: 131072,
CopyAction: 65536,
CutAction: 32768,
DeleteAction: 16384,
ViewAction: 8192,
EditAction: 2048,
SearchAction: 1024,
RePublishAction: 512,
UnPublishAction: 256,
PublishAction: 128,
UnLocalizeAction: 64,
LocalizeAction: 32,
RollbackAction: 16,
HistoryListAction: 8,
UndoCheckOutAction: 4,
CheckInAction: 2,
CheckOutAction: 1
};
function decode() {
var original = left = parseInt(prompt('Specify Allow/Deny actions'));
var msg = "";
for (var action in AllowedActionsEnum) {
if (left >= AllowedActionsEnum[action]) {
msg += '\n' + action + ' ('+AllowedActionsEnum[action]+')';
left -= AllowedActionsEnum[action];
}
}
alert(original+msg);
}
The solution is to really look over the entire solution and be absolutely positive that nobody snuck something in recently that messes with the Save button and is magically enabling it behind the scenes. I've re-edited the code to show how I initially had it. And it does work. It will disable the save, save/close, save/new buttons and make all fields disabled. I'm sorry that I wasted Frank's time. Hopefully having this here for historical purposes may come in handy for someone else with similar requirements in the future.

Javascript permission denied error when using Atalasoft DotImage

Have a real puzzler here. I'm using Atalasoft DotImage to allow the user to add some annotations to an image. When I add two annotations of the same type that contain text that have the same name, I get a javascript permission denied error in the Atalasoft's compressed js. The error is accessing the style member of a rule:
In the debugger (Visual Studio 2010 .Net 4.0) I can access
h._rule
but not
h._rule.style
What in javascript would cause permission denied when accessing a membere of an object?
Just wondering if anyone else has encountered this. I see several people using Atalasoft on SO and I even saw a response from someone with Atalasoft. And yes, I'm talking to them, but it never hurts to throw it out to the crowd. This only happens in IE8, not FireFox.
Thanks, Brian
Updates: Yes, using latest version: 9.0.2.43666
By same name (see comment below) I mean, I created default annotations and they are named so they can be added with javascript later.
// create a default annotation
TextData text = new TextData();
text.Name = "DefaultTextAnnotation";
text.Text = "Default Text Annotation:\n double-click to edit";
//text.Font = new AnnotationFont("Arial", 12f);
text.Font = new AnnotationFont(_strAnnotationFontName, _fltAnnotationFontSize);
text.Font.Bold = true;
text.FontBrush = new AnnotationBrush(Color.Black);
text.Fill = new AnnotationBrush(Color.Ivory);
text.Outline = new AnnotationPen(new AnnotationBrush(Color.White), 2);
WebAnnotationViewer1.Annotations.DefaultAnnotations.Add(text);
In javascript:
CreateAnnotation('TextData', 'DefaultTextAnnotation');
function CreateAnnotation(type, name) {
SetAnnotationModified(true);
WebAnnotationViewer1.DeselectAll();
var ann = WebAnnotationViewer1.CreateAnnotation(type, name);
WebThumbnailViewer1.Update();
}
There was a bug in an earlier version that allowed annotations to be saved with the same unique id's. This generally doesn't cause problems for any annotations except for TextAnnotations, since they use the unique id to create a CSS class for the text editor. CSS doesn't like having two or more classes defined by the same name, this is what causes the "Permission denied" error.
You can remove the unique id's from the annotations without it causing problems. I have provided a few code snippets below that demonstrate how this can be done. Calling ResetUniques() after you load the annotation data (on the server side) should make everything run smoothly.
-Dave C. from Atalasoft
protected void ResetUniques()
{
foreach (LayerAnnotation layerAnn in WebAnnotationViewer1.Annotations.Layers)
{
ResetLayer(layerAnn.Data as LayerData);
}
}
protected void ResetLayer(LayerData layer)
{
ResetUniqueID(layer);
foreach (AnnotationData data in layer.Items)
{
LayerData group = data as LayerData;
if (group != null)
{
ResetLayer(data as LayerData);
}
else
{
ResetUniqueID(data);
}
}
}
protected void ResetUniqueID(AnnotationData data)
{
data.SetExtraProperty("_atalaUniqueIndex", null);
}

Resources