Performing search queries in Active Directory with Node.js Application - node.js

The web application I'm developing involves accessing the Active Directory in order to perform the necessary authorization and authentication operations. The backend of my application involves nodeJS and it should be using Active Directory NPM package in order to access the Active Directory of my organization. I happen to be totally new to Active Directory and I'm a bit confused with the usage of Active Directory NPM package. I read the usage section of this package and it shows that it (the config object variable) requires the user to input four arguments which are as follows: url, baseDN, username & password. Below is the code in the usage section:
var ActiveDirectory = require('activedirectory');
var config = { url: 'ldap://dc.domain.com',
baseDN: 'dc=domain,dc=com',
username: 'username#domain.com',
password: 'password' }
var ad = new ActiveDirectory(config);
Out of these 4 parameters that are there in the config object, I'm not able to understand the role of the baseDN parameter and how do we have to use it when performing search queries in Active Directory. ( I have highlighted this parameter in the above image. )
It would be really great if someone could explain the usage of this particular parameter and how do we have to use it, when performing search queries in Active Directory.
Also, I was wondering if someone could refer me to a source or a tutorial that offers a clear explanation regarding the performing search queries in Active Directory with Nodejs Application. Any help would be highly appreciated. Thanks!

The concept of the base DN is not specific to just Node.js. It's the same for all LDAP queries, regardless of where you do it from.
DN stands for Distinguished Name, which is an identifier for each object in the directory. The base DN (or sometimes called "search root") defines the scope of your search. In most cases, the baseDN will be the root of your domain, like DC=example,DC=com (if your domain name is example.com). However, you can set it to a specific OU if you only want results from that OU: OU=Users,DC=example,DC=com.
In short: the search will only return results where the DN ends with the baseDN you specified.
For documentation on how performing queries in AD in Node.js, you have to refer to the packages that are created for that purpose, like the activedirectory package you found. However, that package is no longer maintained (hasn't been touched in 4 years). The activedirectory2 was forked from that and is actively maintained. You are better off using that.

We can search user using fineUser method in activeDirectory in the following way after user Authentication
var options = {
url: domaincontroller,
tlsOptions: {
ca: [fs.readFileSync(caCertRoot), fs.readFileSync(caCertIntermediate)],
rejectUnauthorized: true,
},
baseDN: "DC=domain,DC=com",
bindDN: `${req.body.username}#domain.com`,
bindCredentials: `${req.body.password}`,
filter: "(objectClass=*)",
attributes: { user: ["givenName", "c", "co"], group: ["com"] },
};
/* Search Functionality */
const ad = new ActiveDirectory(Object.assign(config, options));
ad.findUser(req.body.username, function (err, user) {
if (err) {
res.writeHeader(401, { "Content-Type": "text/html" });
res.write(
`<h3>Username or Password is not valid, please try again!</h3>`
);
res.send();
return;
} else {
res.send({ msg: "user logged successfully", ...user });
}
});

Related

How to transfer ownership of file create with service account using Drive API v3

I'm using the googleapis package in npmjs to create a spreadsheet as follows:
const { google } = require("googleapis")
const sheets = google.sheets("v4")
const drive = google.drive("v3")
async function run() {
// ....
// auth obtained here
// ....
var resp = await sheets.spreadsheets.create({
auth,
resource: {
properties: {
title:"SSTEST2",
}
}
})
var folder = "1XXXXXXXXXXXXXXXXXXXXX" // actual folder id omitted
var ssid = resp.data.spreadsheetId
resp = await drive.files.update({
auth,
addParents:[folder],
removeParents:"root",
fileId:ssid
})
}
I'm using a service account I created, so as a result when the process is complete, the spreadsheet is owned by googlesheetsuser#some-random-words.iam.gserviceaccount.com. This doesn't seem to prevent me from editing the document, however if I delete it, presumably it is still sitting off in limbo somewhere (if I try to access it before moving it to my folder I get a page telling me to request access, which doesn't make any sense because a service account address isn't a real e-mail and so there's no way for this to succeed!) so I'm not sure if it's even going into a trash can for the service account or if it will sit around on Google's servers forever. Not actually owning the document causes issues so I need to get ownership.
On the other hand, I haven't found any documentation that explains exactly what I need to do to transfer ownership to myself. Google's API seem to leave out a lot of information about exactly what to pass; leaving out important info in the sample source with comments like "TODO: Add desired properties to the request body" is beyond unhelpful! I have found a few examples in other languages using other mechanisms I don't recognize which purport to do at least something close to this, but I haven't been able to glean any useful information about how to do it in my particular setup. I have also found a couple of answers which seem to imply that doing this is impossible as you can't change ownership between domains, in which case I just have no words because obviously leaving it owned by my service account creates issues and leaving it owned by the service account indefinitely just isn't an option so some solution is needed.
How can I wind up with my file (Google Spreadsheet) owned by myself at the end of this process?
Update: Per the suggested by #Kessy, I tried to use this code:
const resp = await drive.permissions.update({
auth,
fileId: <fileId>,
permissionId: <id found using list call>,
transferOwnership: true,
requestBody: {
role: "owner",
emailAddress: <my email address>,
type: "user",
}
})
I get this error:
Error: The resource body includes fields which are not directly writable.
I don't know which field it thinks isn't writeable. I tried omitting "type" and get the same error. I tried omitting "role" and it complains that this field is required: Error: The permission role field is required. If I omit "emailAddress" then I don't get the error, but this defeats who whole purpose of the call, which is to transfer ownership to that e-mail address!

Google Search Console API: How do I Solve "User does not have sufficient permission for site"? (When User Has Permissions)

I'm trying to use Google's Search Console API via their Node package, and my code looks like the following:
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/webmasters.readonly',
});
const webmasters = google.webmasters('v3');
const params = {
auth,
siteUrl: 'example.com',
resource: {
startDate: '2015-08-25',
endDate: '2015-08-25',
dimensions: ['query', 'page'],
rowLimit: 10,
},
aggregationType: 'byPage',
};
const res = await webmasters.searchanalytics.query(params);
console.log(res.data);
... except that in my version example.com has been replaced with my actual domain.
I'm invoking it at the command line with:
GOOGLE_APPLICATION_CREDENTIALS="/path/to/service_key.json" node index.js
I have a service account created, and the service key JSON file is from it. That service account has access to my Search Console account. When I look at https://search.google.com/search-console/users I see the service user there, and in the Permission column it has "Full".
Can anyone help me understand why I'm getting the following error when I run my code?
{
message: "User does not have sufficient permission for site 'http://example.com'. See also: https://support.google.com/webmasters/answer/2451999.",
domain: 'global',
reason: 'forbidden'
}
The URL mentioned, https://support.google.com/webmasters/answer/2451999, simply links me to the search console users page ... which (again) says the service user has full permissions.
After rooting in google forums and rest of internet I have figured out why it happen.
Need to copy Service account email, long weird format(example: whiskey-tango-foxtrot#certain-something-0123456.iam.gserviceaccount.com) from Google Cloud: https://console.cloud.google.com/apis/credentials to Search Console https://search.google.com/u/1/search-console/users, as a "Full site" user.
Make sure you have added that site(as from on Search Console) via your API or with this tool: https://developers.google.com/webmaster-tools/v1/sites/add
Then, when you perform "list_sites" request, your site should be listed ere and permission_level is "siteFullUser"(according to step 1)
When you add_site or perform query API requests, make sure to set siteUrl according to steps above, eg: http://www.example.com/ (for a URL-prefix property) or sc-domain:example.com (for a Domain property)
Happy coding

Firestore Security Rules: How to enforce the number of fields CREATED in a Firestore document thru security rules? [duplicate]

This question already has an answer here:
How to get Firestore rules working with service accounts
(1 answer)
Closed 3 years ago.
I am unable to restrict the number of fields in a created document using the Firestore Security Rules.
https://firebase.google.com/docs/firestore/security/rules-structure
I have 7 fields in my user document. I would like to restrict any document CREATE attempts to the number of fields that I expect. I have set the number to 100 which clearly is not what I am sending over to intentionally create a fail. I would expect 100 to fail but it does not.
match /users/{uid} {
// Applies to writes to nonexistent documents
allow create: if request.resource.data.size() == 100;
}
I have also tried the following by placing the size property directly onto the resource object with no success.
allow create: if request.resource.size() == 100;
Here is the code that attempts the CREATE operation;
const DEFAULT_systemRole = 'User';
const DEFAULT_disabled = false;
// creates up a static sentinel value to be set on the Firestore server
var serverTimestamp = admin.firestore.FieldValue.serverTimestamp()
admin.firestore().collection('users').doc(req.body.uid).set({
// sent from client
usernameSlug: req.body.usernameSlug,
username: req.body.username,
email: req.body.email,
// set only here on server
systemRole: DEFAULT_systemRole,
disabled: DEFAULT_disabled,
dateModified: serverTimestamp,
dateCreated: serverTimestamp,
}).then(function() {
console.log("Successfully created FireStore user");
}).catch(function(error) {
console.log("Error creating user:", error);
throw(error)
})
I just tested this quickly in the simulator, and it seems to work for me there:
So the above security rules require 2 fields.
When I write a new document with only one field (in the screenshot), the write fails.
When I change the rules to require only one field, the same write succeeds.
It appears that by design the Firebase Admin sdk, which I happen to be using, does not support Firebase Security Rules. Instead, the Firebase Admin sdk BYPASSES all security Rules. So it looks like I will move all my calls to the Firebase Admin sdk and simply lock down the Firestore db with the following rule to prevent any possibility for client-side hacking.
match /users/{uid} {
allow read, write: if false;
}
The following post also found the same;
How to get Firestore rules working with service accounts

Is there a library compatible with Hapi for fine-grained ACL / User permissions?

Looking to use HapiJS as our API server. We need fine-grained user permissions, e.g. "User A can edit field B" "User C can view field D" for a given model / resource.
Before we start building something I've been looking to see if something like this has already been done that is compatible with Hapi.
I have just read an article where the ACL permissions are validated using the build-in scopes.
Here is the link to the mentioned article :
https://blog.andyet.com/2015/06/16/harnessing-hapi-scopes/
And to resume quickly (using the example from the above link), you get a user object that looks like so :
{
"username": "han",
"scope": ["door-trash-compactor"]
}
The scope can be generated by whatever is backing your ACL for this user. In this case you have the resource door with id trash-compactor that can be checked like so :
server.route({
method: 'GET',
route: '/doors/{door_id}',
config: {
handler: function (request, reply) {
reply(request.params.door_id ' door is closed');
},
auth: {
scope: ['door-{params.door_id}']
}
}
});
The scope door-{params.door_id} will be translated to door-trash-compactor which will then be validated. Han's request to the trash compactor door will be valid and he will get the door is closed message.
The blog post is well written (much better then this summary) and describes this in better detail - would recommend the read.
I've recently been working on an ACL project for hapijs. It should get you a good start. https://www.npmjs.org/package/hapi-authorization

Pulling user's name and photo with Passport.js

I am developing a node.js app and recently figured out how to authenticate via GitHub. I want to know if there is a way to pull a user's profile photo and name so I can display it elsewhere in the app. I found this question, but it looks like Facebook and Google use different methods of obtaining these things.
Can someone point me in the right direction?
Passport module developers usually normalize the user profile object so some standard fields are available in the same way across all modules in this way:
profile.id - assigned by the provider
profile.username - their 'login'
profile.displayName - full name of the user
profile.profileUrl - user's profile page on the provider's site
profile.emails - array of email addresses, but usually just a single address
Thus, you already have the username and/or displayname. One down!
These cover what is consistently needed, but providers can return other information. This naturally depends upon (a) what kind of services the provider offers and (b) the requested scopes.
The provider-specific info isn't lost. It is stored in profile._json next to the normalized properties...and inspecting this will show what else you have to work with.
For Github profile photos/images, you'll be looking for a gravatar_id property to get the user's photo URL.
You can confirm this, using the boilerplate example from passport-github for illustration purposes only, you could do something like this to see what Github is handing back to you so you can evaluate the entire provider response:
passport.use(new GitHubStrategy({
// authentication stuff here, per `passport-github` docs
},
function(accessToken, refreshToken, profile, done) {
// skipping error handling &c for brevity...
fs.writeFile("githubProfile.json", JSON.stringify(profile));
return done(null, profile);
});
}
));
If correct, you could then grab the user photo URL from profile._json.gravatar for use in whatever way you need (saving to database, inserting into a profile page, etc).

Resources