Office 365 EWS - GetUserAvailability ID is null - c#

I am connecting to Exchange on 365 using EWS and using the GetUserAvailability method on the ExchangeService object to retrieve a collection of calendar items for a number of users (no more that 30).
My user that I am using to connect to EWS has impersonation rights and everything is working with the exception of one thing.
When I loop through the AttendeesAvailability collection, and then through each CalendarEvent within its CalendarEvents collection, I am able to see the Details object and access things like the Subject, Location, IsPrivate, etc...
The problem I have, is that the Details.StoreId is always null.
I have two 365 Exchange environments, our and a client's. When I try and access the StoreId on ours, it works absolutely fine and has the unique ID as expected. When I run the same come on the client's instance, it is null; but all the other properties are populated for the Details object.
This lead me to believe that it was a permissions issue for the service user within Exchange. So, I have slowly raised the permissions levels, hoping to hit the sweet spot, but nothing. The user is now an Exchange Admin (which I believe should have blanket access to anything and everything within the Exchange environment - including calendar items for each person's mailbox). Still not joy.
So, is it a configuration issue with the Exchange setup?
It is worth noting that all the other functionality that my application has, works perfect. It is just this ID that I am missing.
I am at a loss on this one. If it wasn't for the fact that I have shaved my head due to the lock down, I would be pulling my hair out right about now.
Any help or suggestions would be greatly appreciated.
Cheers
UPDATE 1
So, I have found that if I perform the same process on the service account that I am authenticating with, I get the StoreId back. This account was created after the migration from on-prem to O365 (which may mean nothing). I am in the process of getting access to another account that has been created since the migration to see if it works for that one as well. This will help me identify if the issue is likely to be with the migrated mailboxes only or with any mailbox apart from the account used to authenticate. I will update soon...
UPDATE 2
I have tested against a new account. I set the default permissions on the new account's calendar to Reviewer (even tried Owner and Anonymous just for a sanity check). When I authenticate using the service account, I don't get the StoreId. When I authenticate using the new test account, I do get the StoreId.
This leads me to believe that it is a permissions issue. It's now a case of finding out what that magic permissions is. It still eludes me as to why it is only the ID that I don't get back.
Still open to any suggestions.
UPDATE 3
It looks like it may be something different between build versions on EWS.
The version that works is reporting a server version of 15.20.3021.030.
The version that does not work is version 15.20.3045.019.

So, it's been a while, but Microsoft have now fixed the issue and the existing code is now working fine.
I can also confirm that this work when using OAuth as well.

The problem is probably related to not having sufficient rights from the account you use for the Exchange Service authentication to the target account.
Use the command below to give rights and try again.
Add-MailboxFolderPermission -Identity User#domain.com:\Calendar -User serviceceaccount#domain.com -AccessRights Reviewer
This is a very annoying issue, I have opened an incident with MS in the past, but got the disappointing answer that this won't change soon.
Below is the PS code to set rights to all users, rooms, resources, regardless of culture (language) and name of the calendar folder.
Param(
[parameter(position=0,Mandatory=$true)][string] $serviceaccount,
[parameter(position=1,Mandatory=$true)][string] $mailboxtype
)
$acl = "Reviewer"
if ($mailboxtype -eq "All") {
$mailboxes = Get-Mailbox | Where {$_.RecipientTypeDetails -eq "UserMailbox" -or $_.RecipientTypeDetails -eq "RoomMailbox" -or $_.RecipientTypeDetails -eq "EquipmentMailbox"} | Select -expand PrimarySmtpAddress | Select -expand Address | Where {$_ -NotLike "Administrator#*"}
}
elseif ($mailboxtype -eq "Users") {
$mailboxes = Get-Mailbox | Where {$_.RecipientTypeDetails -eq "UserMailbox"} | Select -expand PrimarySmtpAddress | Select -expand Address | Where {$_ -NotLike "Administrator#*"}
}
elseif ($mailboxtype -eq "Resources") {
$mailboxes = Get-Mailbox | Where {$_.RecipientTypeDetails -eq "RoomMailbox" -or $_.RecipientTypeDetails -eq "EquipmentMailbox"} | Select -expand PrimarySmtpAddress | Select -expand Address
}
else {
"Error specifying target mailbox type. Choose one of: 'All', 'Users' or 'Resources'.`n
'All' - UserMailbox, RoomMailbox, EquipmentMailbox.`n
'Users' - UserMailbox.`n
'Resources' - RoomMailbox, EquipmentMailbox."
}
foreach ($mbx in $mailboxes)
{
$calName = Get-MailboxFolderStatistics -Identity $mbx -FolderScope Calendar | Where-Object { $_.FolderType -eq 'Calendar'} | Select-Object Name, FolderId #-ErrorAction Stop
#echo "${mbx}:\$($calName.Name)"
Add-MailboxFolderPermission -Identity ${mbx}:\$($calName.Name) -User $serviceaccount -AccessRights $acl | Select-Object Identity, FolderName, User, AccessRights
}
Save as PS script, call with two params: serviceaccount & mailboxtype

Related

How to get PowerShell JEA client IP address

I am working with PowerShell constrained endpoints. I have an endpoint that runs as a specific user as it needs to reach out to make calls to another service on another system. Virtual accounts and gMSAs will not work with this other service so it must be a regular user account.
I would like to be able to record the connection's client IP address in the logs. In the past I have used the following:
$script:EndpointClientIp = Get-WSManInstance Shell -Enumerate | Where-Object { $_.ProcessId -eq $script:EndpointProcessId } | Select-Object -ExpandProperty ClientIP
However, the above Get-WSManInstance command only works if the user is an admin. In these new JEA endpoints I am trying to avoid giving the run-as user admin privileges.
Is there a way to acquire this IP address without being an admin on the system? Perhaps there is a way to modify the WSMan permissions to allow this user account to access that portion of the WSMan config? If so, what else would it gain access to that I would want to consider? If not through the WSMan, is there another way to tie the IP address of an incoming connection to the process ID of the app allowing the connection (again, without admin privileges)?
This is a Windows Server 2019 box.

Grant access on ActiveDirectory ServerContainer in C#

I need to find a way to programmatically grant permissions on a ServerContainer of a default site in Active Directory.
Manually it's simple to do via the "Active Directory Sites and Services":
And then from the properties one needs to go to here to grant required permissions:
I have tried to do it via PowerShell, I managed to find the ServersContainer but I cannot find a way to set its permissions. I have browsed lots of stuff on Google but I cannot find a solution.
Anybody has got an idea how to approach it?
Sites are ADObjects living in CN=Configuration, so if you use Get-ADObject you can then use Get-ACL on that object in the AD PS drive. Which you can then use the methods on that object or the Set-ACL cmdlet to alter.
$params = #{
Filter = 'ObjectClass -eq "site" -and name -eq "Default-First-Site-Name"'
SearchBase = 'CN=Configuration,DC=contoso,DC=com'
}
$SiteDN = (Get-ADObject #params).DistinguishedName
$SiteACL = Get-ACL "AD:\$($SiteDN)"

Exchange Routing agent - check if email address is a exchange shared mailbox using C#

I'm trying to build a routing agent dll for Exchange 2010, that will check if the recipient email address is an Exchange shared mailbox (a regular mailbox with multiple full control owner users). If it is, I want to get the email addresses of the owners on that mailbox.
so basically I need two things:
Query exchange to check if owners on the specific email address or if shared mailbox have another identifier I can use.
Get all the owner's email addresses.
I'm working on visual studio 2013 with C#, I found a partial answer with PowerShell, but I don't know how to convert it to C#.
This is what I got so far:
Get-MailboxPermission sharedMailBox#dudu.com | where $_.user.tostring() -ne "NT AUTHORITY\SELF" -and $_.IsInherited -eq $false}
This will give me the owners list, but I don't know how to use this in C# in a native way.
// run over all recipients list
foreach (EnvelopeRecipient recp in messageEventArgs.MailItem.Recipients)
{
.........
}

Win32_GroupUser does not return nested groups from domain security groups

When I use Win32_GroupUser to request members of a local security group, I also get nested domain groups.
E.g. in PowerShell, this query on a member server:
Get-WmiObject Win32_GroupUser -Filter "GroupComponent='Win32_Group.Domain=`"myserver`",Name=`"LocalTestGroup02`"'" | select PartComponent
returns both user- and domain accounts:
PartComponent
-------------
\\myserver\root\cimv2:Win32_Group.Domain="FLIP",Name="TestGroup"
\\myserver\root\cimv2:Win32_UserAccount.Domain="FLIP",Name="test5"
\\myserver\root\cimv2:Win32_UserAccount.Domain="FLIP",Name="test4"
The same query, but this time on a domain controller and for membership of a domain security group:
Get-WmiObject Win32_GroupUser -Filter "GroupComponent='Win32_Group.Domain=`"FLIP`",Name=`"TestGroup2`"'" | select PartComponent
only returns the user accounts, despite that its members are exactly the same as the local group above.
PartComponent
------------
\\VW-DC01\root\cimv2:Win32_UserAccount.Domain="FLIP",Name="test4"
\\VW-DC01\root\cimv2:Win32_UserAccount.Domain="FLIP",Name="test5"
I see this behavior
when executed locally and from another server with the -ComputerName parameter
also with a C# application using System.Management
on Domain local, Global, and Universal security groups
Does anybody know why that is?
More important, can I change this behavior so I also get to see nested groups in domain groups?
I'm trying to enumerate membership of both local- and domain groups, including members in nested groups.
PS: I know I can also use the Get-ADGroupMember cmdlet, but that's not an option. I only have a WMI connection to the target servers, not LDAP or WinRM.
Thx,
You will only be able to see local groups (i.e. non-domain) on the computer itself, not from the domain controller.
As for seeing nested groups, apart from rolling your own recursive function, you can take a look at Quest Active Directory tools
function is Get-QADGroupMember

Connect to MySQL database w/ C# as non-root user?

Let me start by saying I'm completely new to databases, but have been reading through the MySQL tutorial they have.
Right now, I'm trying to make an app that allows unprivileged users (non-root) to connect and do some commands on a database through a C# GUI app. The users will login to the database using windows authentication.
Now, I was able to to make a quick GUI where the person running the program can connect to the database on a local host using "root" and get whatever content is in it.
My question is, how exactly do I allow users to connect w/ non-root privileges? The only things I've been able to find all deal w/ using the connection string w/ "root" as the user.
Edit: The database is already made. I won't personally be able to connect to it as a root user and give other users permissions.
You could use the grant syntax, as root to grant permissions to other users. E.g.:
GRANT ALL PRIVILEGES ON mydatabase.* TO pfinferno
Some important concepts too are the rows in the table shown here:
select user,host,password from mysql.user where user='pfinferno';
Important takeaways, users can have multiple hostnames or wildcard. Each have their own rights and passwords. Though the password from the above is hashed, at least you can quickly eyeball it so see if all the passwords match (such as root with 3 accounts).
The host column is populated by the following values, mostly:
Specimen A:
localhost
127.0.0.1
%
common names such md21.newyork.comcastbusiness.net and the like
The % value is the wildcard. It is for flexibility, but it can be highly dangerous when used with users like 'root'#'%'. Keeping root as the 1st two only in Specimen A is highly advised. Also, the first two are quite different in different tools. What I recommend is having a root user row for the first two, and keeping the passwords the same. There are pros and cons to this approach. But remember that when you can't connect that something else out there was relying on the connection, be it an agent, phpmyadmin, a tool, the my.conf settings, etc. You will be revisiting this a lot.
In the example given by Mureinik it grants privileges to the user with the wildcard host = %. This means it is banking on a user created as such. Note that it is typical to have user accounts setup that way. Though there is nothing restricting you to locking it down tighter.
When a user attempts to connect to the server, the user he is ultimately connected as can be resolved to a different user/host combo, as can be seen in the case where there is no localhost (host) user or common-name, but rather one with a host value of wildcard %. This can be seen with this query:
Specimen B:
select current_user(),user();
The latter is the user presented by the connection attempt, the former is the one that was resolved and actual. The implications can cause one to waste days in debugging, as can be seen in these forums. Some people can't connect for days, I am serious.
Specimen C:
create user 'pfinferno'#'localhost' identified by 'thePassword';
create user 'pfinferno'#'127.0.0.1' identified by 'thePassword';
create user 'pfinferno' identified by 'thePassword';
create user 'pfinferno'#'md21.newyork.comcastbusiness.net' identified by 'thePassword';
-- note #3 above is same as 'pfinferno'#'%'
Now granted, the host name may be wildcard for normal users, and host name may be localhost and 127.0.0.1 for root only. But in the attempt to lock down security, admins often create user accounts based on hostname coming in (such as Specimen C, line 4), and vary security with grants based on that. Managing it can be a bit overwhelming. So they often just say screw it, I will create the wildcard user. That may be fine for a new user, say Susie, but for Secimen C line 4, if that is who you are coming in, you can have grants on line 3 user that you won't pick up. So the resolving of what user your are actuallity (See Specimen B), goes from most specific hostname to fallback to more general, until it finds one such as wildcard.
Unfortunately, users don't connect with hostname specified per se, they just are what they are. They try to connect. So you don't say hey I want to be this thing #hostname, you just are what you are. You are 'pfinferno'#'md21.newyork.comcastbusiness.net', but may fallback to wildcard.
If users are dropped in Specimen C except for wildcard %, well you better have the grants that went with them that you expect, because you are now a new user.
Try to limit the use of wildcards on the grant statement to not do *.* as in the lazy approach, which would just grant rights to all databases and tables. In Mureinik's example, it was for all tables in one database. Not too shabby. Try to fine tune rights, such as granting SELECT privileges on tables or not at all, to users that don't need them. Be careful with WITH GRANT OPTION as seen on the net with cut and paste from it. If you use it, you just granted the user rights to grant other users rights.
SSH Tunnels
One reason you might not want to use Secimen A host = % wildcard (other than the obvious We are Risk Averse) is that
create user 'pfinferno'#'localhost' identified by 'thePassword';
is perfect for SSH Tunnels. You would be connecting through a cryptographically secure channel with PKI, and present yourself as if you are # localhost. This drastically reduces security exposure.
Why Can't I Connect?
Hopefully the below visual with little commentary can show why I named this section as I did.
drop user 'pfinferno'#'localhost';
drop user 'pfinferno'#'127.0.0.1';
drop user 'pfinferno'#'%';
drop user 'pfinferno'#'md21.newyork.comcastbusiness.net';
flush privileges; -- some say this is not necessary, I have found otherwise
create user 'pfinferno'#'localhost' identified by 'thePassword';
create user 'pfinferno'#'127.0.0.1' identified by 'thePassword';
create user 'pfinferno' identified by 'thePassword';
create user 'pfinferno'#'md21.newyork.comcastbusiness.net' identified by 'thePassword';
...
select user,host,password from mysql.user where user='pfinferno';
grant all on so_gibberish.* to 'pfinferno'#'%'; -- grant all rights on so_gibberish db
flush privileges; -- some say this is not necessary, I have found otherwise
Look at some grants.
show grants for 'pfinferno'#'localhost'; -- sandboxed. Can just log in and sit there
+-----------------------------------------------------------------------------------------+
| Grants for pfinferno#localhost |
+-----------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'pfinferno'#'localhost' IDENTIFIED BY PASSWORD '*74692AE70C53...' |
+-----------------------------------------------------------------------------------------+
show grants for 'pfinferno'#'127.0.0.1'; -- sandboxed. Can just log in and sit there
same as above
show grants for 'pfinferno'; -- wildcard % user, has all rights on so_gibberish;
+-----------------------------------------------------------------------------------------+
| Grants for pfinferno#% |
+-----------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'pfinferno'#'%' IDENTIFIED BY PASSWORD '*74692AE70C53...' |
| GRANT ALL PRIVILEGES ON `so_gibberish`.* TO 'pfinferno'#'%' |
+-----------------------------------------------------------------------------------------+
Note above that GRANT USAGE means at least you have the rights to log in and sit (sandboxed). But the wildcard %user also has all rights on so_gibberish db in its entirety.
Now I go to mysql prompt via mysql -u pfinferno -pthePassword
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
+--------------------+
mysql> select current_user(),user();
+---------------------+---------------------+
| current_user() | user() |
+---------------------+---------------------+
| pfinferno#localhost | pfinferno#localhost |
+---------------------+---------------------+
The attempted user (user()) was resolved to the same (current_user()). I was sandboxed, able to do basically nothing, except select now() til bored to death.
mysql> use so_gibberish;
ERROR 1044 (42000): Access denied for user 'pfinferno'#'localhost' to database 'so_gibberish'
quit mysql CLI as that user.
Now
drop user 'pfinferno'#'localhost';
drop user 'pfinferno'#'127.0.0.1';
drop user 'pfinferno'#'md21.newyork.comcastbusiness.net';
I just dropped three out of four of my pfinferno users
Go to mysql prompt via mysql -u pfinferno -pthePassword
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| so_gibberish |
+--------------------+
mysql> select current_user(),user();
+----------------+---------------------+
| current_user() | user() |
+----------------+---------------------+
| pfinferno#% | pfinferno#localhost |
+----------------+---------------------+
mysql> use so_gibberish;
Database changed
This shows that # the CLI (or any program) from any host, that the attempt to connect is first as presented by user and then as resolved to actual ( see the output from user() and current_user(), respectively).
So, perhaps oddly, as I dropped users, the EFFECTIVE RIGHTS increased as the user was resolved to a different one. This can be the subject of hours/days/weeks of debugging. Users can have no clue who they are really logged in as (resolved to), or why they can't, as each, by the way, can have a different password ! This is especially true with user root with several rows in mysql.user ... and considering that probably 50% of all mysql users connect with it, initially, as developers. Just a guestimate.
Well, first of all, you have to realize that this "root" (from MySQL) is different from the "root" user (from your computer).
When you log on MySQL database (as root firstly), you can create another user with least privileges. (Creating user: https://dev.mysql.com/doc/refman/5.1/en/create-user.html)
After that, you can give (grant) some privileges to this specific user. (Granting privileges: https://dev.mysql.com/doc/refman/5.1/en/grant.html)
You can, for instance, allow that some user will only select data from the tables. Another user can insert/update. And so on.
So, in your application, you can specify another user instead of root, as you can normally see.

Categories