Beispiel #1
0
 public GeneralSettingsController(IServiceProvider sp, GeneralSettingsService service) : base(sp)
 {
     _service = service;
 }
Beispiel #2
0
 public GeneralSettingsController(IServiceProvider sp, GeneralSettingsService service, ILogger <GeneralSettingsController> logger, ISettingsCache settingsCache) : base(sp)
 {
     _service       = service;
     _logger        = logger;
     _settingsCache = settingsCache;
 }
Beispiel #3
0
            public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
            {
                // (1) Make sure the API caller have provided a tenantId, and extract it
                try
                {
                    var cancellation = context.HttpContext.RequestAborted;
                    int tenantId     = _tenantIdAccessor.GetTenantId();

                    // Init the database connection...
                    // The client sometimes makes ambient API calls, not in response to user interaction
                    // Such calls should not update LastAccess of that user
                    bool silent = context.HttpContext.Request.Query["silent"].FirstOrDefault()?.ToString()?.ToLower() == "true";
                    await _appRepo.InitConnectionAsync(tenantId, setLastActive : !silent, cancellation);

                    // (2) Make sure the user is a member of this tenant
                    UserInfo userInfo = await _appRepo.GetUserInfoAsync(cancellation);

                    if (userInfo.UserId == null)
                    {
                        // If there is no user cut the pipeline short and return a Forbidden 403
                        context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);

                        // This indicates to the client to discard all cached information about this
                        // company since the user is no longer a member of it
                        context.HttpContext.Response.Headers.Add("x-settings-version", Constants.Unauthorized);
                        context.HttpContext.Response.Headers.Add("x-definitions-version", Constants.Unauthorized);
                        context.HttpContext.Response.Headers.Add("x-permissions-version", Constants.Unauthorized);
                        context.HttpContext.Response.Headers.Add("x-user-settings-version", Constants.Unauthorized);

                        return;
                    }

                    var userId        = userInfo.UserId.Value;
                    var externalId    = _externalUserAccessor.GetUserId();
                    var externalEmail = _externalUserAccessor.GetUserEmail();

                    // (3) If the user exists but new, set the External Id
                    if (userInfo.ExternalId == null)
                    {
                        // Update external Id in this tenant database
                        await _appRepo.Users__SetExternalIdByUserId(userId, externalId);

                        // Update external Id in the central Admin database too (To avoid an awkward situation
                        // where a user exists on the tenant but not on the Admin db, if they change their email in between)
                        var adminRepo = _serviceProvider.GetRequiredService <AdminRepository>();
                        await adminRepo.DirectoryUsers__SetExternalIdByEmail(externalEmail, externalId);
                    }

                    else if (userInfo.ExternalId != externalId)
                    {
                        // Note: there is the edge case of identity providers who allow email recycling. I.e. we can get the same email twice with
                        // two different external Ids. This issue is so unlikely to naturally occur and cause problems here that we are not going
                        // to handle it for now. It can however happen artificually if the application is re-configured to a new identity provider,
                        // or if someone messed with the identity database directly, but again out of scope for now.
                        context.Result = new BadRequestObjectResult("The sign-in email already exists but with a different external Id");
                        return;
                    }

                    // (4) If the user's email address has changed at the identity server, update it locally
                    else if (userInfo.Email != externalEmail)
                    {
                        await _appRepo.Users__SetEmailByUserId(userId, externalEmail);
                    }

                    // (5) Set the tenant info in the context, to make it accessible for model metadata providers
                    var tenantInfo = await _appRepo.GetTenantInfoAsync(cancellation);

                    _tenantInfoAccessor.SetInfo(tenantId, tenantInfo);

                    // (6) Ensure the freshness of the definitions and settings caches
                    {
                        var databaseVersion = tenantInfo.DefinitionsVersion;
                        var serverVersion   = _definitionsCache.GetDefinitionsIfCached(tenantId)?.Version;

                        if (serverVersion == null || serverVersion != databaseVersion)
                        {
                            // Update the cache
                            var definitions = await DefinitionsService.LoadDefinitionsForClient(_appRepo, cancellation);

                            if (!cancellation.IsCancellationRequested)
                            {
                                _definitionsCache.SetDefinitions(tenantId, definitions);
                            }
                        }
                    }
                    {
                        var databaseVersion = tenantInfo.SettingsVersion;
                        var serverVersion   = _settingsCache.GetSettingsIfCached(tenantId)?.Version;

                        if (serverVersion == null || serverVersion != databaseVersion)
                        {
                            // Update the cache
                            var settings = await GeneralSettingsService.LoadSettingsForClient(_appRepo, cancellation);

                            if (!cancellation.IsCancellationRequested)
                            {
                                _settingsCache.SetSettings(tenantId, settings);
                            }
                        }
                    }

                    // (7) If any version headers are supplied: examine their freshness
                    {
                        // Permissions
                        var clientVersion = context.HttpContext.Request.Headers["X-Permissions-Version"].FirstOrDefault();
                        if (!string.IsNullOrWhiteSpace(clientVersion))
                        {
                            var databaseVersion = userInfo.PermissionsVersion;
                            context.HttpContext.Response.Headers.Add("x-permissions-version",
                                                                     clientVersion == databaseVersion ? Constants.Fresh : Constants.Stale);
                        }
                    }

                    {
                        // User Settings
                        var clientVersion = context.HttpContext.Request.Headers["X-User-Settings-Version"].FirstOrDefault();
                        if (!string.IsNullOrWhiteSpace(clientVersion))
                        {
                            var databaseVersion = userInfo.UserSettingsVersion;
                            context.HttpContext.Response.Headers.Add("x-user-settings-version",
                                                                     clientVersion == databaseVersion ? Constants.Fresh : Constants.Stale);
                        }
                    }

                    {
                        // Definitions
                        var clientVersion = context.HttpContext.Request.Headers["X-Definitions-Version"].FirstOrDefault();
                        if (!string.IsNullOrWhiteSpace(clientVersion))
                        {
                            var databaseVersion = tenantInfo.DefinitionsVersion;
                            context.HttpContext.Response.Headers.Add("x-definitions-version",
                                                                     clientVersion == databaseVersion ? Constants.Fresh : Constants.Stale);
                        }
                    }
                    {
                        // Settings
                        var clientVersion = context.HttpContext.Request.Headers["X-Settings-Version"].FirstOrDefault();
                        if (!string.IsNullOrWhiteSpace(clientVersion))
                        {
                            var databaseVersion = tenantInfo.SettingsVersion;
                            context.HttpContext.Response.Headers.Add("x-settings-version",
                                                                     clientVersion == databaseVersion ? Constants.Fresh : Constants.Stale);
                        }
                    }

                    // Call the Action itself
                    await next();
                }
                catch (TaskCanceledException)
                {
                    context.Result = new OkResult();
                    return;
                }
                catch (MultitenancyException ex)
                {
                    // If the tenant Id is not provided cut the pipeline short and return a Bad Request 400
                    context.Result = new BadRequestObjectResult(ex.Message);
                    return;
                }
                catch (BadRequestException ex)
                {
                    // If the tenant Id is not provided cut the pipeline short and return a Bad Request 400
                    context.Result = new BadRequestObjectResult(ex.Message);
                    return;
                }
                catch (Exception ex)
                {
                    // TODO: Return to logging and 500 status code
                    context.Result = new BadRequestObjectResult(ex.GetType().Name + ": " + ex.Message);
                    //_logger.LogError(ex.Message);
                    //context.Result = new StatusCodeResult(StatusCodes.Status500InternalServerError);

                    return;
                }
            }