How to get a list of shared mailboxes for a user in ews-java-api? - exchangewebservices

Currently, I am able to access the shared mailbox of a user account if I already know the address of the shared mailbox. However, if I know just the user account details (but not the shared mailboxes it has access to), how can I retrieve the list of shared mailboxes linked to this user account in ews-java-api?
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
ExchangeCredentials credentials = new WebCredentials("username", "pwd");
service.setCredentials(credentials);
service.setUrl(new URI("https://xyz/EWS/Exchange.asmx"));
Mailbox mailbox = new Mailbox("sharedMailboxAddress");
FolderId folderId = new FolderId(WellKnownFolderName.MsgFolderRoot, mailbox);
FolderView folderView = new FolderView(20);
FindFoldersResults results = service.findFolders(folderId, folderView);
Here is the link to ews-java-api --> https://github.com/OfficeDev/ews-java-api

Related

how to access and modify google workspace domain user's google drive files as a domain administrator using google apps scripts

Objective:
as a google workspace domain admin for a school that uses google workspace education, I want to create a google apps script that given a google workspace user's email address (the current owner), the scritp should be able to get a list of all the user's folders and files in their google drive and then it should also be able to transfer the ownership of those folders and files to domain user and add the current owner as a viewer so they can only see the folders/files but can't modify them in any way.
things I tried:
DriveApp can access files/folders and change the ownership of the file/folder but only if you are the owner, and I want to do this as the domain admin, regardless which user owns the google drive and respective files/folders.
Drive API, seems to do the same as DriveApp as far you're the owner, I couldn't figure out how to give Drive API admin permissions so I can see every domain user google drive file list, if that's even possible.
GAM advance: I found this as management tool, I set it and it migh do what I need but it's bit complex for me, plus I was really hoping to be able to build the tool myself.
What worked halfway:
I found this: https://github.com/googleworkspace/apps-script-oauth2#using-service-accounts which refers to using a service account. It took a while but I manage to get a list of items that exist on a user's google drive with the script below. but I can't figure out how to access those files/folders so I can change the ownership or set viewers on them. I think I read that the service account will only give me read-only access so I'm doubting this is even possible.
Here's what I got so far:
function main(){
// Private key and client email of the service account.
var key = getJsonKey()
var clientEmail = 'service_account_email_setup_in_google_dev_console';
// Email address of the user to impersonate.
var userEmail = 'a_regular_domain_user#my_google_workspace_domain.com';
try{
var drive = getDriveService_(key,userEmail,clientEmail);
if (drive.hasAccess()) {
// this code gets me a json response with items that list id's and urls and other
//file metadata of the
// files that belongs to the domain user, this is as far as i got.
var url = 'https://www.googleapis.com/drive/v2/files';
var response = UrlFetchApp.fetch(url, {
headers: {
Authorization: 'Bearer ' + drive.getAccessToken()
}
});
var result = JSON.parse(response.getContentText());
//the following code returns a fileid in the user's google
//drive not shared with the admin
var fileid = JSON.stringify(result.items[0].id)
Logger.log(fileid);
//but the following code returns an error indicating that the
//file is not found (in reality it's not accessible by the
//admin account)
var file = Drive.Files.get(fileid);
//access a list of items and as I traverse it I'd like to
//change the ownership and
//add the the current user as a file viewer
//??
} else {
Logger.log(drive.getLastError());
}
}catch (e){
Logger.log(e)
}
}
// Load the JSON key file with private key for service account
function getJsonKey(){
var keyFile = DriveApp.getFileById("json_fileid_in_drive_obtained_from_googledevcons");
var key = JSON.parse(keyFile.getBlob().getDataAsString()).private_key;
return key
}
function reset() {
getDriveService__().reset();
}
//get the google drive from the domain user's email address
function getDriveService_(key,userEmail,clientEmail) {
return OAuth2.createService('GoogleDrive:' + userEmail)
.setTokenUrl('https://oauth2.googleapis.com/token')
.setPrivateKey(key)
.setIssuer(clientEmail)
.setSubject(userEmail)
.setPropertyStore(PropertiesService.getUserProperties())
.setCache(CacheService.getUserCache())
.setScope('https://www.googleapis.com/auth/drive');
}
Any help is appreciated :)
You are going in the right direction, the only part you are missing currently is setting up Domain Wide Delegation this will allow you to impersonate the users in your domain so you can make the changes on behalf of them by granting the service account permissions through the above mentioned DWD.
Since you have already created the Oauth2Service you will just need to send the user to impersonate through the OauthParams:
const oauthParams = {
serviceName: 'Nameofyourservice',
serviceAccount,
scopes: ['https://www.googleapis.com/auth/appsmarketplace.license', 'https://www.googleapis.com/auth/userinfo.email', 'https://mail.google.com', 'https://www.googleapis.com/auth/iam'],
userToImpersonate: 'usertoimpersonate#test.com',
};
The scopes were from the Marketplace API as an example.

How to obtain a list of all users with access to a Google shared drive using apps script

I used getEditors to obtain a list of editors of a spreadsheet and the returned list includes shared drive users. However, users with 'content manager' access to the shared drive are not included in the list. Any reason why this is the case?
I also found that getAccess may be used to obtain the type of access a specific user has to a drive folder. Using this approach, my objective would be to identify all users with FILE_ORGANIZER or ORGANIZER permission. See official documentation on permissions.
Is it possible to use an if statement or a loop to get this information?
Alternatively, is there a workaround to obtaining a list of all users with access to a shared drive that I might not have considered?
PS: Advanced drive service is not enabled.
// This code aims to protect a sheet in a spreadsheet
// by granting specific users access (super users)
// and revoking the access of any other user that can edit the spreadsheet.
/*
Attempted approach:
user1 and user2 have manager permission to the shared drive as a whole
user3 has content manager permission to the shared drive as a whole
user4 only has access to this spreadsheet in the shared drive
My objective is to ensure that only users 1 and 2 can edit the protected sheet.
Result:
Log shows that users 1 and 2 have access and user 4 does not.
However, user3 can still edit the sheet because they were no included in the getEditors() result hence their access could not be revoked.
*/
function protectASheet() {
var superusers = ['user1', 'user2'];
var ss = SpreadsheetApp.getActive();
var editors = ss.getEditors(); //get file editors
var sheet = SpreadsheetApp.getActive().getSheetByName('TestSheet');
//Set protection
var protection = sheet.protect().setDescription('This sheet is protected.');
protection.addEditors(superusers); //Grant superusers edit permission
protection.removeEditors(editors); //Revoke other file editors' permission
Logger.log("Only the following can edit " + sheet.getSheetName() + ": " + protection.getEditors());
}
Got some help from a colleague.
This approach relies on the advanced drive service being activate in the script editor. To turn on go to Resources -> Advanced Google Services.
It fetches the permissions and other details of every user that has any form of access to a file or folder without exception.
Code below:
function getPermissionsList() {
const fileId = "<FILE, FOLDER OR SHARED DRIVE ID HERE>"; // ID of your shared drive
// THIS IS IMPORTANT! The default value is false, so the call won't
// work with shared drives unless you change this via optional arguments
const args = {
supportsAllDrives: true
};
// Use advanced service to get the permissions list for the shared drive
let pList = Drive.Permissions.list(fileId, args);
//Put email and role in an array
let editors = pList.items;
var arr = [];
for (var i = 0; i < editors.length; i++) {
let email = editors[i].emailAddress;
let role = editors[i].role;
arr.push([email, role]);
}
Logger.log(arr);
}

Google Spreadsheet error on open "You do not have permission to call inputBox"

I am sharing a sheet with colleagues and want them to enter their name after opening the file for logging purposes. I have no problem running any of my scripts, naturally.
After they open the file, the script does not fire, but I can find the error logs and they say this:
You do not have permission to call inputBox at onOpen(Code:12). Why is that? What can I do about it?
Moreover, there are instances of the same error message but with a code 13 and 14.
I am not very fluent in Apps Script yet, but is it an authorization, protection issue?
function onOpen() {
//Log the user
var id, usrprops, logname;
id = Browser.inputBox('ID Check', 'Enter your identity (nickname) and press OK', Browser.Buttons.OK);
if (!id) { //if The user did not enter a name
id = Session.getActiveUser().getEmail(); //Get the logged users email address
id = id.slice(0,id.indexOf("#")); //Remove the domain part of email address
}
usrprops = PropertiesService.getUserProperties(); //Get User Properties
usrprops.setProperty('usrname', id); //Save the user name with a key name
return id;
}

How do I getGivenName() of getActiveUser()

Each user on the domain initiates a simple script we run for leave entitlements but we want the welcome message to be "Hi First Name," however the script doesn't seem to be able to fetch getGivenName() from getActiveUser() for a standard user.
Is there a way?
As noted in comments, and in Documentation, the UserManager Service is only accessible by Domain Administrators.
Here's an alternative. Domain Users may have themselves in their own contacts, so how about a best-effort attempt at finding themselves there?
/**
* Get current user's name, by accessing their contacts.
*
* #returns {String} First name (GivenName) if available,
* else FullName, or login ID (userName)
* if record not found in contacts.
*/
function getOwnName(){
var email = Session.getEffectiveUser().getEmail();
var self = ContactsApp.getContact(email);
// If user has themselves in their contacts, return their name
if (self) {
// Prefer given name, if that's available
var name = self.getGivenName();
// But we will settle for the full name
if (!name) name = self.getFullName();
return name;
}
// If they don't have themselves in Contacts, return the bald userName.
else {
var userName = Session.getEffectiveUser().getUsername();
return userName;
}
}
In Apps Script, I was able to get this information using the About REST API: https://developers.google.com/drive/v2/reference/about/get
var aboutData = DriveApp.About.get();
var userEmail = aboutData["user"]["emailAddress"];
var userDisplayName = aboutData["user"]["displayName"];
You can get a user name but first you have to create a domain user using the provisioning api. You can enable the API by logging in to your admin account, and select Domain settings and the User settings tab to select the checkbox enabling the Provisioning API. Read more about it here
You can then use
user = user.getgivenName()
Since the UserManager Service is only available to a Domain Administrator, you could publish a service as the administrator, that serves user's Given Names, and invoke that from the user-run script using the UrlFetchApp.
The UserName Service
Refer to the Content Service Documentation for the background information this is based upon.
The service accepts a parameter, userName, which it uses to perform a lookup as the administrator.
Paste the following code into a script, then deploy the script as a web service. This must be done by a Domain Administrator, as the service access the UserManager Service, but the script must be made accessible by all users in the domain. (Since I'm not an admin in my domain, I cannot access the UserManager, so I've included a domain-user-invokable line for testing, calling the getOwnName() function I described in my first answer.)
Remember to invoke doGet() from the debugger to go through the authorization before accessing the published service.
/**
* When invoked as a Web Service running as Domain Administrator,
* returns the GivenName of the requested user.
*
* #param {String} userName= Should be set to Session.getEffectiveUser().getUsername().
*/
function doGet(request) {
//return ContentService.createTextOutput(getOwnName()); // for testing by non-admin user
var userName = request.parameters.userName;
var givenName = UserManager.getUser(userName).getGivenName();
return ContentService.createTextOutput(givenName);
}
Invoke service using UrlFetch
Refer to Using External APIs for an explanation of how to make use of the service written in the previous section. I'll show how to access the service from another script, but remember that you can also do this from web pages within your domain.
We will use UrlFetchApp.fetch() to get our service to return the user's first name as a String.
The service was written to accept one parameter, userName, and we append this to the url, in the form userName=<string>.
With the URL built, we fetch(), then retrieve the name from the response. While this example returns just the name, you may choose to change the service to return the complete "Hello User" string.
function testService() {
var domain = "my-google-domain.com";
var scriptId = "Script ID of service";
var url = "https://script.google.com/a/macros/"+domain+"/s/"+scriptId+"/exec?"
+ "userName="+Session.getEffectiveUser().getUsername();
var response = UrlFetchApp.fetch(url);
var myName = response.getContentText();
debugger; // pause in debugger
}
Another potential way of getting the display name on a gmail account is to find a Draft email in the GmailApp, and get the From header, which may have the full name. Some drafts might be setup with no display name in gmail, in which case the From header will only be the email address, but typically the From header is in the format:
Firstname Lastname <email#domain.com>
This code should get you the string above from the first gmail Draft: (note this will probably throw an exception if there are no drafts, so check that first.)
GmailApp.getDrafts()[0].getMessage().getHeader("From")
Ref: https://developers.google.com/apps-script/reference/gmail/gmail-message#getHeader(String)
Ref: https://www.ietf.org/rfc/rfc2822.txt

Unable to list files with GoogleDrive API

I'm trying to get a list of documents from google drive via its SDK. I use the service account to accomplish the authentication, and set the scope to https://www.googleapis.com/auth/drive which has the full permission.
However, the response contains an empty item list though there do have test files in my google drive.
AuthorizationServerDescription desc = GoogleAuthenticationServer.Description;
X509Certificate2 key = new X509Certificate2(m_file, "notasecret", X509KeyStorageFlags.Exportable);
string scope = Google.Apis.Drive.v2.DriveService.Scopes.Drive.ToString().ToLower();
AssertionFlowClient client = new AssertionFlowClient(desc, key) { ServiceAccountId = m_email, Scope = "https://www.googleapis.com/auth/" + scope };
OAuth2Authenticator<AssertionFlowClient> auth = new OAuth2Authenticator<AssertionFlowClient>(client, AssertionFlowClient.GetState);
DriveService service = new DriveService(auth);
FilesResource.ListRequest request = service.Files.List();
request.Q = "";
Stream dataStream = request.FetchAsStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();
Check Nivco's answer to Google Drive SDK: Empty array of files returned from Drive for more details about your issue.
You're basically listing the files in the service account's Drive and not yours. What you need to do is setup the email for the user of the domain you are trying to impersonate, check the Google Drive SDK documentation for instructions and sample code showing how to do it:
https://developers.google.com/drive/delegation