Google Workspace: Add all users in a child organization unit to a group using Google Apps Script - google-admin-sdk

I'd like to add all users in a child Organization Unit to a group. I can do that in Admin Dashboard but it only shows 50 users at a time. Since we have thousands of users in each child OU, this process would be inconvenience.
My solution:
I followed a guide (Source) and used Google Apps Script to run the following code but it simply didn't do anything. The log shows "Execution started" then "Execution completed" but no user is moved to the group. I suspect the format for the OU in the code is wrong. It is a bit tricky to get it right especially the OU is Arabic (Right to Left). Any idea what could be wrong?
function myFunction() {
/**
* Add all users of an organizational unit (OU) to specific groups
* in Google Workspace
*
* Usage:
* Change the OU variable, in a format of /OU/SubOU/SubSubOU. The root OU is represented as /
* Change the groupEmails variable, which is a list of group emails.
*
* © 2021 xFanatical, Inc.
* #license MIT
*
* #since 1.0.0 proof of concept
*/
const OU = '/كلية الطب/الطلبة/طلبة الدراسات الاولية 2019 - 2020/testing'
const groupEmails = ['100gb.limit#uokufa.edu.iq']
function addAllOUUsersToGroup() {
let pageToken
let page
do {
page = AdminDirectory.Users.list({
customer: 'my_customer',
maxResults: 100,
pageToken,
query: `orgUnitPath='${OU}'`,
})
let users = page.users
if (users) {
users.forEach((user) => {
groupEmails.forEach((groupEmail) => {
try {
AdminDirectory.Members.insert({
email: user.primaryEmail,
role: 'MEMBER',
type: 'USER',
}, groupEmail)
Logger.log(`Added user [${user.primaryEmail}] to group [${groupEmail}]`)
} catch (e) {
Logger.log(`Failed to add user [${user.primaryEmail}] to group [${groupEmail}], error: ${e.details && e.details.message && e.details.message}`)
}
})
})
} else {
Logger.log('No users found.')
}
pageToken = page.nextPageToken
} while (pageToken)
}
}

Issue:
You are not executing the function addAllOUUsersToGroup.
addAllOUUsersToGroup is declared inside myFunction, but it is never called. Therefore, if you execute myFunction, addAllOUUsersToGroup won't run.
Solution:
Either call addAllOUUsersToGroup inside myFunction. For example:
function myFunction() {
// ...stuff...
function addAllOUUsersToGroup() {
// ...stuff...
}
addAllOUUsersToGroup(); // <== ADD THIS
}
Or, alternatively, take the function addAllOUUsersToGroup outside myFunction and call it directly:
function addAllOUUsersToGroup() { <== THIS IS NOT INSIDE myFunction
// ...stuff...
}
Reference:
Calling functions

Related

Smarthome AOG. The best way (in 2021) to integrate state of devices with firestore fields

I have this project: https://github.com/neuberfran/firebasefunction/blob/main/firebase/functions/smart-home/fulfillment.js
It works well. But, for example, I want to implement a condition that if I have the garage closed and I said "close garage", the Home assistantt will alert me about it.
As shown in the photo below, I am using an rpi3/iot-device/back-end that controls the garagestate field.
I need to know the best way to implement this condition, that is, read the value of the garagestate field and from that, know if I can open the garage or not:
You'd probably need to add an intermediary condition in your onExecute to return an error based on the Firestore state:
// ...
for (const target of command.devices) {
const configRef = firestore.doc(`device-configs/${target.id}`)
const targetDoc = await configRef.get()
const {garagestate} = targetDoc.data()
if (garagestate === false) {
// garagestate exists and is false
// return an error
return {
requestId,
payload: {
status: 'ERROR',
errorCode: 'alreadyClosed'
}
}
}
// ...
}
// ...

Fabric Composer test code not working

I´ve just replaced the Composer default sample ("sampleAsset", "sampleTransaction", etc) by another one I created, for my better understanding. Everything works except for the transaction, which return me the error message:
"**Error: Could not find any functions to execute for transaction org.acme.sample.CompraDoVinho#**2b2d0624-bc..."
Find below the source codes:
Blockquote
Model file:
namespace org.acme.sample
asset Vinho identified by IDvinho {
o String IDvinho
--> Participante owner
o String uva
o String nomeVinho
o Integer preco
}
participant Participante identified by IDparticipante {
o String IDparticipante
o String tipo
o String nomeEmpresa
}
transaction CompraDoVinho identified by IDcompra {
o String IDcompra
--> Vinho asset
o Integer precoVenda
}
Logic:
function onSampleTransaction(CompraDoVinho) {
CompraDoVinho.asset.preco = CompraDoVinho.precoVenda;
return getAssetRegistry('org.acme.sample.Vinho')
.then(function (assetRegistry) {
return assetRegistry.update(CompraDoVinho.asset);
});
}
Permissions:
rule Default {
description: "Allow all participants access to all resources"
participant: "ANY"
operation: ALL
resource: "org.acme.sample"
action: ALLOW
}
Blockquote
Could anybody help me finding where is the bug in my code?
Thanks in advance
The issue is almost certainly because you've renamed the transaction. Composer has 2 mechanisms to route transactions to JS functions:
(Legacy) using an onMyTransactionType naming convention. I.e. the function will be called when an instance of MyTransactionType is submitted.
(Preferred) using the #transaction and #param annotations. See below for an example. The #transaction annotation indicates that the function would like to process transactions and the #param annotation is used to specify the type of the transaction to process.
/**
* Place an order for a vehicle
* #param {org.acme.vehicle.lifecycle.manufacturer.PlaceOrder} placeOrder - the PlaceOrder transaction
* #transaction
*/
function placeOrder(placeOrder) {
console.log('placeOrder');
let factory = getFactory();
let NS = 'org.acme.vehicle.lifecycle.manufacturer';
let order = factory.newResource(NS, 'Order', placeOrder.transactionId);
order.vehicleDetails = placeOrder.vehicleDetails;
order.orderStatus = 'PLACED';
order.manufacturer = placeOrder.manufacturer;
// save the order
return getAssetRegistry(order.getFullyQualifiedType())
.then(function (registry) {
return registry.add(order);
});
}
Absolutely. The annotation is essential for the function to work!
#param must state the class name of the transaction and the param name
#transaction declared underneath, with function to follow in block below
#param {org.acme.mynetwork.Foo} foo - the report to be processed
* #transaction
Please Replace the code in your logic.js file with following code and the error will surely be gone. Mine was the same problem, I just added the required JS doc annotations above the function and the same issue was resolved!
'use strict';
var NS = 'org.acme.sample';
/**
* #param {org.acme.sample} CompraDoVinho
* #transaction
*/
function onSampleTransaction(CompraDoVinho) {
CompraDoVinho.asset.preco = CompraDoVinho.precoVenda;
return getAssetRegistry('org.acme.sample.Vinho')
.then(function (assetRegistry) {
return assetRegistry.update(CompraDoVinho.asset);
});
}
Hope this helps you!

Sitecore workflow approval state query

I have created workflow in my sitecore project and on final state ( Approval ) I just want auto publish to a particular database.
So where should I do the changes to point to database.
Thanks
In order to perform automatic publishing, your final state should contain a workflow action, that does the job for you. You may take a look on Sample Workflow (that comes by default with Sitecore) - Approved state. It contains child item Auto Publish, that has two fields.
Type string:
Sitecore.Workflows.Simple.PublishAction, Sitecore.Kernel
sets the class that in fact does publishing. You may inherit from that class and implement your own behavior, supply extra parameters etc. I would advise you to take dotPeek or Reflector and look-up this class implementation so that you may adjust your own code.
Parameters:
deep=0
..stands for publishing child items recursively.
Update: Lets take a look on decompiled class from Sample Workflow Auto Publish action:
public class PublishAction
{
public void Process(WorkflowPipelineArgs args)
{
Item dataItem = args.DataItem;
Item innerItem = args.ProcessorItem.InnerItem;
Database[] targets = this.GetTargets(dataItem);
PublishManager.PublishItem(dataItem, targets, new Language[1]
{
dataItem.Language
}, (this.GetDeep(innerItem) ? 1 : 0) != 0, 0 != 0);
}
private bool GetDeep(Item actionItem)
{
return actionItem["deep"] == "1" || WebUtil.ParseUrlParameters(actionItem["parameters"])["deep"] == "1";
}
private Database[] GetTargets(Item item)
{
using (new SecurityDisabler())
{
Item obj = item.Database.Items["/sitecore/system/publishing targets"];
if (obj != null)
{
ArrayList arrayList = new ArrayList();
foreach (BaseItem baseItem in obj.Children)
{
string name = baseItem["Target database"];
if (name.Length > 0)
{
Database database = Factory.GetDatabase(name, false);
if (database != null)
arrayList.Add((object)database);
else
Log.Warn("Unknown database in PublishAction: " + name, (object)this);
}
}
return arrayList.ToArray(typeof(Database)) as Database[];
}
}
return new Database[0];
}
}
GetTargets() method from above default example does publishing to all targets that are specified under /sitecore/system/publishing targets path. As I mentioned above, you may create your own class with your own implementation and reference that from workflow action definition item.
You can look into Sample workflow's Auto publish action. But in general you can create a Workflow Action with type: Sitecore.Workflows.Simple.PublishAction, Sitecore.Kernel and set parameters as deep=1&related=1&targets=somedb,web&alllanguages=1

Silverlight paging webservice results

We have a Silverlight application that calls a web service to retrieve database rows from SQL Server. These are then displayed on the screen in a paged control. However, the entire table is needed and it consists of several thousand rows. On the customer system, if we ask for more than around 1500 rows, we get HttpRequestTimedOutWithoutDetail. On our development system we need about 500,000 rows before this happens.
Obviously, what we should be doing is paging the results and returning them to the silverlight bit by bit. But I don't know how to do this. Can anyone advise, or point me to some web-pages that clearly explain the principles and methods (I am a bit simple!)
Here is the code in the Web Service:
public IQueryable<Referral> GetReferrals()
{
/*
* In the customer's environments it seems that any more than around 1500 referrals will break the system: they will fail to load.
* In the dev environment is takes around 500,000 so it seems to be timeout related.
*
* The code below was an attempt to restrict the number, only returning referrals above a certain size and within a certain age.
* It seems the customer is more interested in the smaller referrals though since they are more likely to be added to existing
* substations so if this method is re-instated, we should be using '<' instead of '>'
*
int mdToBeMet = int.Parse(ConfigurationManager.AppSettings["ReferralMDToBeMet"]);
DateTime minusNYears = DateTime.Today.AddYears(int.Parse(ConfigurationManager.AppSettings["ReferralTargetDate"]) * -1);
int maxReferralsCount = int.Parse(ConfigurationManager.AppSettings["ReferralMaxRecordCount"]);
if (mdToBeMet != 0 && maxReferralsCount != 0)
{
return this.ObjectContext.Referrals.Where(x => x.MD_to_be_Met > mdToBeMet && x.Target_Date > minusNYears).OrderByDescending(y => y.Target_Date).Take(maxReferralsCount);
}
*/
/*
* This is the 2nd attempt: the customer is mainly interested in referrals that have an allocated substation
*/
bool allocatedReferralsOnly = bool.Parse(ConfigurationManager.AppSettings["ReferralAllocatedOnly"]);
int maxReferralsCount = int.Parse(ConfigurationManager.AppSettings["ReferralMaxRecordCount"]);
if (allocatedReferralsOnly)
{
var referrals = this.ObjectContext.Referrals.Where(x => x.Sub_no != "").OrderByDescending(y => y.Target_Date).Take(maxReferralsCount);
return referrals;
}
else
{
/*
* Ideally, we should just page the referrals here and return to retrieving all of them, bit by bit.
*/
var referrals = this.ObjectContext.Referrals;
return referrals;
}
}
Many thanks for any suggestions.
An example to expand on the comment I gave ...
/// <summary>
/// Returns a page of Referrels
/// pageNumber is 0-index based
/// </summary>
public IQueryable<Referrel> GetReferrals(int pageNumber)
{
var pageSize = 100;
return ObjectContext.Referrals.Skip(pageNumber*pageSize).Take(pageSize);
}
Obviously you could pass in the pageSize too if you wanted, or define it as a constant somewhere outside of this method.

Using the Reporting Services Web Service, how do you get the permissions of a particular user?

Using the SQL Server Reporting Services Web Service, how can I determine the permissions of a particular domain user for a particular report? The user in question is not the user that is accessing the Web Service.
I am accessing the Web Service using a domain service account (lets say MYDOMAIN\SSRSAdmin) that has full permissions in SSRS. I would like to programmatically find the permissions of a domain user (lets say MYDOMAIN\JimBob) for a particular report.
The GetPermissions() method on the Web Service will return a list of permissions that the current user has (MYDOMAIN\SSRSAdmin), but that is not what I'm looking for. How can I get this same list of permissions for MYDOMAIN\JimBob? I will not have the user's domain password, so using their credentials to call the GetPermissions() method is not an option. I am however accessing this from an account that has full permissions, so I would think that theoretically the information should be available to it.
SSRS gets the NT groups from the users' NT login token. This is why when you are added to a new group, you are expected to log out and back in. The same applies to most Windows checks (SQL Server, shares, NTFS etc).
If you know the NT group(s)...
You can query the ReportServer database directly. I've lifted this almost directly out of one of our reports which we use to check folder security (C.Type = 1). Filter on U.UserName.
SELECT
R.RoleName,
U.UserName,
C.Path
FROM
ReportServer.dbo.Catalog C WITH (NOLOCK) --Parent
JOIN
ReportServer.dbo.Policies P WITH (NOLOCK) ON C.PolicyID = P.PolicyID
JOIN
ReportServer.dbo.PolicyUserRole PUR WITH (NOLOCK) ON P.PolicyID = PUR.PolicyID
JOIN
ReportServer.dbo.Users U WITH (NOLOCK) ON PUR.UserID = U.UserID
JOIN
ReportServer.dbo.Roles R WITH (NOLOCK) ON PUR.RoleID = R.RoleID
WHERE
C.Type = 1
look into "GetPolicies Method" you can see at the following link.
http://msdn.microsoft.com/en-us/library/reportservice2010.reportingservice2010.getpolicies.aspx
Hopefully this will get you started. I use it when copying Folder structure, and Reports from an old server to a new server when I want to 'migrate' my SSRS items from the Source to the Destination Server. It is a a Method to Get the Security Policies for an item on one server, and then set the Security Policies for an identical item on another server, after I have copied the item from the Source Server to the Destination Server. You have to set your own Source and Destination Server Names.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Web.Services.Protocols; //<=== required for SoapException
namespace SSRS_WebServices_Utility
{
internal static class TEST
{
internal static void GetPoliciesForAnItem_from_Source_ThenSetThePolicyForTheItem_on_Destination(string itemPath)
{
string sSourceServer = "SOURCE-ServerName";
Source_ReportService2010.ReportingService2010 sourceRS = new Source_ReportService2010.ReportingService2010();
sourceRS.Credentials = System.Net.CredentialCache.DefaultCredentials;
sourceRS.Url = #"http://" + sSourceServer + "/reportserver/reportservice2010.asmx";
string sDestinationServer = "DESTINATION-ServerName";
Destination_ReportService2010.ReportingService2010 DestinationRS = new Destination_ReportService2010.ReportingService2010();
DestinationRS.Credentials = System.Net.CredentialCache.DefaultCredentials;
DestinationRS.Url = #"http://" + sDestinationServer + "/reportserver/reportservice2010.asmx";
Boolean val = true;
Source_ReportService2010.Policy[] curPolicy = null;
Destination_ReportService2010.Policy[] newPolicy = null;
try
{
curPolicy = new Source_ReportService2010.Policy[1];
curPolicy = sourceRS.GetPolicies(itemPath, out val); //e.g. of itemPath: "/B2W/001_OLD_PuertoRicoReport"
//DestinationRS.SetPolicies(itemPath, newPolicy);
int iCounter = 0;
//int iMax = curPolicy.Length;
newPolicy = new Destination_ReportService2010.Policy[curPolicy.Length];
foreach (Source_ReportService2010.Policy p in curPolicy)
{
//create the Policy
Destination_ReportService2010.Policy pNew = new Destination_ReportService2010.Policy();
pNew.GroupUserName = p.GroupUserName;
pNew.GroupUserName = p.GroupUserName;
Destination_ReportService2010.Role rNew = new Destination_ReportService2010.Role();
rNew.Description = p.Roles[0].Description;
rNew.Name = p.Roles[0].Name;
//create the Role, which is part of the Policy
pNew.Roles = new Destination_ReportService2010.Role[1];
pNew.Roles[0]=rNew;
newPolicy[iCounter] = pNew;
iCounter += 1;
}
DestinationRS.SetPolicies(itemPath, newPolicy);
Debug.Print("whatever");
}
catch (SoapException ex)
{
Debug.Print("SoapException: " + ex.Message);
}
catch (Exception Ex)
{
Debug.Print("NON-SoapException: " + Ex.Message);
}
finally
{
if (sourceRS != null)
sourceRS.Dispose();
if (DestinationRS != null)
DestinationRS.Dispose();
}
}
}
}
To invoke it use the following:
TEST.GetPoliciesForAnItem_from_Source_ThenSetThePolicyForTheItem_on_Destination("/FolderName/ReportName");
Where you have to put your own SSRS Folder Name and Report Name, i.e. the Path to the item.
In fact I use a method that loops through all the items in the Destination folder that then calls the method like this:
internal static void CopyTheSecurityPolicyFromSourceToDestinationForAllItems_2010()
{
string sDestinationServer = "DESTINATION-ServerName";
Destination_ReportService2010.ReportingService2010 DestinationRS = new Destination_ReportService2010.ReportingService2010();
DestinationRS.Credentials = System.Net.CredentialCache.DefaultCredentials;
DestinationRS.Url = #"http://" + sDestinationServer + "/reportserver/reportservice2010.asmx";
// Return a list of catalog items in the report server database
Destination_ReportService2010.CatalogItem[] items = DestinationRS.ListChildren("/", true);
// For each FOLDER, debug Print some properties
foreach (Destination_ReportService2010.CatalogItem ci in items)
{
{
Debug.Print("START----------------------------------------------------");
Debug.Print("Object Name: " + ci.Name);
Debug.Print("Object Type: " + ci.TypeName);
Debug.Print("Object Path: " + ci.Path);
Debug.Print("Object Description: " + ci.Description);
Debug.Print("Object ID: " + ci.ID);
Debug.Print("END----------------------------------------------------");
try
{
GetPoliciesForAnItem_from_Source_ThenSetThePolicyForTheItem_on_Destination(ci.Path);
}
catch (SoapException e)
{
Debug.Print("SoapException START----------------------------------------------------");
Debug.Print(e.Detail.InnerXml);
Debug.Print("SoapException END----------------------------------------------------");
}
catch (Exception ex)
{
Debug.Print("ERROR START----------------------------------------------------");
Debug.Print(ex.GetType().FullName);
Debug.Print(ex.Message);
Debug.Print("ERROR END----------------------------------------------------");
}
}
}
}