public HomeController(IOptions <AzureADSettings> settings, AzureResourceManagerUtil resourceManagerUtilty, ISubscriptionRepository subscriptionRepository, SignedInUserService signedInUserService) { azureADSettings = settings.Value; this.resourceManagerUtility = resourceManagerUtilty; this.subscriptionRepository = subscriptionRepository; this.signedInUserService = signedInUserService; }
private static void AddSwagger(this IServiceCollection services, AzureADSettings azureAdSettings) { services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Migs Tech Blizzard API", Version = "v1" }); // Only include Controllers and their Actions that have a defined group name c.DocInclusionPredicate((_, controller) => !string.IsNullOrWhiteSpace(controller.GroupName)); // Rather than grouping Actions by their Controllers, we group Actions by the group name of the controller. // If two separate controller files have similar business logic, we can give the two controllers the same // group name and Swagger will group the Actions in those two controllers together. c.TagActionsBy(controller => new[] { controller.GroupName }); // Include the xml comments generated at build time for each Controller/Action var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFile)); c.AddSecurityDefinition(SecurityDefinitionName, new OpenApiSecurityScheme { Name = "Authorization", Type = SecuritySchemeType.OAuth2, Scheme = "Bearer", BearerFormat = "JWT", In = ParameterLocation.Header, Description = "Azure AD Auth", Flows = new OpenApiOAuthFlows { Implicit = new OpenApiOAuthFlow { Scopes = new Dictionary <string, string> { { string.Format(ScopeNamePattern, azureAdSettings.ClientId, azureAdSettings.Scope), azureAdSettings.AdminConsentName } }, AuthorizationUrl = new Uri(string.Format(AuthorizeUrlPattern, azureAdSettings.Instance, azureAdSettings.TenantId)), TokenUrl = new Uri(string.Format(TokenUrlPattern, azureAdSettings.Instance, azureAdSettings.TenantId)) } } }); c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = SecurityDefinitionName } }, Array.Empty <string>() } }); }); }
private static OnBehalfOfProvider CreateOnBehalfOfProvider(AzureADSettings aadSettings, string[] scopes) { IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder .Create(aadSettings.AppId) .WithClientSecret(aadSettings.AppPassword) .WithTenantId(aadSettings.TenantId) .Build(); return new OnBehalfOfProvider(confidentialClientApplication, scopes); }
private static ClientCredentialProvider CreateClientCredentialProvider(AzureADSettings aadSettings) { IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder .Create(aadSettings.AppId) .WithClientSecret(aadSettings.AppPassword) .WithTenantId(aadSettings.TenantId) .Build(); return(new ClientCredentialProvider(confidentialClientApplication)); }
public async Task UpdateSettingsAsync(AzureADSettings settings) { if (settings == null) { throw new ArgumentNullException(nameof(settings)); } var container = await _siteService.LoadSiteSettingsAsync(); container.Alter <AzureADSettings>(nameof(AzureADSettings), aspect => { aspect.AppId = settings.AppId; aspect.CallbackPath = settings.CallbackPath; aspect.DisplayName = settings.DisplayName; aspect.TenantId = settings.TenantId; }); await _siteService.UpdateSiteSettingsAsync(container); }
public IEnumerable <ValidationResult> ValidateSettings(AzureADSettings settings) { if (settings == null) { throw new ArgumentNullException(nameof(settings)); } if (string.IsNullOrWhiteSpace(settings.DisplayName)) { yield return(new ValidationResult(S["DisplayName is required"], new string[] { nameof(settings.DisplayName) })); } if (string.IsNullOrWhiteSpace(settings.AppId)) { yield return(new ValidationResult(S["AppId is required"], new string[] { nameof(settings.AppId) })); } if (string.IsNullOrWhiteSpace(settings.TenantId)) { yield return(new ValidationResult(S["TenantId is required"], new string[] { nameof(settings.TenantId) })); } }
public AzureResourceManagerUtil(IOptions <AzureADSettings> azureAdSettings, SignedInUserService signedInUserService) { this.azureADSettings = azureAdSettings.Value; this.signedInUserService = signedInUserService; }
private static void AddAuthenticationPolicies(this IServiceCollection services, AzureADSettings azureAdSettings) { services .AddAuthentication(sharedOptions => { sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { // Authority will be Your AzureAd Instance and Tenant Id options.Authority = $"{azureAdSettings.Instance}{azureAdSettings.TenantId}/v2.0"; // The valid audiences are both the Client ID(options.Audience) and api://{ClientID} options.TokenValidationParameters = new TokenValidationParameters { ValidAudiences = new string[] { azureAdSettings.ClientId, $"api://{azureAdSettings.ClientId}" } }; }); }
/// <summary> /// Gets the Agents Calendar view. /// </summary> /// <param name="ssoToken">The sso access token used to make request against graph apis.</param> /// <param name="constraints">The time constraint for finding calendar view.</param> /// /// <param name="azureADSettings">Azure AD configuration settings.</param> /// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns> public static async Task<GraphResponse<IEnumerable<MeetingDetails>>> GetCalendarViewAsync(string ssoToken, TimeBlock constraints, AzureADSettings azureADSettings) { var authProvider = CreateOnBehalfOfProvider(azureADSettings, new[] { "Calendars.Read" }); GraphServiceClient graphServiceClient = new GraphServiceClient(authProvider); var startDateTimeParam = Uri.EscapeDataString(constraints.StartDateTime.ToString("o")); var endDateTimeParam = Uri.EscapeDataString(constraints.EndDateTime.ToString("o")); var queryOptions = new List<QueryOption>() { new QueryOption("startdatetime", startDateTimeParam), new QueryOption("enddatetime", endDateTimeParam), }; GraphResponse<IEnumerable<MeetingDetails>> response = new GraphResponse<IEnumerable<MeetingDetails>>(); try { var page = await graphServiceClient.Me .CalendarView.Request(queryOptions) .WithUserAssertion(new UserAssertion(ssoToken)) .GetAsync(); List<MeetingDetails> result = new List<MeetingDetails>(); while (page != null) { var meetingList = page.CurrentPage; foreach (var meeting in meetingList) { result.Add(new MeetingDetails { Subject = meeting.Subject, MeetingTime = new TimeBlock { StartDateTime = meeting.Start.ToDateTimeOffset().ToUniversalTime(), EndDateTime = meeting.End.ToDateTimeOffset().ToUniversalTime(), }, }); } if (page.NextPageRequest == null) { break; } page = await page.NextPageRequest.GetAsync(); } response.Result = result.ToImmutableList(); return response; } catch (Exception e) { response.FailureReason = e.Message; response.Result = new List<MeetingDetails>(); } return response; }
/// <summary> /// Gets the Bookings staff member ID for the given user. /// </summary> /// <param name="ssoToken">The SSO token.</param> /// <param name="businessId">The ID for the Bookings business to use.</param> /// <param name="userPrincipalName">The UPN of the user.</param> /// <param name="azureADSettings">Azure AD configuration settings.</param> /// <returns>The Bookings staff member ID for the given user, or null if not found.</returns> public static async Task<string> GetBookingsStaffMemberId(string ssoToken, string businessId, string userPrincipalName, AzureADSettings azureADSettings) { var authProvider = CreateOnBehalfOfProvider(azureADSettings, new[] { "BookingsAppointment.ReadWrite.All" }); var graphServiceClient = new GraphBeta.GraphServiceClient(authProvider); var staffMembersRequest = graphServiceClient.BookingBusinesses[businessId].StaffMembers .Request() .Select(staffMember => new { staffMember.Id }) .Filter($"emailAddress eq '{userPrincipalName}'") .WithUserAssertion(new UserAssertion(ssoToken)); var staffMembers = await staffMembersRequest.GetAsync(); // We expect exactly one staff member to match if (staffMembers.Count != 1) { return null; } return staffMembers.FirstOrDefault()?.Id; }
/// <summary> /// Update a Bookings appointment. /// </summary> /// <param name="ssoToken">The SSO token.</param> /// <param name="request">The consult request to book.</param> /// <param name="assignedStaffMemberId">The Bookings staff member ID of the assigned agent.</param> /// <param name="azureADSettings">Azure AD configuration settings.</param> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> public static async Task UpdateBookingsAppointment(string ssoToken, Request request, string assignedStaffMemberId, AzureADSettings azureADSettings) { var authProvider = CreateOnBehalfOfProvider(azureADSettings, new[] { "BookingsAppointment.ReadWrite.All" }); var graphServiceClient = new GraphBeta.GraphServiceClient(authProvider); var bookingAppointment = new GraphBeta.BookingAppointment { StaffMemberIds = new List<string> { assignedStaffMemberId }, }; await graphServiceClient.BookingBusinesses[request.BookingsBusinessId].Appointments[request.BookingsAppointmentId] .Request() .WithUserAssertion(new UserAssertion(ssoToken)) .UpdateAsync(bookingAppointment); }
/// <summary> /// Gets the users in a teams channel defined by channel id. /// </summary> /// <param name="teamAadObjectId">The AadObjectId id of the channel to grab users from.</param> /// <param name="azureADSettings">Azure AD configuration settings.</param> /// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns> public static async Task<GraphResponse<IEnumerable<User>>> GetMembersInTeamsChannelAsync(string teamAadObjectId, AzureADSettings azureADSettings) { var authProvider = CreateClientCredentialProvider(azureADSettings); GraphServiceClient graphServiceClient = new GraphServiceClient(authProvider); GraphResponse<IEnumerable<User>> response = new GraphResponse<IEnumerable<User>>(); try { var page = await graphServiceClient.Groups[teamAadObjectId] .Members .Request() .GetAsync(); List<User> result = new List<User>(); while (page != null) { var usersList = page.CurrentPage.ToList(); foreach (var user in usersList) { var currentUserObj = user as User; result.Add(currentUserObj); } if (page.NextPageRequest == null) { break; } page = await page.NextPageRequest.GetAsync(); } response.Result = result.ToImmutableList(); return response; } catch (Exception e) { response.FailureReason = e.Message; response.Result = new List<User>(); } return response; }
/// <summary> /// Create a Bookings appointment for the given consult request. /// </summary> /// <param name="ssoToken">The SSO token of the calling agent.</param> /// <param name="request">The consult request to book.</param> /// <param name="assignedStaffMemberId">The Bookings staff member ID of the assigned agent.</param> /// <param name="azureADSettings">Azure AD configuration settings.</param> /// <returns>A <see cref="Task"/> representing the asynchronous operation. The task result contains the created <see cref="GraphBeta.BookingAppointment"/>.</returns> public static async Task<GraphBeta.BookingAppointment> CreateBookingsAppointment(string ssoToken, Request request, string assignedStaffMemberId, AzureADSettings azureADSettings) { var authProvider = CreateOnBehalfOfProvider(azureADSettings, new[] { "BookingsAppointment.ReadWrite.All" }); var graphServiceClient = new GraphBeta.GraphServiceClient(authProvider); var bookingAppointment = new GraphBeta.BookingAppointment { CustomerEmailAddress = request.CustomerEmail, CustomerName = request.CustomerName, CustomerPhone = request.CustomerPhone, Start = GraphBeta.DateTimeTimeZone.FromDateTimeOffset(request.AssignedTimeBlock.StartDateTime.ToUniversalTime(), System.TimeZoneInfo.Utc), End = GraphBeta.DateTimeTimeZone.FromDateTimeOffset(request.AssignedTimeBlock.EndDateTime.ToUniversalTime(), System.TimeZoneInfo.Utc), OptOutOfCustomerEmail = false, ServiceId = request.BookingsServiceId, StaffMemberIds = new List<string> { assignedStaffMemberId }, IsLocationOnline = true, }; var bookingResult = await graphServiceClient.BookingBusinesses[request.BookingsBusinessId].Appointments .Request() .WithUserAssertion(new UserAssertion(ssoToken)) .AddAsync(bookingAppointment); return bookingResult; }
/// <summary> /// Gets the user's display photo. /// </summary> /// <param name="ssoToken">The sso access token used to make request against graph apis.</param> /// <param name="userId">The user to get the .</param> /// /// <param name="azureADSettings">Azure AD configuration settings.</param> /// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns> public static async Task<GraphResponse<byte[]>> GetUserDisplayPhotoAsync(string ssoToken, string userId, AzureADSettings azureADSettings) { var authProvider = CreateOnBehalfOfProvider(azureADSettings, new[] { "User.ReadBasic.All" }); GraphServiceClient graphServiceClient = new GraphServiceClient(authProvider); GraphResponse<byte[]> response = new GraphResponse<byte[]>(); try { var result = await graphServiceClient.Users[userId].Photos["48x48"].Content.Request().WithUserAssertion(new UserAssertion(ssoToken)).GetAsync(); if (result == null) { response.FailureReason = "Unable to get the user's display photo"; response.Result = null; return response; } byte[] bytes = new byte[result.Length]; result.Read(bytes, 0, (int)result.Length); response.Result = bytes; return response; } catch (Exception e) { response.FailureReason = e.Message; response.Result = null; } return response; }
/// <summary> /// Gets the user available times. /// </summary> /// <param name="ssoToken">The sso access token used to make request against graph apis.</param> /// <param name="constraints">The time constraint for finding free times.</param> /// <param name="organizer">The organizer the ssoToken belongs to.</param> /// <param name="azureADSettings">The Azure AD application settings.</param> /// <param name="maxTimeSlots">The maximum number of time slots to return.</param> /// <param name="emailAddresses">Emails.</param> /// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns> public static async Task<Dictionary<string, List<TimeBlock>>> GetAvailableTimeSlotsAsync(string ssoToken, IEnumerable<TimeSlot> constraints, AgentAvailability organizer, AzureADSettings azureADSettings, int? maxTimeSlots = null, IEnumerable<string> emailAddresses = null) { // setup graph client var authProvider = CreateOnBehalfOfProvider(azureADSettings, new[] { "Calendars.Read" }); GraphServiceClient graphServiceClient = new GraphServiceClient(authProvider); // define the time constraints var timeConstaint = new TimeConstraint { ActivityDomain = ActivityDomain.Work, TimeSlots = constraints, }; // add the attendees var attendees = new List<AttendeeBase>(); foreach (var address in emailAddresses) { var attendee = new AttendeeBase { Type = AttendeeType.Required, EmailAddress = new EmailAddress { Address = address, }, }; attendees.Add(attendee); } bool isMe = emailAddresses.Count() == 0; try { // perform the graph call for find meeting times var result = await graphServiceClient.Me.FindMeetingTimes(timeConstraint: timeConstaint, maxCandidates: maxTimeSlots, attendees: attendees, isOrganizerOptional: !isMe, minimumAttendeePercentage: 10) .Request() .WithUserAssertion(new UserAssertion(ssoToken)) .PostAsync(); // return an empty collection if EmptySuggestionsReason returned if (!result.EmptySuggestionsReason.Equals(string.Empty)) { return new Dictionary<string, List<TimeBlock>>(); } // pivot the results from timeblock centric to user centric Dictionary<string, List<TimeBlock>> dictionary = new Dictionary<string, List<TimeBlock>>(); foreach (var timeslot in result.MeetingTimeSuggestions.ToList()) { if (isMe) { if (timeslot.OrganizerAvailability != FreeBusyStatus.Free) { continue; } if (!dictionary.ContainsKey(organizer.Emailaddress)) { dictionary.Add(organizer.Emailaddress, new List<TimeBlock>()); } dictionary[organizer.Emailaddress].Add(new TimeBlock() { StartDateTime = timeslot.MeetingTimeSlot.Start.ToDateTimeOffset().ToUniversalTime(), EndDateTime = timeslot.MeetingTimeSlot.End.ToDateTimeOffset().ToUniversalTime(), }); } else { foreach (var agent in timeslot.AttendeeAvailability) { if (agent.Availability.GetValueOrDefault(FreeBusyStatus.Unknown) != FreeBusyStatus.Free) { continue; } if (!dictionary.ContainsKey(agent.Attendee.EmailAddress.Address)) { dictionary.Add(agent.Attendee.EmailAddress.Address, new List<TimeBlock>()); } dictionary[agent.Attendee.EmailAddress.Address].Add(new TimeBlock() { StartDateTime = timeslot.MeetingTimeSlot.Start.ToDateTimeOffset().ToUniversalTime(), EndDateTime = timeslot.MeetingTimeSlot.End.ToDateTimeOffset().ToUniversalTime(), }); } } } return dictionary; } catch (Exception) { return new Dictionary<string, List<TimeBlock>>(); } }