Skip to content

Commit

Permalink
Merge pull request #102 from jpfulton:group-management
Browse files Browse the repository at this point in the history
Group-management
  • Loading branch information
jpfulton authored Sep 5, 2023
2 parents ebaf6e8 + 6be533d commit cd40b30
Show file tree
Hide file tree
Showing 35 changed files with 1,704 additions and 1,197 deletions.
229 changes: 229 additions & 0 deletions ng-resume-api/Functions/GroupFunctions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Jpf.NgResume.Api.Auth;
using Jpf.NgResume.Api.MicrosoftGraph;
using Jpf.NgResume.Api.Models;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.Functions.Worker.Extensions.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
using Microsoft.Graph;
using Microsoft.OpenApi.Models;

namespace Jpf.NgResume.Api.Functions
{

public class GroupFunctions {
private readonly GraphServiceClient graphClient;

public GroupFunctions(GraphServiceClient graphClient) {
this.graphClient = graphClient;
}

[Function("GroupsGetAll")]
[OpenApiOperation(operationId: "GetAll", tags: new[] { "groups" })]
[OpenApiSecurity(
"Bearer",
SecuritySchemeType.Http,
Scheme = OpenApiSecuritySchemeType.Bearer,
BearerFormat = "JWT",
In = OpenApiSecurityLocationType.Header)]
[OpenApiResponseWithBody(
statusCode: HttpStatusCode.OK,
contentType: "application/json; charset=utf-8",
bodyType: typeof(List<Models.Group>),
Description = "A list of application groups."
)]
[OpenApiResponseWithBody(
statusCode: HttpStatusCode.Unauthorized,
contentType: "application/problem+json; charset=utf-8",
bodyType: typeof(CustomProblemDetails),
Description = "Problem details of an unauthorized access result."
)]
public async Task<HttpResponseData> GetAllAsync(
[HttpTrigger(
AuthorizationLevel.Anonymous,
"get",
Route = "groups"
)
]
HttpRequestData request,
FunctionContext functionContext)
{
var log = functionContext.GetLogger<GroupFunctions>();

var (authorized, authorizationResponse) =
await request.AuthenticateThenAuthorizeWithGroup(
functionContext,
graphClient,
log,
"SiteOwners");
if (!authorized) return authorizationResponse;

var groups = await graphClient.Groups
.Request()
.SelectGroupProperties()
.GetAsync();

// Iterate over all the groups in the directory
var pageIterator = PageIterator<Microsoft.Graph.Group>
.CreatePageIterator(
graphClient,
groups,
// Callback executed for each group in the collection
(group) =>
{
return true;
}
/*
// Used to configure subsequent page requests
(req) =>
{
return req;
}
*/
);

await pageIterator.IterateAsync();

var appGroups = new List<Models.Group>();
groups.ToList().ForEach(group =>
{
appGroups.Add(group.FromMicrosoftGraph());
});

var response = request.CreateResponse();
await response.WriteAsJsonAsync(appGroups);

return response;
}

[Function("GroupsAddUserToGroup")]
[OpenApiOperation(operationId: "AddUser", tags: new[] { "groups" })]
[OpenApiSecurity(
"Bearer",
SecuritySchemeType.Http,
Scheme = OpenApiSecuritySchemeType.Bearer,
BearerFormat = "JWT",
In = OpenApiSecurityLocationType.Header)]
[OpenApiParameter(
"groupId",
In = ParameterLocation.Path,
Required = true,
Type = typeof(string),
Description = "Id of the group."
)]
[OpenApiRequestBody(
contentType: "application/json; charset=utf-8",
bodyType: typeof(Models.User),
Description = "User to be added to the group.",
Required = true
)]
[OpenApiResponseWithoutBody(
statusCode: HttpStatusCode.NoContent,
Description = "User has been added to the group."
)]
[OpenApiResponseWithBody(
statusCode: HttpStatusCode.Unauthorized,
contentType: "application/problem+json; charset=utf-8",
bodyType: typeof(CustomProblemDetails),
Description = "Problem details of an unauthorized access result."
)]
public async Task<HttpResponseData> AddUserToGroupAsync(
[HttpTrigger(
AuthorizationLevel.Anonymous,
"post",
Route = "groups/{groupId}/users"
)
]
HttpRequestData request,
[FromBody] Models.User user,
string groupId,
FunctionContext functionContext)
{
var log = functionContext.GetLogger<GroupFunctions>();

var (authorized, authorizationResponse) =
await request.AuthenticateThenAuthorizeWithGroup(
functionContext,
graphClient,
log,
"SiteOwners");
if (!authorized) return authorizationResponse;

var directoryObject = new DirectoryObject
{
Id = user.Id,
};
await graphClient.Groups[groupId].Members.References.Request().AddAsync(directoryObject);

var response = request.CreateResponse(HttpStatusCode.NoContent);
return response;
}

[Function("GroupsRemoveUserFromGroup")]
[OpenApiOperation(operationId: "RemoveUser", tags: new[] { "groups" })]
[OpenApiSecurity(
"Bearer",
SecuritySchemeType.Http,
Scheme = OpenApiSecuritySchemeType.Bearer,
BearerFormat = "JWT",
In = OpenApiSecurityLocationType.Header
)]
[OpenApiParameter(
"groupId",
In = ParameterLocation.Path,
Required = true,
Type = typeof(string),
Description = "Id of the group."
)]
[OpenApiParameter(
"userId",
In = ParameterLocation.Path,
Required = true,
Type = typeof(string),
Description = "Id of the user."
)]
[OpenApiResponseWithoutBody(
statusCode: HttpStatusCode.NoContent,
Description = "User has been removed from the group."
)]
[OpenApiResponseWithBody(
statusCode: HttpStatusCode.Unauthorized,
contentType: "application/problem+json; charset=utf-8",
bodyType: typeof(CustomProblemDetails),
Description = "Problem details of an unauthorized access result."
)]
public async Task<HttpResponseData> RemoveUserFromGroupAsync(
[HttpTrigger(
AuthorizationLevel.Anonymous,
"delete",
Route = "groups/{groupId}/users/{userId}"
)
]
HttpRequestData request,
string groupId,
string userId,
FunctionContext functionContext)
{
var log = functionContext.GetLogger<GroupFunctions>();

var (authorized, authorizationResponse) =
await request.AuthenticateThenAuthorizeWithGroup(
functionContext,
graphClient,
log,
"SiteOwners");
if (!authorized) return authorizationResponse;

await graphClient.Groups[groupId].Members[userId].Reference.Request().DeleteAsync();

var response = request.CreateResponse(HttpStatusCode.NoContent);
return response;
}
}
}
85 changes: 77 additions & 8 deletions ng-resume-api/Functions/UserFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@
using Jpf.NgResume.Api.MicrosoftGraph;
using Jpf.NgResume.Api.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
using Microsoft.Extensions.Logging;
using Microsoft.Graph;
using Microsoft.OpenApi.Models;

namespace Jpf.NgResume.Api.Functions {
namespace Jpf.NgResume.Api.Functions
{

public class UserFunctions {
private readonly GraphServiceClient graphClient;
Expand All @@ -36,7 +35,7 @@ public UserFunctions(GraphServiceClient graphClient) {
statusCode: HttpStatusCode.OK,
contentType: "application/json; charset=utf-8",
bodyType: typeof(List<Models.User>),
Description = "A user representing the current user's profile."
Description = "A list of application users."
)]
[OpenApiResponseWithBody(
statusCode: HttpStatusCode.Unauthorized,
Expand All @@ -54,7 +53,7 @@ public async Task<HttpResponseData> GetAllAsync(
HttpRequestData request,
FunctionContext functionContext)
{
var log = functionContext.GetLogger<TestFunctions>();
var log = functionContext.GetLogger<UserFunctions>();

var (authorized, authorizationResponse) =
await request.AuthenticateThenAuthorizeWithGroup(
Expand All @@ -65,9 +64,9 @@ await request.AuthenticateThenAuthorizeWithGroup(
if (!authorized) return authorizationResponse;

var users = await graphClient.Users
.Request()
.SelectUserProperties()
.GetAsync();
.Request()
.SelectUserProperties()
.GetAsync();

// Iterate over all the users in the directory
var pageIterator = PageIterator<Microsoft.Graph.User>
Expand Down Expand Up @@ -102,5 +101,75 @@ await request.AuthenticateThenAuthorizeWithGroup(

return response;
}

[Function("UsersGetUserGroupMembership")]
[OpenApiOperation(operationId: "GetUserGroupMembership", tags: new[] { "users" })]
[OpenApiSecurity(
"Bearer",
SecuritySchemeType.Http,
Scheme = OpenApiSecuritySchemeType.Bearer,
BearerFormat = "JWT",
In = OpenApiSecurityLocationType.Header)]
[OpenApiParameter(
"userId",
In = ParameterLocation.Path,
Required = true,
Type = typeof(string),
Description = "Id of the user."
)]
[OpenApiResponseWithBody(
statusCode: HttpStatusCode.OK,
contentType: "application/json; charset=utf-8",
bodyType: typeof(List<Models.Group>),
Description = "A list of application groups."
)]
[OpenApiResponseWithBody(
statusCode: HttpStatusCode.Unauthorized,
contentType: "application/problem+json; charset=utf-8",
bodyType: typeof(CustomProblemDetails),
Description = "Problem details of an unauthorized access result."
)]
public async Task<HttpResponseData> GetUserGroupMembershipAsync(
[HttpTrigger(
AuthorizationLevel.Anonymous,
"get",
Route = "users/{userId}/groups"
)
]
HttpRequestData request,
string userId,
FunctionContext functionContext)
{
var log = functionContext.GetLogger<GroupFunctions>();

var (authorized, authorizationResponse) =
await request.AuthenticateThenAuthorizeWithGroup(
functionContext,
graphClient,
log,
"SiteOwners");
if (!authorized) return authorizationResponse;

var memberships = await graphClient.Users[userId]
.MemberOf
.Request()
.GetAsync();

var graphGroups = memberships
.Where(p => p.GetType() == typeof(Microsoft.Graph.Group))
.Cast<Microsoft.Graph.Group>()
.ToList();

var groups = new List<Models.Group>();
graphGroups.ForEach((msGroup) =>
{
groups.Add(msGroup.FromMicrosoftGraph());
});

var response = request.CreateResponse();
await response.WriteAsJsonAsync(groups);

return response;
}
}
}
35 changes: 35 additions & 0 deletions ng-resume-api/MicrosoftGraph/GroupExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using System.Linq.Expressions;
using Microsoft.Graph;

namespace Jpf.NgResume.Api.MicrosoftGraph
{
public static class GroupExtensions {

public static IGraphServiceGroupsCollectionRequest SelectGroupProperties(
this IGraphServiceGroupsCollectionRequest groups
)
{
return groups.Select(SelectGroup());
}

public static IGroupRequest SelectGroupProperties(
this IGroupRequest groups
)
{
return groups.Select(SelectGroup());
}

private static Expression<Func<Group, object>> SelectGroup() {
Expression<Func<Group, object>> f = g => new
{
g.Id,
g.DisplayName,
g.SecurityEnabled
};

return f;
}

}
}
Loading

0 comments on commit cd40b30

Please sign in to comment.