Query Google Admin Directory users for partial name match - google-apps-script

I'm trying to do two things:
Query AdminDirectory.Users.list by name field to return a list of anyone whose name matches a given string (even partially)
If possible, prevent the email address field from being included in the search
For example, "donald" should return data for the users Donald Duck, Donald Trump, and Ronald McDonald. If someone searched for "onald", that should work too.
The below sort of works. In the "donald" scenario, it would return only Donald Duck's data. For some reason it won't return more than one user.
function processForm(formObject) { // formObject comes from form on front end
var textSearchObject = formObject.textSearch; // "donald"
var userList = AdminDirectory.Users.list({
domain: 'somedomain.com',
query: "name:'" + textSearchObject + "'",
viewType: 'domain_public',
projection: 'full'
}).users;
return userList;
}
I know, query: "name:'" + textSearchObject + "'" looks very strange and most people would just use query: textSearchObject. The problem is that this searches email addresses - I need to avoid this if possible.

You're limited to "starts with" searching e.g. Donald* but you can search just the givenName, familyName or the combined name (fullName). Full documentation is here: https://developers.google.com/admin-sdk/directory/v1/guides/search-users

Related

.Net Core 3.1 Linq to Entities - How to concat strings in where clause

I have a user table with [FirstName] and [LastName] columns.
I'm trying to build a search function that returns users that meet one of the criteria below:
FirstName == myPattern, or
LastName == myPattern, or
FirstName LastName == myPattern
For example, if I have the following users in my database:
Jack One
Jack Two
Jack Three
I'd like the function to return all of them when the input is Jack, but only return Jack One when the input is Jack One
I currently have the following code:
var users = context.User.Where(x => x.FirstName == pattern
|| x.LastName == pattern
|| x.FirstName + " " + x.LastName == pattern)
But this does not work as the it gets translated to the following query in MySQL
...WHERE (`p`.`firstName` = 'Jack One') OR (`p`.`lastName` = 'Jack One')) OR (((`p`.`firstName` + ' ') + `p`.`lastName`) = 'Jack One')
It does not work because I believe we need to use CONCAT(firstName, ' ', lastName) if I want to concat multiple strings in MySQL.
I tried using the following .NET functions but they cannot be translated to sql (The LINQ expression ... could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync())
string.Join(' ', firstName, lastName)
string.Concat(firstName, " ", lastName)
How can I achieve this in .NET CORE 3.1 without pulling all data into memory and evaluating it in client?
Thanks
This looks like an case of Linq translating the query in a manner you aren't predicting.
Going from memory and no IDE on hand to check it, but give this a shot. If you split the full name on the space first, you can use the values in your query.
// returns an array based on the patter, if it can be split
var names = pattern.Split(" ");
// then use the array elements in the query
var users = context.User.Where(x => x.FirstName == pattern
|| x.LastName == pattern
|| (x.FirstName == names[0] && x.LastName == names[1]));
The last OR condition of the query should then evaulate the 2 new elements in the names array, that was created off the pattern
It seems a bug of MySql.Data.EntityFrameworkCore.
I use Pomelo.EntityFrameworkCore.MySql instead to solve the problem.

How can I escape an apostrophe to match data from a MySQL database in Node.js/Discord.js?

I know how escaping works in general, but in this case, I cannot get it to give me the desired results.
I have a table with a list of cards in it. At the top of the command, I have the query push each card name into an array as a "master list." Later on in the code, I have the users provide card names they would like to add to their deck. In order to check that they are using cards that exist, exactly as spelled, it takes each card name the user provides and tries to find it in the master list array.
However, when the user provides a card name with an apostrophe in it (i.e. Drago's Fury), it refuses to find the card name in the list.
I have tried replacing all occurrences of ' with \' and '' and several combinations of the two, multiple \'s and multiple ' 's in various amounts. Nothing seems to let me match it. Drago's Fury in the table does contain the 's in the name as expected, because I was able to put it into the table by escaping it with \'.
Here's the code I have:
con.query(`SELECT * FROM cardsmaster`, (error, rows, fields) => {
let cardsList = []
for (var i in rows) {
cardsList.push(rows[i].name)
}
});
Here is how Drago's Fury is listed in the MySQL table:
This is what I'm using right now to try and escape it and make it find Drago's Fury when it is provided inside Discord from a user:
cardsToAdd = cardsToAdd.content.replace(/'/gi, "\'");
This is how cardsToAdd displays Drago's Fury when it's logged after the replacement:
But then I receive this error:
Error: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 's Fury, Fierce Boost, Fierce Boost, Fiery Rage, Fire Boost, Fire Boost, Magma Bo' at line 1
So, I tried:
cardsToAdd = cardsToAdd.content.replace(/'/gi, "\\'");
I was thinking that the first \ would escape the second \ and the apostrophe, but that isn't exactly what I want. The incoming value is the same, except that it has double quotes instead of single, so I'm not certain if that is causing the problem or not...
This is the section that tries to take what the user gives and finds it in the master list:
let usercardnamearray = cardsToAdd.split(", ")
let matchedcards = []
let unmatchedcards = []
for (let i = 0; i < usercardnamearray.length; i++) {
console.log(usercardnamearray[i])
let found = cardsList.indexOf(`${usercardnamearray[i]}`)
console.log(found)
if (found >= 0) {
matchedcards.push(usercardnamearray[i])
} else if (found == -1){
unmatchedcards.push(usercardnamearray[i])
}
}
How can I consistently match the provided name in the master list?
As explained in the comments, by using placeholders (?), the input is automatically escaped for you; the node-mysql driver uses mysql.escape() for them on its own. This will prevent injection and your current error, without you having to do anything else yourself. Your apostrophe won't trigger the same error since it's being read literally rather than as part of the statement.
Example usage:
const name = "Drago's Fury";
con.query("SELECT * FROM cardmaster WHERE name = ?", [name], (err, rows) => {
if (err) return console.error(err);
console.log(rows);
})

How is 'Escaping query values' safe in sql? (Or why is it dangerous?) [SQL injection]

I'm following Node.js with sql examples on W3schools. here
It said the following code prevents SQL injections.
var adr = 'Mountain 21';
var sql = 'SELECT * FROM customers WHERE address = ' + mysql.escape(adr);
con.query(sql, function (err, result) {
if (err) throw err;
console.log(result);
});
When query values are variables provided by the user, you should escape the values.This is to prevent SQL injections, which is a common web hacking technique to destroy or misuse your database.
This was the explanation.
I want to understand how this is safe. (how this prevents SQL injections).
Also, how is the following code dangerous?
var sql = 'SELECT * FROM customers WHERE address = "Mountain 21"';
Unprotected string concatenation to generate a SQL statement is dangerous if the injected value (i.e. "Mountain 21") is source from an uncontrolled external source. For example, it is entered by a user.
Consider a plain string concatenation as follows:
var adr = <something accepted from an external source>
var sql = `SELECT * FROM customers WHERE address = "${adr}"`;
Then consider what might happen if the user entered the following into the text field:
Mountain 21"; delete all from customers; //
The query would become:
SELECT * FROM customers WHERE address = "Mountain 21"; delete all from customers; //"
If you ran that, you would probably end up with no customers in your table.
I am not personally familiar with the operation of the node.js mysql.escape function, but typically these sorts of functions "escape" special characters so they lose their "special-ness". For example, it might put a \ in front of the ; to remove it's significance as a statement separator.
Another more common example of what the escape function will typically do is convert a piece of text such as "O'Brien" to "O''Brien" (two single quotes is the way to specify a single quote in an SQL text string). A query that uses the "O'Brien" name would look something like this:
select *
from customers
where name = 'O''Brien';
The mySql.escape function will almost certainly provide the necessary conversion of "O'Brien" into "O''Brien" so that it can properly be run in an SQL query. Without the escape, the last line of the query would read:
where name = 'O'Brien';
which would result in a syntax error.
FWIW, The safest way is to use ? placeholders in your query for user supplied values (e.g. the address). This is a bit more cumbersome as you need to prepare your query, supply all of the values and then execute it. However, the benefit is that this is (should be?) completely immune to most, if not all, forms of "injection attack".
The basic flow for a parameterised query as per your example is (in java'ish pseudocode - as I don't about node.js's capabilities in this area) is:
val sql = "SELECT * FROM customers WHERE address = ?";
val preparedStatement = conn.prepareStatement(sql);
preparedStatement.setString (1, adr);
val resultSet = preparedStatement.executeQuery();
Most if not all databases support parameterised queries, most languages expose this capability, but not all do expose it (or at least not easily). Again, I'm not sure about node.js.
I hope this helps you.
var adr = 'Mountain 21';
var sql = `SELECT * FROM customers WHERE address = "${mysql.escape(adr)}"`;
con.query(sql, function (err, result) {
if (err) throw err;
console.log(result);
});
var sql = 'SELECT * FROM customers WHERE address = Mountain 21';
To
var sql = 'SELECT * FROM customers WHERE address = "Mountain 21"';
https://stackoverflow.com/a/33679883/11343720
The Grave accent is better quote, double quote

How get the current user's own email address or check entered, using Exchange Web Services (EWS)?

I have to work with external Exchange server. How can I get the own email address or check address entered by the user (that he introduced exactly own address), using EWS?
Email address is not the same as username.
The best solution at this moment.
You can use ConvertId with a generic address and Exchange will then return the PrimarySMTP for that mailbox eg.
Folder Inbox = Folder.Bind(service, WellKnownFolderName.Inbox);
AlternateId aiAlternateid = new AlternateId(IdFormat.EwsId, Inbox.Id.UniqueId, "mailbox#domain.com");
AlternateIdBase aiResponse = service.ConvertId(aiAlternateid, IdFormat.EwsId);
Console.WriteLine(((AlternateId)aiResponse).Mailbox);
You might have some luck with the method ResolveName. Using this method you can search the Global Address List for the user. And by using a simple if else to see if any results were returned. This method does resolve ambiguous names so be sure to check the result carefully
Example:
NameResolutionCollection coll = service.ResolveName("Johnson", folders, ResolveNameSearchLocation.DirectoryOnly, false);
foreach (NameResolution nameRes in coll)
{
Console.WriteLine("Contact name: " + nameRes.Mailbox.Name);
Console.WriteLine("Contact e-mail address: " + nameRes.Mailbox.Address);
Console.WriteLine("Mailbox type: " + nameRes.Mailbox.MailboxType);
}
If you want to read more about it: https://msdn.microsoft.com/en-us/library/microsoft.exchange.webservices.data.exchangeservice.resolvename(v=exchg.80).aspx
Based on iCode4U's Answer, if your service uses default credentials (from the logged user), then this might get what you need:
String address = service.ResolveName(Environment.UserName)(0).Mailbox.Address;
EDIT: If one can't trust the uniqueness of the results brought by the query above, then it is better to use something like this (this would work in my organization, where usernames are also email identifiers, but each one must tweak it to fit his own scenario):
string address = Service.ResolveName(Environment.UserName).Select(
a => a.Mailbox.Address).FirstOrDefault(
b => b.StartsWith(Environment.UserName + "#",
StringComparison.InvariantCultureIgnoreCase));
We use this function loaded in the user PowerShell profile.
Function CurrentUserPrimarySmtpAddress()
{
<#
.SYSNOPSIS
Attempt to retrieve the current user's primary SMTP address.
.DESCRIPTION
Attempt to retrieve the current user's primary SMTP address.
.NOTES
Author: David Barrett
Date Created: 08NOV2016
.LINK
https://gallery.technet.microsoft.com/office/PowerShellEWS-Update-items-48c3dcfc
.EXAMPLE
$PrimarySmtpAddress = CurrentUserPrimarySmtpAddress
#>
$searcher = [adsisearcher]"(samaccountname=$env:USERNAME)"
$result = $searcher.FindOne()
if ($result -ne $null)
{
return $result.Properties["mail"]
}
return $null
}

Keeping code modular when accessing a database

I've been working on the database for a very basic social networking site: all you can do is register for an account and send/accept friend requests. The server is written in Javascript (NodeJS)
I have a method getUser(username) that accesses the database to get the JSON object representing a particular user given their username, and a method to list the friends of a particular user. From the listFriends(username) function, I want to return an array of the user objects rather than just their usernames and ideally I would like to utilize my own get() function rather than altering the SQL query that listFriends() uses
It might be easier to understand with an example. If I have three tables:
TABLE: UsersName
username (unique) | firstName | lastName |
------------------|-----------|------------|
pjfry | Phillip | Fry |
proff | Professor | Farnsworth |
bender | Bender | Rodriguez |
TABLE: UsersProfile (their profile description)
username (unique) | description |
------------------|------------------------|
pjfry | I went to the future |
proff | I am very old |
bender | Destroy all humans |
TABLE: Friendships (for simplicity assume that if (a,b) is an entry then so is (b,a))
user1 | user2
-----------|---------------
bender | pjfry
pjfry | bender
pjfry | proff
proff | pjfry
And a function to get the user object:
//the callback accepts the user object
function get (username, callback) {
db.query(
'select * from (UsersName n, UsersProfile p) where n.username=p.username and n.username=\'' + username + '\'',
function (rows) {
//for simplicity assume that this call always succeeds
//the user object is altered a bit:
callback({
username: rows[0].username,
name: {
first: rows[0].firstName,
last: rows[0].lastName,
full: rows[0].firstName + ' ' + rows[0].lastName
},
profile: {
description: rows[0].description
}
});
}
}
And here is the function to list the friends of a given user
//callback accepts the array of friends
function listFriends(username, callback) {
db.query(
'select user2 from Friendships where user1=\'' + username + '\'',
function(rows) {
//assume this call always succeeds
callback(rows)
}
)
}
The problem here is that listFriends() will just return the array of usernames rather than user objects. How could I modify the listFriends() function so it returns the user objects by utilizing the get() function?
It could be done by modifying the SQL statement in listFriends() but it would be much cleaner to use the get() method so that if the structure of the user object is ever changed, it only needs to be changed in one place.
Trying to keep things as DRY as possible, something like this should fit your requirements:
// Disclaimer: I have not run tested this code myself
//the callback accepts the user object
function get(username, callback) {
getFriendsWithConditions('n.username=\'' + username + '\'', function(rows){
// returns only a single object to the input callback
callback(rows[0]);
});
}
//callback accepts the array of friends
function listFriends(username, callback) {
getFriendsWithConditions('n.username in (select user2 from Friendships where user1=\'' + username + '\')', callback);
}
function getFriendsWithConditions(whereCondition, callback) {
db.query(
'select * from (UsersName n, UsersProfile p) where n.username=p.username and (' + whereCondition + ')',
function(rows) {
// Using ECMAScript 5 Array.map
callback(rows.map(rowToUserObject));
}
);
}
function rowToUserObject(row)
{
return {
username: row.username,
name: {
first: row.firstName,
last: row.lastName,
full: row.firstName + ' ' + row.lastName
},
profile: {
description: row.description
}
};
}
A few things that came to mind as I was writing this:
UserName and UserProfile feel like they should only be one table, but for all I know this is a simplified version of your actual database, so I left them as is.
I used Array.map as defined in ECMAScript 5 which should be available in Node.
The SQL alias names for each table are hardcoded throughout each function, meaning that you would also need to a) set some sort of constant or come of with a convention to keep all aliases consistent throughout all your functions (which can be a pain to maintain as the project grows) or b) look into using an ORM that can do these things for you (and more!) This will help you avoid sweating the details of how to construct queries, leaving you to worry about more important things, like what data you actually need. I'm not terribly familiar with what's available for NodeJS in terms of ORMs, but the official NodeJS wiki should be a good place start.
Might it be better not to destroy the existing functionality of listFriends()?
Instead, my thought would be to create a new function called getFriends() which calls listFriends() and for each returned username, calls get() for it. The concatenated series of results from each get() would serve as the return for getFriends().
This is a fairly simple solution that does what I want in 2 network calls (rather than 1, like I had originally hoped to do).
I added a function getUserArray(usernames) that takes an array of usernames and returns the objects from that. It does this by generating SQL that looks like: "SELECT ... FROM (UsersName n, UsersProfile p) WHERE n.username=p.username and n.username in (?, ?, ?, ...) order by field(username, ?, ?, ?, ...)" and passes the array of usernames as the values to be escaped and replaced with the ?'s.
The functions getUserArray() and getUser() share a lot in common, so common parts of the code for those can easily be put into common helper functions.
The functionality of listFriends() is augmented as follows: just before it would have returned the array of usernames, it calls getUserArray() to map the usernames to objects.
Simple, and only 2 network calls. And it keeps listFriends() from messing with the structure of the user metadata tables.