Setting the ACL when creating a new database via JS in XPages - xpages

I am creating a new database using JS from an action button. The following sequence happens:
When the database is created the -default- access is set to Manager which it has to be.
I then create several aclEntries. The first entry created for the current user and set them as a type person and having Manager rights
I then create several other entries with varying right and save the ACL.
If I open the Db the ACL looks and acts correctly except the -default- has an access level of Manager.
So I tried change my code to set the access level for -default- to Author in various ways, all resulting in an exception:
Change -default- to Author and save
First save the ACL as above, then change the -default- entry and save
create the ACL object newly using var newACL = db.getACL(), change and try to save
It seems that it does not recognize me as a manger in the ACL.
Q: How do I change the -default- access away from manager?

Probably the easiest way to get a good default ACL is using a template database. All entries in your template that come in square brackets are copied into the new database as ACL entries.
So in your template you would have
[-Default-]=Author and [Anonymous]=No Access which results in
-Default-=Author and Anonymous=No Access in the new DB
Update
Easier that it looks. You need to make sure to get the entries right...
Use this function:
function makeDB(dbName) {
var server = database.getServer();
var you = #UserName();
var dbDir = session.getDbDirectory(server);
var db = dbDir.createDatabase(dbName);
var acl = db.getACL();
acl.createACLEntry(you,6);
if (server != "" && server != you) {
acl.createACLEntry(server,6);
}
var def = acl.getEntry("-Default-");
def.setLevel(3);
acl.save();
}
Then you call the function using:
makeDB("someFancyDBName");
In the function we make sure that you, who runs the script and the server where it runs are both in the ACL as managers (including the case of a local database, where in the Notes client the server is empty and in the web preview would be your user name).
Works like a charm. If it doesn't work for you, there are a few things to check:
Does it work on local (no server involved)?
Do you have errors on the console?
What access level do you have in the server ECL
Check the "max Internet access" of the database
The previous answer (obsolete):
Other than that it is a little trickier...
You create the new database:
var db:Database = [... whatever you have todo here ...];
var acl:Acl = db.getAcl();
// Do whatever you do, don't touch -Default-
acl.save();
acl.recycle();
var dbURL = db.getUrl(); // <-- off my head might be slightly different name
db.recycle();
// Now the real work
var db2 = session.evaluate(dbUrl);
var acl2 = db2.getAcl();
// Change default here;
Typed off my head, contains typos.
This should work. Let us know how it goes

Related

Tracking currently active users in node.js

I am building an application using node.js and socket.io. I would like to create a table of users who are actively browsing the site at any given moment, which will update dynamically.
I am setting a cookie to give each browser a unique ID, and have a mysql database of all users (whether online or not); however, I'm not sure how best to use these two pieces of information to determine who is, and who isn't, actively browsing right now.
The simplest way would seem to be to store the cookie & socket IDs in an array, but I have read that global variables (which presumably this would have to be) are generally bad, and to be avoided.
Alternatively I could create a new database table, where IDs are inserted and deleted when a socket connects/disconnects; but I'm not sure whether this would be overkill.
Is one of these methods any better than the other, or is there a way of tracking this information which I haven't thought of yet?
You can keep track of active users in memory without it being a global variable. It can simply be a module level variable. This is one of the advantages of the nodejs module system.
The reasons to put it in a database instead of memory are:
You have multiple servers so you need a centralized place to put the data
You want the data stored persistently so if the server is restarted (normally or abnormally) you will have the recent data
The reasons for not putting it directly in a database:
It's a significant load of new database operations since you have to update the data on every single incoming request.
You can sometimes get the persistence without directly using a database by logging the access to a log file and then running chron jobs that parse the logs and do bulk addition of data to the database. This has a downside in that it's not as easy to query live data (since the most recent data is sitting in databases and hasn't been parsed yet).
For an in-memory store, you could do something like this:
// middleware that keeps track of user access
let userAccessMap = new Map();
app.use((req, res, next) => {
// get userId from the cookie (substitute your own cookie logic here)
let id = id: req.cookie.userID;
let lastAccess = Date.now();
// if you want to keep track of more than just lastAccess,
// you can store an object of data here instead of just the lastAccess time
// To update it, you would get the previous object, update some properties
// in it, and then set it back in the userAccessMap
userAccessMap.set(id, lastAccess);
next();
});
// routinely clean up the userAccessMap to remove old access times
// so it doesn't just grow forever
const cleanupFrequency = 30 * 60 * 1000; // run cleanup every 30 minutes
const cleanupTarget = 24 * 60 * 60 * 1000; // clean out users who haven't been here in the last day
setInterval(() => {
let now = Date.now();
for (let [id, lastAccess] of userAccessMap.entries()) {
if (now - lastAccess > cleanupTarget) {
// delete users who haven't been here in a long time
userAccessMap.delete(id);
}
}
}, cleanupFrequncy);
// Then, create some sort of adminstrative interface (probably with some sort of access protection)
// that gives you access to the user access info
// This might even be available in a separate web server on a separate port that isn't open to the general publoic
app.get("/userAccessData", (req, res) => {
// perhaps convert this to a human readable user name by looking up the user id
// also may want to sort the data by recentAccess
res.json(Array.from(userAccessMap));
});

Parse CLP / ACL causing issues with beforeSave trigger

thanks for stopping by.
I have a class, let's call Class1, that I use to give users of my app discounts / coupons. When a user finishes the registration process, I create an object and store the pointer to it on the user.
I want to give this class CLP permissions so that the public can create, but not write to this object. Let it only be something that I use internally.
My problem is that the class has several arrays that can't be undefined, or other cloud functions will end up throwing errors trying to read those values. I set up a beforeSave() trigger for the class, and use the master key, but the object isn't being saved so my entire user save at the end of registration isn't working. How do I fix this while keeping my object secure and making sure users can't steal all of my app's services for free?
Here is my beforeSave trigger:
Parse.Cloud.beforeSave("Class1", function(request, response)
{
Parse.Cloud.useMasterKey();
var emptyArray = [];
class1 = request.object;
if( class1.isNew() )
{
class1.set("array1", emptyArray);
class1.set("array2", emptyArray);
class1.set("array3", emptyArray);
}
response.success();
});
So I guess you can't use the master key to override CLP / ACL stuff in a beforeSave trigger... though it makes sense, as that would entirely defeat the point.
However, if you give public create access, you can populate any initial data, you just can't update it without update access. So, the obvious answer was to just set these parameters on the client side when I created the object and before I saved it.

Xpages SSJS Create Administration Process

Has anyone been able to get SSJS CreateAdministrationProcess to work? I have searched for functioning code but was not able to find any.
I am trying to create an adminP request in SSJS to set a users password. I can't use the ?changepassword in the url method because we do not allow web users access to the NAB.
I am using OAUTH and when I try to hash and update the password directly to the NAB it without an adminp request, it creates problems with the current client session, logging them out and then locking them out.
I assume this is because I changed the credential tokens on the server but not on the client and when it realizes this it thinks I'm trying to authenticate over and over and locks me out.
If I can't get the SSJS to work I am going to write it in a lotusscript agent and call the agent from SSJS, but for posterity sake I wanted to get AdminP requests to work from SSJS directly.
Here is my code:
var hashednew = session.hashPassword(thenewpw)
nabDoc.replaceItemValue("HTTPPassword",hashednew)
var dt:NotesDateTime = session.createDateTime("Today 12");
nabDoc.replaceItemValue("HTTPPasswordChangeDate",dt)
dt.recycle()
var nabServerAccessView:NotesView = nabDB.getView("($ServerAccess)")
nabDB.DelayUpdates = false;
var AdminP=sessionAsSigner.CreateAdministrationProcess("abcServerName/Co")
var AdminPNoteId=AdminP.SetUserPasswordSettings(#Name("[ABBREVIATE]" ,#UserName()), 0, 0, 0, True)
nabDoc.save(true,true)
nabServerAccessView.refresh()
It is crashing at the line:
var AdminP=sessionAsSigner.CreateAdministrationProcess("abcServerName/Co")
and the server error is:
Error calling method 'CreateAdministrationProcess(string)' on an object of type 'lotus.domino.local.Session [Static Java Interface Wrapper, lotus.domino.local.Session: lotus.d
AS A FOLLOWUP,
The original code I posted had more than the uppercase/lowercase issue, in practice. I was able to get it to work, but the way I was updating to the NAB directly was wrong. I found a better way to do the password change using SSJS with the following snippet, and it's pretty simple. Of course you have to validate the old password and complexity of the new password first, but once you've done that you can run the following:
try {
var AdminP=sessionAsSignerWithFullAccess.createAdministrationProcess(server)
var chgPW=AdminP.changeHTTPPassword(theuser,theoldpw,thenewpw)
} catch(e) {print("AdminProcess configure error: " + e)}
In my opinion the problem is in naming convention - Java methods start with lower case letters.
var AdminP=sessionAsSigner.createAdministrationProcess("abcServerName/Co")
var AdminPNoteId=AdminP.setUserPasswordSettings(#Name("[ABBREVIATE]" ,#UserName()), 0, 0, 0, True)
Please check your ACL settings: Is "Maximum internet name and password" set to "Manager" or "Designer"?

My program turns a spreadsheet into an Excel-file. But it only works for one user

I've made a larger (1000+ lines of code) App Script. It works great, for me. No other user can run it. I want them to be able to run it as well, and I can not figure out why they can't.
The problem occurs on this part:
var id = 'A_correct_ID_of_a_Google_Spreadsheet';
var SSurl = 'https://docs.google.com/feeds/';
var doc = UrlFetchApp.fetch(SSurl+'download/spreadsheets/Export?key='+id+'&exportFormat=xls',googleOAuth_('docs',SSurl)).getBlob();
var spreadsheet = DocsList.createFile(doc);
The function (and structure) was published here: other thread
function googleOAuth_(name,scope) {
var oAuthConfig = UrlFetchApp.addOAuthService(name);
oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oAuthConfig.setConsumerKey('anonymous');
oAuthConfig.setConsumerSecret('anonymous');
return {oAuthServiceName:name, oAuthUseToken:"always"};
}
I can't see any reason why the program only would run for one user. All the files are shared between all the users and ownership have been swapped around.
When a script uses oAuth (googleOAuth_(name,scope) in you case) it needs to be authorized from the script editor, independently from the other authorization that the user grands with the "normal" usual procedure.
This has been the object of an enhancement request for quite a long time and has no valid workaround as far as I know.
So, depending on how your script is deployed (in a SS or a Doc or as webApp) you might find a solution or not... if, as suggested in the first comment on your post, you run this from a webapp you can deploy it to run as yourself and allow anonymous access and it will work easily, but in every other case your other users will have to open the script editor and run a function that triggers the oAuth authorization process.

How do you get the filename from the XPages FileUpload Control

In XPages, in the file upload control, after a user selects a file but before it's saved how can you get the filename? I'm not interested in the path as I believe that's not getable due to security issues but I would like to get the filename and extension if at all possible.
Thanks!
Actually you can get the file and fully manipulate it, read it, do whatever you want with it, its stored in the xsp folder on the server, to which you have read/write access... here is a code snippet that interacts with the file, I usually call from beforeRenderResponse...
var fileData:com.ibm.xsp.http.UploadedFile = facesContext.getExternalContext().getRequest().getParameterMap().get(getClientId('<INSERT ID OF UPLOAD CONTROL HERE (ie. fileUpload1)>'));
if (fileData != null) {
var tempFile:java.io.File = fileData.getServerFile();
// Get the path
var filePath:String = tempFile.getParentFile().getAbsolutePath();
// Get file Name
var fileName:String = tempFile.getParentFile().getName();
// Get the Name of the file as it appeared on the client machine - the name on the server will NOT be the same
var clientFileName:String = fileData.getClientFileName();
}
It sounds like you are referring to needing to get the data via CSJS, which you can do with the following code:
var filename = dojo.byId('#{id:fileUpload1}').value.split('\\').pop();
These links should be able to help you.
http://www.bleedyellow.com/blogs/andyc/entry/intercepting_a_file_upload4?lang=en
http://www.bleedyellow.com/blogs/m.leusink/entry/processing_files_uploaded_to_an_xpage?lang=en

Resources