Skip to content

Commit bd92d0a

Browse files
abrilgzzdanielluo-msft
authored andcommitted
Wording update and display correct message in Select Destination onboarding step
1 parent c054693 commit bd92d0a

File tree

12 files changed

+93
-25
lines changed

12 files changed

+93
-25
lines changed

Service/GroupMembershipManagement/Hosts/WebApi/Services.Messages/Requests/GetGroupOnboardingStatusRequest.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,12 @@ namespace Services.Messages.Requests
88
public class GetGroupOnboardingStatusRequest : RequestBase
99
{
1010
public Guid GroupId { get; set; }
11+
public string UserIdentity { get; }
12+
13+
public GetGroupOnboardingStatusRequest(Guid groupId, string userIdentity)
14+
{
15+
GroupId = groupId;
16+
UserIdentity = userIdentity;
17+
}
1118
}
1219
}

Service/GroupMembershipManagement/Hosts/WebApi/Services.WebApi/GetGroupOnboardingStatusHandler.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public GetGroupOnboardingStatusHandler(ILoggingRepository loggingRepository,
2929
protected override async Task<GetGroupOnboardingStatusResponse> ExecuteCoreAsync(GetGroupOnboardingStatusRequest request)
3030
{
3131
var isAppIdOwner = await _graphGroupRepository.IsAppIDOwnerOfGroup(_gmmAppId, request.GroupId);
32+
var isUserOwner = await _graphGroupRepository.IsEmailRecipientOwnerOfGroupAsync(request.UserIdentity, request.GroupId);
3233
var syncJobExists = await _syncJobRepository.GetSyncJobByObjectIdAsync(request.GroupId);
3334
bool isOnboarded = syncJobExists != null;
3435

@@ -38,13 +39,17 @@ protected override async Task<GetGroupOnboardingStatusResponse> ExecuteCoreAsync
3839
{
3940
response.Status = OnboardingStatus.Onboarded;
4041
}
41-
else if (isAppIdOwner)
42+
else if (!isAppIdOwner)
4243
{
43-
response.Status = OnboardingStatus.ReadyForOnboarding;
44+
response.Status = OnboardingStatus.AppIdNotOwner;
45+
}
46+
else if (!isUserOwner)
47+
{
48+
response.Status = OnboardingStatus.UserNotOwner;
4449
}
4550
else
4651
{
47-
response.Status = OnboardingStatus.NotReadyForOnboarding;
52+
response.Status = OnboardingStatus.ReadyForOnboarding;
4853
}
4954

5055
return response;

Service/GroupMembershipManagement/Hosts/WebApi/WebApi.Tests/DestinationsControllerTests.cs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
using Models;
1111
using Microsoft.Extensions.Options;
1212
using Common.DependencyInjection;
13+
using System.Security.Claims;
14+
using WebApi.Models;
1315

1416
namespace Services.Tests
1517
{
@@ -55,10 +57,12 @@ public void Initialize()
5557

5658
_destinationController = new DestinationController(_searchDestinationsHandler, _getGroupEndpointsHandler, _getGroupOnboardingStatusHandler)
5759
{
58-
ControllerContext = new ControllerContext
60+
ControllerContext = CreateControllerContext(new List<Claim>
5961
{
60-
HttpContext = _context
61-
}
62+
new Claim(ClaimTypes.Name, "user@domain.com"),
63+
new Claim(ClaimTypes.Role, Roles.JOB_CREATOR),
64+
new Claim("http://schemas.microsoft.com/identity/claims/objectidentifier", Guid.NewGuid().ToString())
65+
})
6266
};
6367

6468
_groupTypes = new List<string>
@@ -149,6 +153,7 @@ public async Task GetGroupReadyForOnboardingStatusAsync()
149153
Guid groupNotOnboarded = Guid.NewGuid();
150154
_syncJobRepository.Setup(x => x.GetSyncJobByObjectIdAsync(It.IsAny<Guid>())).ReturnsAsync((SyncJob)null);
151155
_graphGroupRepository.Setup(x => x.IsAppIDOwnerOfGroup(It.IsAny<string>(), It.Is<Guid>(g => g == groupNotOnboarded))).ReturnsAsync(true);
156+
_graphGroupRepository.Setup(x => x.IsEmailRecipientOwnerOfGroupAsync(It.IsAny<string>(), It.Is<Guid>(g => g == groupNotOnboarded))).ReturnsAsync(true);
152157

153158
var response = await _destinationController.GetGroupOnboardingStatusAsync(groupNotOnboarded);
154159
var result = response.Result as OkObjectResult;
@@ -161,6 +166,26 @@ public async Task GetGroupReadyForOnboardingStatusAsync()
161166
Assert.IsNotNull(onboardingStatus);
162167
Assert.AreEqual(OnboardingStatus.ReadyForOnboarding, onboardingStatus);
163168
}
169+
170+
private ControllerContext CreateControllerContext(HttpContext httpContext)
171+
{
172+
return new ControllerContext { HttpContext = httpContext };
173+
}
174+
175+
private ControllerContext CreateControllerContext(List<Claim> claims)
176+
{
177+
return new ControllerContext { HttpContext = CreateHttpContext(claims) };
178+
}
179+
180+
private HttpContext CreateHttpContext(List<Claim> claims)
181+
{
182+
var identity = new ClaimsIdentity(claims, "TestAuthType");
183+
var principal = new ClaimsPrincipal(identity);
184+
var httpContext = new DefaultHttpContext();
185+
httpContext.User = principal;
186+
187+
return httpContext;
188+
}
164189
}
165190
}
166191

Service/GroupMembershipManagement/Hosts/WebApi/WebApi/Controllers/v1/Destination/DestinationController.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Services.Contracts;
1010
using Services.Messages.Requests;
1111
using Services.Messages.Responses;
12+
using System.Security.Claims;
1213
using AzureADGroup = Models.AzureADGroup;
1314

1415
namespace WebApi.Controllers.v1.Destination
@@ -52,8 +53,24 @@ public async Task<ActionResult<List<string>>> GetGroupEndpointsAsync(Guid groupI
5253
[HttpGet("groups/{groupId}/onboarding-status")]
5354
public async Task<ActionResult<GetGroupOnboardingStatusResponse>> GetGroupOnboardingStatusAsync(Guid groupId)
5455
{
55-
var response = await _getGroupOnboardingStatusHandler.ExecuteAsync(new GetGroupOnboardingStatusRequest { GroupId = groupId });
56-
return Ok(response.Status);
56+
try
57+
{
58+
var user = User;
59+
var claimsIdentity = User.Identity as ClaimsIdentity;
60+
var userId = claimsIdentity?.Claims.FirstOrDefault(c => c.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value;
61+
62+
if (string.IsNullOrEmpty(userId))
63+
{
64+
return new ForbidResult();
65+
}
66+
67+
var response = await _getGroupOnboardingStatusHandler.ExecuteAsync(new GetGroupOnboardingStatusRequest (groupId, userId));
68+
return Ok(response.Status);
69+
}
70+
catch (Exception ex)
71+
{
72+
return Problem(statusCode: (int)System.Net.HttpStatusCode.InternalServerError, detail: $"An error occurred: ${ex}");
73+
}
5774
}
5875
}
5976
}

Service/GroupMembershipManagement/Models/OnboardingStatus.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public enum OnboardingStatus
77
{
88
Onboarded,
99
ReadyForOnboarding,
10-
NotReadyForOnboarding
10+
AppIdNotOwner,
11+
UserNotOwner
1112
}
1213
}

UI/web-app/src/components/SelectDestination/SelectDestination.base.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,15 @@ export const SelectDestinationBase: React.FunctionComponent<ISelectDestinationPr
7575
};
7676

7777
const addGroupOwnerLink: string = `https://portal.azure.com/#view/Microsoft_AAD_IAM/GroupDetailsMenuBlade/~/Owners/groupId/${selectedDestination?.id}/menuId/`
78-
const ownershipWarning = onboardingStatus === OnboardingStatus.NotReadyForOnboarding ? (
78+
const appIdNotOwnerWarning = onboardingStatus === OnboardingStatus.AppIdNotOwner ? (
7979
<div className={classNames.ownershipWarning}>
80-
{strings.ManageMembership.labels.ownershipWarning} <a href={addGroupOwnerLink}>{strings.ManageMembership.labels.clickHere}</a>.
80+
{strings.ManageMembership.labels.appIdNotOwnerWarning} <a href={addGroupOwnerLink}>{strings.ManageMembership.labels.clickHere}</a>.
81+
</div>
82+
) : null;
83+
84+
const userNotOwnerWarning = onboardingStatus === OnboardingStatus.UserNotOwner ? (
85+
<div className={classNames.ownershipWarning}>
86+
{strings.ManageMembership.labels.userNotOwnerWarning}
8187
</div>
8288
) : null;
8389

@@ -202,7 +208,8 @@ export const SelectDestinationBase: React.FunctionComponent<ISelectDestinationPr
202208
Yammer
203209
</ActionButton>
204210
)}
205-
{ownershipWarning}
211+
{appIdNotOwnerWarning}
212+
{userNotOwnerWarning}
206213
{alreadyOnboardedWarning}
207214
</div>
208215
) : null}

UI/web-app/src/models/GroupOnboardingStatus.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ export type GroupOnboardingStatus = {
88
export enum OnboardingStatus {
99
Onboarded,
1010
ReadyForOnboarding,
11-
NotReadyForOnboarding
11+
AppIdNotOwner,
12+
UserNotOwner
1213
}

UI/web-app/src/pages/JobDetails/JobDetails.base.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ const MembershipDetails: React.FunctionComponent<IContentProps> = (
252252
<div className={classNames.card}>
253253
<div>
254254
<Text className={classNames.title} block>
255-
{strings.JobDetails.labels.pageTitle}
255+
{strings.JobDetails.labels.pageTitle} - {props.job.targetGroupName}
256256
</Text>
257257
</div>
258258
{/* <div> // Hidden until feature is enabled

UI/web-app/src/services/localization/IStrings.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,8 @@ export type IStrings = {
279279
noResultsFound: string;
280280
appsUsed: string;
281281
outlookWarning: string;
282-
ownershipWarning: string;
282+
appIdNotOwnerWarning: string;
283+
userNotOwnerWarning: string;
283284
step2title: string;
284285
step2description: string;
285286
advancedQuery: string;

UI/web-app/src/services/localization/i18n/locales/en/translations.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ export const strings: IStrings = {
156156
lastModifiedby: 'User who made the last change to this job.',
157157
startDate: 'Date of the onboarding of this job into GMM.',
158158
endDate: 'Date of the last run of this job.',
159-
type: 'Sync type.',
159+
type: 'The destination\'s type. Currently GMM only manages the membership of Group destinations. In the future it will also support Teams Channel membership.',
160160
id: 'Object ID of the destination group.',
161161
lastRun: 'Time of the last run of this job.',
162162
nextRun: 'Next run time for this job.',
@@ -182,7 +182,7 @@ export const strings: IStrings = {
182182
editButton: 'Edit',
183183
},
184184
JobsList: {
185-
listOfMemberships: 'List of memberships',
185+
listOfMemberships: 'Managed groups',
186186
ShimmeredDetailsList: {
187187
toggleSelection: 'Toggle selection',
188188
toggleAllSelection: 'Toggle selection for all items',
@@ -284,7 +284,8 @@ export const strings: IStrings = {
284284
noResultsFound: 'No results found',
285285
appsUsed: 'This group uses the following apps:',
286286
outlookWarning: 'There are important settings that should be considered before sending email to this Outlook group. Follow the instructions on your organization.',
287-
ownershipWarning: 'Warning: GMM is not the owner of this group! It will not be able to manage membership for this group until you add it.',
287+
appIdNotOwnerWarning: 'Warning: GMM is not the owner of this group! It will not be able to manage membership for this group until you add it.',
288+
userNotOwnerWarning: 'Warning: You are not the owner of this group! You can only manage memberships with GMM for groups you own.',
288289
step2title: 'Step 2: Run Configuration',
289290
step2description: '',
290291
advancedQuery: 'Advanced Query',

0 commit comments

Comments
 (0)