Control external access pr Office 365 groups - azure-active-directory

Is it possible to enable/disable external access pr 365 groups from c#. I can see that some PowerShell cmd have a property called AllowGuestsUsers but I cant find anything in Microsoft Graph or similar?

These settings are managed with the Microsoft Graph group settings. Settings can be set tenant-wide (affecting all groups), and they can be set for a specific group (affecting only that group).
Below are a couple examples using the Microsoft Graph SDK for .NET to illustrate how you can change these settings:
Disable guest users accessing Office 365 groups
The example below updates the tenant-wide settings for guest users in Office 365 group to disable adding guest users, and disable existing guest users' access to group content. It is roughly the equivalent of the steps described in Use PowerShell to control guest access.
var groupSettingsName = "Group.Unified";
// Get the group settings
var groupSettingsResult = await graph.GroupSettings.Request().GetAsync();
var groupSettings = groupSettingsResult
.FirstOrDefault(s => s.DisplayName == groupSettingsName);
// If these settings don't exist, add them (with their default values)
if (groupSettings == null)
{
// Get the settings template
var groupSettingsTemplates = await graph.GroupSettingTemplates.Request().GetAsync();
var unifiedGroupSettingTemplate = groupSettingsTemplates
.First(g => g.DisplayName == groupSettingsName);
// Create a new setting based on the template
var newGroupSettings = new GroupSetting()
{
TemplateId = unifiedGroupSettingTemplate.Id,
Values = unifiedGroupSettingTemplate.Values.Select(
v => new SettingValue()
{
Name = v.Name,
Value = v.DefaultValue
})
};
// Create the settings
groupSettings = await graph.GroupSettings.Request().AddAsync(newGroupSettings);
}
// Update settings (if needed)
var settings = groupSettings.Values.ToDictionary(x => x.Name, x => x);
if (settings["AllowToAddGuests"].Value.ToLower() != "false"
|| settings["AllowGuestsToAccessGroups"].Value.ToLower() != "false")
{
settings["AllowGuestsToAccessGroups"].Value = "false";
settings["AllowToAddGuests"].Value = "false";
await graph.GroupSettings[groupSettings.Id].Request()
.UpdateAsync(new GroupSetting() { Values = settings.Values });
}
Disable adding guest users to a specific Office 365 group
In the example below, we are setting a setting on a specific group, to disable additional guest users from being added to the group.
var groupGuestSettingsName = "Group.Unified.Guest";
// Get the group in question
var groupResult = await graph.Groups.Request()
.Filter("displayName eq 'Test_Office365_group'").GetAsync();
var group = groupResult.First();
// Get the group's settings relating to guests
var groupSettingsResult = await graph.Groups[group.Id].Settings.Request().GetAsync();
var groupSettings = groupSettingsResult
.FirstOrDefault(s => s.DisplayName == groupGuestSettingsName);
// If these settings don't exist on the group, add them (with their default values)
if (groupSettings == null)
{
// Get the settings template
var groupSettingsTemplates = await graph.GroupSettingTemplates.Request().GetAsync();
var unifiedGroupGuestSettingTemplate = groupSettingsTemplates
.First(g => g.DisplayName == groupGuestSettingsName);
// Create a new group setting based on the template
var newGroupSettings = new GroupSetting()
{
TemplateId = unifiedGroupGuestSettingTemplate.Id,
Values = unifiedGroupGuestSettingTemplate.Values.Select(
v => new SettingValue()
{
Name = v.Name,
Value = v.DefaultValue
})
};
// Add these settings to the group
groupSettings = await graph.Groups[group.Id].Settings.Request().AddAsync(newGroupSettings);
}
// Change AllowToAddGuests setting to false, if needed
var settings = groupSettings.Values.ToDictionary(x => x.Name, x => x);
if (settings["AllowToAddGuests"].Value.ToLower() != "false")
{
settings["AllowToAddGuests"].Value = "False";
await graph.GroupSettings[groupSettings.Id].Request()
.UpdateAsync(new GroupSetting() { Values = settings.Values });
}

Related

multiple configuration databases for IdentityServer4

Iam using an identityServer4 with multiple Databases. I could so far use muliple databases with the user-store. I solve it as the follwoing code of a middleware:
public async Task Invoke(HttpContext context)
{
var req = context.Request;
if (req.Path == "/Account/Login" && req.Method.Equals("POST"))
{
if (req.Form.Keys.Contains("Input.Database") == false)
{
throw new InvalidOperationException("No database key was sent with this request: " + req.Path);
}
var lDatabasKey = req.Form["Input.Database"];
_configuration["ConnectionStrings:default"] = _configuration[$"ConnectionStrings:{lDatabasKey}"];
}
the configuration Object get updated, so the value ConnectionString:default get updated with the connectionstring i need to use.
This concept unfortunatley not working the the configuration database, which inistalised in the ConfigureServices in Startup.cs:
string connectionString = Configuration.GetConnectionString("default");
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.SignIn.RequireConfirmedEmail = false;
})
.AddEntityFrameworkStores<IdentityUserDbContext>()
.AddDefaultTokenProviders().AddClaimsPrincipalFactory<CentralHubClaimsPrincipalFactory>();
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.UserInteraction.LoginUrl = "/Account/Login";
options.UserInteraction.LogoutUrl = "/Account/Logout";
options.Authentication = new AuthenticationOptions()
{
CookieLifetime = TimeSpan.FromHours(10), // ID server cookie timeout set to 10 hours
CookieSlidingExpiration = true
};
})
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly));
}).AddAspNetIdentity<ApplicationUser>()
.AddProfileService<AspNetIdentityProfileService>();
using breaking point i can see that the connectionString:default in the middleware has correct value of the database i want to use. but it still uses the default connectionString which has been saved in the previous method in startup.cs.
So is it possible to use multiple configuration databases for the identityServer?
One option is to create your own EntityFramework configuration backend for IdentityServer by taking the existing source code and hack the queries made.
Then use a clientID prefix as the "database" identifier/selector, like
A0000-A9999 -> goes to Database A
B0000-B9999 -> goes to Database B
C0000-C9999 -> goes to Database C
or use a clientID like:
AAA:XXX where AAA is the client/DB identifier and XXX is the client within that database.
To select the database/connection string to use. To "fool" IdentityServer to believe that there is only one "database".
Having a structured clientID also makes it easier to debug and reason about the system.

Blazor cascading AuthorizeView Policy not working

I'm working on a new project that will have some in depth policies for what user can and can't access/see, with Identity Server 4.
I'm trying to use AuthorizeView with policies to hide options in my navigation, but the views are cascading, meaning I have something like this:
<MatNavMenu>
<MatNavItem Href="/home" Title="Home"><MatIcon Icon="#MatIconNames.Home"></MatIcon> Home</MatNavItem>
<MatNavItem Href="/claims" Title="Claims"><MatIcon Icon="#MatIconNames.Vpn_key"></MatIcon> Claims</MatNavItem>
<AuthorizeView Policy="#PolicyNames.IdentitySystemAccess">
<Authorized>
<AuthorizeView Policy="#PolicyNames.AccessManagement">
<Authorized>
<MatNavSubMenu #bind-Expanded="#_accessSubMenuState">
<MatNavSubMenuHeader>
<MatNavItem AllowSelection="false"> Access Management</MatNavItem>
</MatNavSubMenuHeader>
<MatNavSubMenuList>
<AuthorizeView Policy="#PolicyNames.User">
<Authorized>
<MatNavItem Href="users" Title="users"><MatIcon Icon="#MatIconNames.People"></MatIcon> Users</MatNavItem>
</Authorized>
</AuthorizeView>
<AuthorizeView Policy="#PolicyNames.Role">
<Authorized>
<MatNavItem Href="roles" Title="roles"><MatIcon Icon="#MatIconNames.Group"></MatIcon> Roles</MatNavItem>
</Authorized>
</AuthorizeView>
</MatNavSubMenuList>
</MatNavSubMenu>
</Authorized>
</AuthorizeView>
</Authorized>
</AuthorizeView>
I have checked that the claims required to fulfil the defined policies are present after the user is logged in, but for some reason the AuthorizeView isn't working.
I have updated my App.Razor to use AuthorizeRouteView. Any ideas as to why this is happening?
Note: I am using claims that are assigned to a role, but these are dynamic and I cannot use policy.RequireRole("my-role") in my policies, thus is use:
options.AddPolicy(PolicyNames.User, b =>
{
b.RequireAuthenticatedUser();
b.RequireClaim(CustomClaimTypes.User, "c", "r", "u", "d");
});
When my app runs, none of the items in the menu show up except for the home and claims items which are not protected by an AuthorizeView.
The issue was due to the current lack of support for Blazor to read claims the are sent as arrays.
e.g. user: ["c","r","u","d"]
Can't be read.
To rectify this you need to add ClaimsPrincipalFactory.
e.g.
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;
using System.Linq;
using System.Security.Claims;
using System.Text.Json;
using System.Threading.Tasks;
namespace YourNameSpace
{
public class ArrayClaimsPrincipalFactory<TAccount> : AccountClaimsPrincipalFactory<TAccount> where TAccount : RemoteUserAccount
{
public ArrayClaimsPrincipalFactory(IAccessTokenProviderAccessor accessor)
: base(accessor)
{ }
// when a user belongs to multiple roles, IS4 returns a single claim with a serialised array of values
// this class improves the original factory by deserializing the claims in the correct way
public async override ValueTask<ClaimsPrincipal> CreateUserAsync(TAccount account, RemoteAuthenticationUserOptions options)
{
var user = await base.CreateUserAsync(account, options);
var claimsIdentity = (ClaimsIdentity)user.Identity;
if (account != null)
{
foreach (var kvp in account.AdditionalProperties)
{
var name = kvp.Key;
var value = kvp.Value;
if (value != null &&
(value is JsonElement element && element.ValueKind == JsonValueKind.Array))
{
claimsIdentity.RemoveClaim(claimsIdentity.FindFirst(kvp.Key));
var claims = element.EnumerateArray()
.Select(x => new Claim(kvp.Key, x.ToString()));
claimsIdentity.AddClaims(claims);
}
}
}
return user;
}
}
}
Then register this in your program/startup(depending on if you use .core hosted or not)like so:
builder.Services.AddOidcAuthentication(options =>
{
builder.Configuration.Bind("oidc", options.ProviderOptions);
})
.AddAccountClaimsPrincipalFactory<ArrayClaimsPrincipalFactory<RemoteUserAccount>>();
After understanding the problem with Steve I did the following solution.
Useful for those who follow Cris Sainty's documentation
I update my method to parse claims from jwt to separate all claim's array!
private IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
{
var claims = new List<Claim>();
var payload = jwt.Split('.')[1];
var jsonBytes = ParseBase64WithoutPadding(payload);
var keyValuePairs = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonBytes);
keyValuePairs.TryGetValue(ClaimTypes.Role, out object roles);
if (roles != null)
{
if (roles.ToString().Trim().StartsWith("["))
{
var parsedRoles = JsonSerializer.Deserialize<string[]>(roles.ToString());
foreach (var parsedRole in parsedRoles)
{
claims.Add(new Claim(ClaimTypes.Role, parsedRole));
}
}
else
{
claims.Add(new Claim(ClaimTypes.Role, roles.ToString()));
}
keyValuePairs.Remove(ClaimTypes.Role);
}
claims.AddRange(keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString())));
for (int i = 0; i < claims.Count; i++)
{
var name = claims[i].Type;
var value = claims[i].Value;
if (value != null && value.StartsWith("["))
{
var array = JsonSerializer.Deserialize<List<string>>(value);
claims.Remove(claims[i]);
foreach (var item in array)
{
claims.Add(new Claim(name, item));
}
}
}
return claims;
}
Adding to the above answers you can avoid it becoming array claims by having different keys for claims creation like this:
var claims = new[]
{
new Claim("UserType1", "c"),
new Claim("UserType2", "r")
....
};

Programming c# code to create a SSRS data driven subscription

I have SSRS report with parameters under SQL Reporting service 2012 standard edition. I like to export to excel and send as an attachment in the email to different receipt and receipt comes from some SQL query that means it is dynamic.
Data-driven subscription can do this but I have SQL Server 2012 Standard edition which does not support data-driven subscription and I can not upgrade, so I am looking for any code which can do the similar job like a data-driven subscription.
I found this link which has the solution to my issue.
http://jaliyaudagedara.blogspot.com/2012/10/creating-data-driven-subscription.html
when I try this code under visual studio 2015 "Class Library" project by adding service reference "http://mylocalserver:81/reportserver/ReportService2010.asmx" I am getting an error on this line of code.
ReportingService2010SoapClient rs= new ReportingService2010SoapClient();
Additional information about the error: Could not find default endpoint element that references contract 'ReportService2010.ReportingService2010Soap' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element.
After spending enough time to make it work with "Class Library" project, I decided to do the code under web service project by adding the web service reference. with some trial and error finally, I got the working code here under web service project. below code works on my local machine which has Sql server 2012 enterprise edition but it gives me the same error saying "Data-driven subscriptions to reports" is not supported in this edition of Reporting Services" on my company server which has SQL server 2012 standard edition.
public void DoWork()
{
ReportingService2010 rs = new ReportingService2010();
rs.Credentials = CredentialCache.DefaultCredentials;
// rs.Url = "http://mylocalserver:81/reportserver/ReportService2010.asmx";
rs.Url = "http://companyserver/reportserver/ReportService2010.asmx";
var reportPath = "/CYTYC Reports/";
string report = $"{reportPath}AllContactCIPPointsReport";
string description = "Programmatic Data Driven Subscription \"Report Server Email\" ";
//set extension as Windows File Share
ExtensionSettings settings = new ExtensionSettings();
settings.Extension = "Report Server Email";
// Set the extension parameter values.
var extensionParams = new ParameterValueOrFieldReference[8];
// var to = new ParameterFieldReference { ParameterName = "TO", FieldAlias = "PARAMS" }; // Data-driven.
var to = new ParameterValue { Name = "TO", Value = "example#gmail.com" }; // Data-driven.
extensionParams[0] = to;
var replyTo = new ParameterValue { Name = "ReplyTo", Value = "example#gmail.com" };
extensionParams[1] = replyTo;
var includeReport = new ParameterValue { Name = "IncludeReport", Value = "False" };
extensionParams[2] = includeReport;
var renderFormat = new ParameterValue { Name = "RenderFormat", Value = "HTML4.0" };
extensionParams[3] = renderFormat;
var priority = new ParameterValue { Name = "Priority", Value = "NORMAL" };
extensionParams[4] = priority;
var subject = new ParameterValue { Name = "Subject", Value = "Subsribed Report" };
extensionParams[5] = subject;
var comment = new ParameterValue { Name = "Comment", Value = "Here is the link to your report." };
extensionParams[6] = comment;
var includeLink = new ParameterValue { Name = "IncludeLink", Value = "True" };
extensionParams[7] = includeLink;
settings.ParameterValues = extensionParams;
// Create the data source for the delivery query.
var delivery = new DataSource { Name = "" };
var dataSourceDefinition = new DataSourceDefinition
{
ConnectString = "Data Source=CYTYC-LIVE;Initial Catalog=yourdatabasename",
CredentialRetrieval = CredentialRetrievalEnum.Store,
Enabled = true,
EnabledSpecified = true,
Extension = "SQL",
ImpersonateUserSpecified = false,
UserName = "username",
Password = "password"
};
delivery.Item = dataSourceDefinition;
// Create the data set for the delivery query.
var dataSetDefinition = new DataSetDefinition
{
AccentSensitivitySpecified = false,
CaseSensitivitySpecified = false,
KanatypeSensitivitySpecified = false,
WidthSensitivitySpecified = false
};
var queryDefinition = new QueryDefinition
{
CommandText = #"Your select * from Query",
CommandType = "Text",
Timeout = 45,
TimeoutSpecified = true
};
dataSetDefinition.Query = queryDefinition;
var results = new DataSetDefinition();
var oServerInfoHeader = new ServerInfoHeader();
var oTrustedUserHeader = new TrustedUserHeader();
bool changed;
string[] paramNames;
try
{
results = rs.PrepareQuery(delivery, dataSetDefinition, out changed, out paramNames);//.PrepareQuery(oTrustedUserHeader, delivery, dataSetDefinition, out results, out changed,out paramNames);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
var dataRetrieval = new DataRetrievalPlan { DataSet = results, Item = dataSourceDefinition };
// Set the event type and match data for the delivery.
const string eventType = "TimedSubscription";
const string matchData = "<ScheduleDefinition><StartDateTime>2018-06-01T14:00:00-07:00</StartDateTime><WeeklyRecurrence><WeeksInterval>1</WeeksInterval><DaysOfWeek><Monday>True</Monday><Tuesday>True</Tuesday><Wednesday>True</Wednesday><Thursday>True</Thursday><Friday>True</Friday></DaysOfWeek></WeeklyRecurrence></ScheduleDefinition>";
//const string eventType = "SnapshotUpdated";
//const string matchData = null;
//// Set the report parameter values.
//var parameters = new ParameterValueOrFieldReference[1];
//// i am retrieving value EMAIL from database and I am passing that value as my report parameter value
//var reportparam = new ParameterFieldReference { ParameterName = "yourreportparametername", FieldAlias = "PARAMS" }; // Data-driven.
//parameters[0] = reportparam;
var parameters = new ParameterValue[1];
var reportparam = new ParameterValue {Name = "yourreportparametername", Value = "yourreportparametervalue"};
parameters[0] = reportparam;
string subscriptionId = "";
try
{
subscriptionId = rs.CreateDataDrivenSubscription(report, settings, dataRetrieval, description, eventType, matchData, parameters);
//(oTrustedUserHeader, report, settings, dataRetrieval,description, eventType, matchData, parameters,out subscriptionId);
}
catch (System.Web.Services.Protocols.SoapException ex)
{
Console.WriteLine(ex.Detail.InnerText.ToString(CultureInfo.InvariantCulture));
}
}
You don't say why you need the Data Driven subscriptions - a regular SSRS subscription can e-mail an Excel report with set or default parameters.
There aren't any third party tools that I know of that emulates the Data Driven subscriptions but there have been some users who have created their own.
If you just want to trigger a subscription based on criteria, you could just use an SSIS job to run the query to determine whether to send or not and trigger the subscription if so.
Something like Data Driven Subscriptions SSRS Standard Edition 2008
If you need something more complicated (like varying TO/CC recipients, changing parameter values...), you'll need to do a bit more programming. Here's a couple things to get started with the theory and code:
https://www.mssqltips.com/sqlservertip/4249/simulate-reporting-services-data-driven-subscriptions-on-unsupported-editions/
http://www.sqlservercentral.com/articles/Reporting+Services+(SSRS)/163119/

Give view access permission to DNN module when copying the module from another page

I'm copying the list of modules from another page in DNN. While doing this I have to set the view permission for all users to view the modules. Even the other page(copying from) module setting is different. Below is the example where I set other properties.
ModuleInfo newModule = new ModuleInfo();
0newModule.ModuleDefID = module.ModuleDefID;
newModule.AllTabs = false;
newModule.PortalID = PortalId;
newModule.ContentItemId = module.ContentItemId;
newModule.ModuleTitle = module.ModuleTitle;
newModule.PaneName = module.PaneName;
newModule.TabID = NewTabInfo.TabID;
newModule.ContainerSrc = module.ContainerSrc;
newModule.ModuleOrder = module.ModuleOrder;
newModule.DisplayPrint = module.DisplayPrint;
newModule.DisplayTitle = module.DisplayTitle;
newModule.IsShareable = module.IsShareable;
newModule.IsShareableViewOnly = module.IsShareableViewOnly;
newModule.ModulePermissions=module.ModulePermissions;
newModule.IsWebSlice = module.IsWebSlice;
newModule.WebSliceTitle = module.WebSliceTitle;
newModule.Footer = module.Footer;
newModule.Header = module.Header;
Here a snippet to dynamically add role permissions to a dotnetnuke module in code behind.
//get the current ModuleInfo
ModuleInfo newModule = ModuleController.Instance.GetModule(ModuleId, TabId, false);
//or create a new module
ModuleInfo newModule = new ModuleInfo();
//clear the old permissions
newModule.ModulePermissions.Clear();
//add admin view permission
ModulePermissionInfo modulePermissionInfo1 = new ModulePermissionInfo();
modulePermissionInfo1.ModuleID = ModuleId;
modulePermissionInfo1.AllowAccess = true;
//view permission id, 1 = view, 2 = edit
modulePermissionInfo1.PermissionID = 1;
//administrator role id (from Roles table in dnn database)
modulePermissionInfo1.RoleID = 0;
//add admin edit permission
ModulePermissionInfo modulePermissionInfo2 = new ModulePermissionInfo();
modulePermissionInfo2.ModuleID = ModuleId;
modulePermissionInfo2.AllowAccess = true;
//view permission id, 1 = view, 2 = edit
modulePermissionInfo2.PermissionID = 2;
//administrator role id (from Roles table in dnn database)
modulePermissionInfo2.RoleID = 0;
//add all users view permission
ModulePermissionInfo modulePermissionInfo3 = new ModulePermissionInfo();
modulePermissionInfo3.ModuleID = ModuleId;
modulePermissionInfo3.AllowAccess = true;
//edit permission id, 1 = view, 2 = edit
modulePermissionInfo3.PermissionID = 1;
//all users role id (from Roles table in dnn database)
modulePermissionInfo3.RoleID = -1;
//add the ModulePermissionInfo to the module
newModule.ModulePermissions.Add(modulePermissionInfo1);
newModule.ModulePermissions.Add(modulePermissionInfo2);
newModule.ModulePermissions.Add(modulePermissionInfo3);
//save the permissions
ModulePermissionController.SaveModulePermissions(newModule);
//clear the dnn cache (if it is the current module, not a new one)
DotNetNuke.Common.Utilities.DataCache.ClearModuleCache(TabId);
DotNetNuke.Common.Utilities.DataCache.ClearTabsCache(PortalId);
DotNetNuke.Common.Utilities.DataCache.ClearPortalCache(PortalId, false);

Initializing a NotificationController in DNN 7

How do you initialize a NotificationController in DNN 7.1.2?
I've tried:
var nc = new DotNetNuke.Services.Social.Notifications.NotificationController();
However this is empty and has no methods to call... Am I initializing the wrong thing?
Surely there should be something in there other than ToString, GetType, Equals and GetHashCode
I need to be able to create NotificationTypes and create Notifications.
Thanks
You can use NotificationsController.Instance.SendNotification method to send notifications.
Here is the example:
var notificationType = NotificationsController.Instance.GetNotificationType("HtmlNotification");
var portalSettings = PortalController.GetCurrentPortalSettings();
var sender = UserController.GetUserById(portalSettings.PortalId, portalSettings.AdministratorId);
var notification = new Notification {NotificationTypeID = notificationType.NotificationTypeId, Subject = subject, Body = body, IncludeDismissAction = true, SenderUserID = sender.UserID};
NotificationsController.Instance.SendNotification(notification, portalSettings.PortalId, null, new List<UserInfo> { user });
This will send notification to a specific user.
If you need to create your own Notification type use the code below as a guide, it will create a NotificationType "GroupApprovedNotification". This is from core groups module.
type = new NotificationType { Name = "GroupApprovedNotification", Description = "Group Approved Notification", DesktopModuleId = deskModuleId };
if (NotificationsController.Instance.GetNotificationType(type.Name) == null)
{
NotificationsController.Instance.CreateNotificationType(type);
}

Resources