Exemplo n.º 1
0
 public async Task <AuthorizationPolicyInfo> FetchPolicy(
     Guid policyId,
     CancellationToken cancellationToken = default(CancellationToken))
 {
     cancellationToken.ThrowIfCancellationRequested();
     return(await _queries.Fetch(_tenantProvider.GetTenantId(), policyId).ConfigureAwait(false));
 }
Exemplo n.º 2
0
        public Task ValidateAccessToEntityAsync(
            ITenantEntity entity, EntityAction desiredAction)
        {
            if (null == entity)
            {
                return(Task.CompletedTask);
            }

            var userTenantId = _tenantIdProvider.GetTenantId();

            if (TenantSpec.BelongsToSite(userTenantId))
            {
                // simplistic access rules for prototype phase:
                // 'site user' can access any tenant
                // for the purposes of administration
                return(Task.CompletedTask);
            }

            // non-'site user'
            if (userTenantId != entity.TenantId)
            {
                string message = BuildAccessDeniedMessage(entity, desiredAction);
                throw new UnauthorizedAccessException(message);
            }

            // 'tenant user' can perform any modification read its own tenant information
            if (desiredAction != EntityAction.Read)
            {
                string message = BuildAccessDeniedMessage(entity, desiredAction);
                throw new UnauthorizedAccessException(message);
            }
            return(Task.CompletedTask);
        }
Exemplo n.º 3
0
        public void Get_All_With_Site_User_Should_Return_All_Entities()
        {
            // Given
            _tenantIdProvider.GetTenantId().Returns(Guid.Empty);

            SetupRepositoryAndService();

            // When
            var entities = _service.GetAllAsync(null, null).Result;

            // Then
            Assert.That(entities.Count(), Is.EqualTo(3));
            _repository.Received().Query();
        }
        public Task ValidateAccessToEntityAsync(
            ITenantEntity entity, EntityAction desiredAction)
        {
            if (null == entity)
            {
                return(Task.CompletedTask);
            }

            // this call may throw exception if the user doesn't have
            // the validated TenantId claim
            var _userTenantId = _tenantIdProvider.GetTenantId();

            if (TenantSpec.BelongsToSite(_userTenantId))
            {
                // simple tenant access (no permissions)
                // 'site user' can access entities within any tenant
                // for the purposes of administration
                return(Task.CompletedTask);
            }
            if (_userTenantId != entity.TenantId)
            {
                var message =
                    $"User belonging to tenant {_userTenantId} has been denied access " +
                    $"to entity '{entity.GetType().Name}' belonging to tenant {entity.TenantId}";
                throw new UnauthorizedAccessException(message);
            }
            return(Task.CompletedTask);
        }
        public async Task <IActionResult> Post([FromBody] BranchOfficeDto value)
        {
            if (value == null)
            {
                return(InvalidRequestBodyJson(nameof(BranchOfficeDto)));
            }
            if (!ModelState.IsValid)
            {
                return(BadRequest(ModelState));
            }

            if (!CheckTimeZoneCode(value.TimeZoneId))
            {
                AddErrors(new [] { $"Unknown time zone code specified: {value.TimeZoneId}" });
            }

            var newBranchOffice = Mapper.Map <BranchOffice>(value);

            newBranchOffice.TenantId = _tenantIdProvider.GetTenantId();
            await _branchOfficeService.AddAsync(newBranchOffice);

            return(Created(nameof(GetBranchOffice), new CreatedWithGuidDto {
                Id = newBranchOffice.Id
            }));
        }
        public WorkplaceCredentialsDto CreateCredentials(WorkplaceDescriptorDto descriptor)
        {
            var userTenant = _tenantIdProvider.GetTenantId().ToString();

            if (userTenant != descriptor.TenantId)
            {
                throw new BadRequestException("Bad tenant id specified");
            }

            var clientId     = descriptor.WorkplaceType + GetRandomStringInBase52Alphabeth(16);
            var clientSecret = CreateSecret();

            return(new WorkplaceCredentialsDto
            {
                ClientId = clientId,
                ClientSecret = clientSecret
            });
        }
Exemplo n.º 7
0
        public override async Task <(bool success, string[] errors)> CreateUserAsync(ApplicationUser newUser, IEnumerable <Guid> roles, string password)
        {
            if (TenantSpec.BelongsToTenant(_tenantIdProvider.GetTenantId()))
            {
                // the call is from a 'Tenant User'...
                if (newUser.TenantId == TenantSpec.EntireSiteTenantId)
                {
                    // ...and the new user model is not assigned a TenantId
                    // 'Tenant User' can only create users in their own tenant
                    newUser.TenantId = _tenantIdProvider.GetTenantId();
                }
                // Note that 'Site User' (see TenantSpec.IsSiteUser(...))
                // should be able to create new Users in any tenant
                // in order to create a 'tenant admin' user in a new tenant
            }

            return(await base.CreateUserAsync(newUser, roles, password));
        }
Exemplo n.º 8
0
        public IQueryable <T> AddScopeFilter <T>(IQueryable <T> entityQuery) where T : class, ITenantEntity
        {
            var tenantId = _tenantIdProvider.GetTenantId();

            if (tenantId != Guid.Empty)
            {
                return(entityQuery.Where(e => e.TenantId == tenantId));
            }
            return(entityQuery);
        }
        public void Setup()
        {
            LogHelper.ConfigureConsoleLogger();

            _newId        = Guid.NewGuid();
            _tenantMockId = Guid.NewGuid();

            _roleService      = Substitute.For <IRestrictedRoleService>();
            _tenantIdProvider = Substitute.For <ITenantIdProvider>();
            _tenantIdProvider.GetTenantId().Returns(_tenantMockId);
        }
Exemplo n.º 10
0
        public void Setup()
        {
            LogHelper.ConfigureConsoleLogger();

            StaticStartup.Startup();

            _newId            = Guid.NewGuid();
            _tenantId         = Guid.NewGuid();
            _testService      = Substitute.For <ITestService>();
            _tenantIdProvider = Substitute.For <ITenantIdProvider>();
            _tenantIdProvider.GetTenantId().Returns(_tenantId);
        }
Exemplo n.º 11
0
        public async Task DeleteBlobs(IEnumerable <string> blobNames)
        {
            // Basic check
            if (blobNames == null)
            {
                throw new ArgumentNullException(nameof(blobNames));
            }

            var blobNamesString = string.Join(',', blobNames);
            int tenantId        = _tenantIdProvider.GetTenantId().Value;
            await _db.Database.ExecuteSqlCommandAsync($"DELETE FROM [dbo].[Blobs] WHERE Id IN (SELECT VALUE FROM STRING_SPLIT({blobNamesString}, ',')) AND TenantId = {tenantId}");
        }
Exemplo n.º 12
0
        public PsaReportService(
            IPsaSummaryService summaryService,
            IOrgStructureReference orgStructReference,
            //IPsaReportRepository reportRepository,
            ITenantEntityAccessChecker tenantEntityAccessChecker,
            ITenantIdProvider tenantIdProvider,
            ITimeService timeService
            )
        {
            _summaryService = summaryService;
//      _reportRepository = reportRepository;
            _orgStructReference        = orgStructReference;
            _tenantEntityAccessChecker = tenantEntityAccessChecker;
            _tenantId    = tenantIdProvider.GetTenantId();
            _timeService = timeService;
        }
Exemplo n.º 13
0
        public void Setup()
        {
            LogHelper.ConfigureConsoleLogger();

            StaticStartup.Startup();

            _newId               = Guid.NewGuid();
            _tenantId            = Guid.NewGuid();
            _branchOfficeService = Substitute.For <IService <BranchOffice> >();
            _tenantIdProvider    = Substitute.For <ITenantIdProvider>();
            _logger              = Substitute.For <ILogger <BranchOfficeController> >();
            _timeService         = Substitute.For <ITimeService>();

            _credentialsService = Substitute.For <IWorkplaceCredentialsService>();
            _tenantIdProvider.GetTenantId().Returns(_tenantId);
            _timeService.CheckTimeZoneId(Arg.Any <string>()).Returns(true);
        }
        public void Setup()
        {
            LogHelper.ConfigureConsoleLogger();

            StaticStartup.Startup();

            _newId             = Guid.NewGuid();
            _tenantId          = Guid.NewGuid();
            _inspectionService = Substitute.For <IInspectionService>();
            _inspectionService.When(s => s.BeginInspectionAsync(Arg.Any <Inspection>()))
            .Do(v => ((Inspection)v[0]).Id = _newId);
            _inspectionService.BeginInspectionAsync(Arg.Any <Inspection>()).ReturnsForAnyArgs(_newId);
            _tenantIdProvider = Substitute.For <ITenantIdProvider>();
            _tenantIdProvider.GetTenantId().Returns(_tenantId);

            _incompleteInspection = new Inspection()
            {
            };
        }
Exemplo n.º 15
0
        public async Task <IActionResult> Post([FromBody] PositionDto value)
        {
            if (value == null)
            {
                return(InvalidRequestBodyJson(nameof(PositionDto)));
            }
            if (!ModelState.IsValid)
            {
                return(BadRequest(ModelState));
            }

            var newPosition = Mapper.Map <Position>(value);

            newPosition.TenantId = _tenantIdProvider.GetTenantId();
            await _positionService.AddAsync(newPosition);

            return(Created(nameof(GetPosition), new CreatedWithGuidDto {
                Id = newPosition.Id
            }));
        }
Exemplo n.º 16
0
        public async Task <IActionResult> PostTest([FromBody] TestPostDto testDto)
        {
            if (null == testDto)
            {
                return(InvalidRequestBodyJson(nameof(TestDto)));
            }
            if (!ModelState.IsValid)
            {
                return(BadRequest(ModelState));
            }

            var newTest = Mapper.Map <Test>(testDto);

            newTest.TenantId = _tenantIdProvider.GetTenantId();
            await _testService.AddAsync(newTest);

            return(Created(nameof(GetTest), new CreatedWithGuidDto {
                Id = newTest.Id
            }));
        }
Exemplo n.º 17
0
        public async Task <IActionResult> Post([FromBody] EmployeeDto value)
        {
            if (value == null)
            {
                return(InvalidRequestBodyJson(nameof(EmployeeDto)));
            }
            if (!ModelState.IsValid)
            {
                return(BadRequest(ModelState));
            }

            var newEmployee = Mapper.Map <Employee>(value);

            newEmployee.TenantId = _tenantIdProvider.GetTenantId();
            await _employeeService.AddAsync(newEmployee);

            return(Created(nameof(GetEmployee), new CreatedWithGuidDto {
                Id = newEmployee.Id
            }));
        }
Exemplo n.º 18
0
        public async Task <IActionResult> Post([FromBody] DepartmentDto newDepartmentDto)
        {
            if (newDepartmentDto == null)
            {
                return(InvalidRequestBodyJson(nameof(DepartmentDto)));
            }
            if (!ModelState.IsValid)
            {
                return(BadRequest(ModelState));
            }

            var newDepartment = Mapper.Map <Department>(newDepartmentDto);

            newDepartment.TenantId = _tenantIdProvider.GetTenantId();
            await _departmentService.AddAsync(newDepartment);

            var depCreatedDto = new CreatedWithGuidDto {
                Id = newDepartment.Id
            };

            _logger.LogInformation($"Successfully created department '{newDepartment.Name}' (id={newDepartment.Id}).");
            return(Created(nameof(GetDepartment), depCreatedDto));
        }
Exemplo n.º 19
0
        public async Task <IActionResult> CreateRole([FromBody] RoleDto role)
        {
            if (!ModelState.IsValid)
            {
                return(BadRequest(ModelState));
            }
            if (role == null)
            {
                return(InvalidRequestBodyJson(nameof(RoleDto)));
            }

            ApplicationRole appRole = _mapper.Map <ApplicationRole>(role);

            // This will throw if tenant Id is not available,
            // which means there is a bug in authentication code
            appRole.TenantId = _tenantIdProvider.GetTenantId();

            await _roleService.CreateRoleAsync(appRole, role.Permissions?.Select(p => p.Value).ToArray());

            RoleDto roleVM = await GetRoleViewModelHelper(appRole.Id);

            return(CreatedAtAction(GetRoleByIdActionName, new { id = roleVM.Id }, roleVM));
        }
Exemplo n.º 20
0
        public async Task <IActionResult> PostInspection([FromBody] InspectionPostDto inspectionDto)
        {
            if (inspectionDto == null)
            {
                return(InvalidRequestBodyJson(nameof(InspectionDto)));
            }
            if (!ModelState.IsValid)
            {
                return(BadRequest(ModelState));
            }

            var newInspection = Mapper.Map <Inspection>(inspectionDto);

            newInspection.TenantId = _tenantIdProvider.GetTenantId();
            try
            {
                var newId = await _inspectionService.BeginInspectionAsync(newInspection);

                return(Created(nameof(GetInspection), new CreatedWithGuidDto {
                    Id = newId
                }));
            }
            catch (ItemAlreadyExistsException ex)
            {
                if (ex.Id.HasValue)
                {
                    // should yield 'found' response status code
                    return(RedirectToAction(nameof(GetInspection), new CreatedWithGuidDto {
                        Id = ex.Id.Value
                    }));
                }
                throw new Exception(
                          $"Exception of type {nameof(ItemAlreadyExistsException)} should have Id defined"
                          );
            }
        }
Exemplo n.º 21
0
        public string GetShardConnectionString()
        {
            // When applying the migrations in Program.cs while running the
            // solution in development, it is convenient to have a default
            // connection string that doesn't depend on tenant Id
            if (!_tenantIdProvider.HasTenantId())
            {
                return(_config.GetConnectionString(Constants.AdminConnection));
            }

            string shardConnString = null;
            int    tenantId        = _tenantIdProvider.GetTenantId().Value;

            // Step (1) retrieve the conn string from the cache inside a READ lock
            _shardingLock.EnterReadLock();
            try
            {
                _cache.TryGetValue(CacheKey(tenantId), out shardConnString);
            }
            finally
            {
                _shardingLock.ExitReadLock();
            }

            // Step (2) if step (1) was a miss, enter inside a WRITE lock, retrieve the conn string from the source and update the cache
            if (shardConnString == null)
            {
                _shardingLock.EnterWriteLock();
                try
                {
                    // To avoid a race-condition causing multiple threads to populate the cache in parallel immediately after they all
                    // have a cache miss inside the previous READ lock, here we check the cache again inside the WRITE lock
                    _cache.TryGetValue(CacheKey(tenantId), out shardConnString);
                    if (shardConnString == null)
                    {
                        using (var scope = _serviceProvider.CreateScope())
                        {
                            var ctx = scope.ServiceProvider.GetRequiredService <AdminContext>();
                            shardConnString = ctx.Tenants.Include(e => e.Shard)
                                              .FirstOrDefault(e => e.Id == tenantId)?.Shard?.ConnectionString;
                        }
                        // This is a catastrophic error, should not happen in theory
                        if (string.IsNullOrWhiteSpace(shardConnString))
                        {
                            throw new InvalidOperationException($"The sharding route for tenant Id {tenantId} is missing");
                        }

                        // There is always one built-in shard that resides in the same DB as the shard manager, the
                        // purpose behind it is to make it easier to do development and also to set-up small instances that do not require sharding
                        else if (shardConnString == SHARD_MANAGER_PLACEHOLDER)
                        {
                            shardConnString = _config.GetConnectionString(Constants.AdminConnection);
                        }

                        // ELSE: this is a normal shard
                        else
                        {
                            // For improved security, allow more secure modes of storing shard passwords, other than in the shard manager DB itself
                            // - Mode 1: in a "Passwords" section in a secure configuration provider, and then they are referenced in the conn string by their names
                            // - Mode 2: as being the same password as the shard manager's connection string, which is also stored safely in a configuration provider

                            var shardConnBuilder = new SqlConnectionStringBuilder(shardConnString);
                            if (!string.IsNullOrWhiteSpace(shardConnBuilder.Password))
                            {
                                // If the shard password is specified, and it matches a valid key in the "Passwords" configuration section, use that configuration value instead
                                string configPassword = _config[$"{PASSWORDS_CONFIG_SECTION}:{shardConnBuilder.Password}"];
                                if (!string.IsNullOrWhiteSpace(configPassword))
                                {
                                    shardConnBuilder.Password = configPassword;
                                    shardConnString           = shardConnBuilder.ConnectionString;
                                }
                                // ELSE we hope that this is a valid password, or else the connection to the shard will sadly fail.
                            }
                            else
                            {
                                // If the password of the shard is not set but the password of the shard manager is, use the shard manager's
                                string shardManagerConnection  = _config.GetConnectionString(Constants.AdminConnection);
                                var    shardManagerConnBuilder = new SqlConnectionStringBuilder(shardManagerConnection);

                                if (!string.IsNullOrWhiteSpace(shardManagerConnBuilder.Password))
                                {
                                    shardConnBuilder.Password = shardManagerConnBuilder.Password;
                                    shardConnString           = shardConnBuilder.ConnectionString;
                                }
                                // ELSE we hope that this is windows authentication, or else the connection to the shard will sadly fail.
                            }
                        }

                        // Set the cache, with an expiry
                        var expiryTime = DateTimeOffset.Now.AddMinutes(GetCacheExpirationInMinutes());
                        _cache.Set(CacheKey(tenantId), shardConnString, expiryTime);

                        // NOTE: Sharding routes is a type of data that is very frequently read, yet very rarely if never updated
                        // so we have decided to rely only on cache expiry to keep the cache fresh (2h by default), so if you move a tenant
                        // across shards, you need to wait those 2 hours before all caches are updated. This is the best compromise
                    }
                }
                finally
                {
                    _shardingLock.ExitWriteLock();
                }
            }

            return(shardConnString);
        }
Exemplo n.º 22
0
 private Task <Summary> TryGetSummaryEntityForInspectionAsync(Guid inspectionId)
 {
     return(_psaSummaryRepository.GetSingleOrDefaultAsync(
                s => s.TenantId == _tenantIdProvider.GetTenantId() && s.InspectionId == inspectionId));
 }
Exemplo n.º 23
0
 public static bool HasTenantId(this ITenantIdProvider tenantIdProvider)
 {
     return(tenantIdProvider.GetTenantId() != null);
 }
Exemplo n.º 24
0
        private async Task <ActionResult <EntitiesResponse <LocalUser> > > ActivateDeactivate([FromBody] List <int> ids, bool returnEntities, string expand, bool isActive)
        {
            await CheckActionPermissions(ids.Cast <int?>());

            var isActiveParam = new SqlParameter("@IsActive", isActive);

            DataTable idsTable = DataTable(ids.Select(id => new { Id = id }), addIndex: false);
            var       idsTvp   = new SqlParameter("@Ids", idsTable)
            {
                TypeName  = $"dbo.IdList",
                SqlDbType = SqlDbType.Structured
            };

            string sql = @"
    SET NOCOUNT ON;
    DECLARE @Now DATETIMEOFFSET(7) = SYSDATETIMEOFFSET();
    DECLARE @UserId INT = CONVERT(INT, SESSION_CONTEXT(N'UserId'));
    DECLARE @Emails [dbo].[CodeList];

    INSERT INTO @Emails([Code])
    SELECT x.[Email]
    FROM
    (
        MERGE INTO [dbo].[LocalUsers] AS t
	        USING (
		        SELECT [Id]
		        FROM @Ids
	        ) AS s ON (t.Id = s.Id)
	        WHEN MATCHED AND (t.IsActive <> @IsActive)
	        THEN
		        UPDATE SET 
			    t.[IsActive]	= @IsActive,
			    t.[ModifiedAt]	= @Now,
			    t.[ModifiedById]= @UserId
                OUTPUT inserted.[Email]
    ) As x;

    SELECT [Code] AS [Value] FROM @Emails;
";

            // Tenant Id
            var tenantId = new SqlParameter("TenantId", _tenantIdProvider.GetTenantId());

            using (var trxApp = await _db.Database.BeginTransactionAsync())
            {
                try
                {
                    // Update the entities and retrieve the emails of the entities that were updated
                    List <string> emails = await _db.Strings.FromSql(sql, idsTvp, isActiveParam).Select(e => e.Value).ToListAsync();

                    // Prepare the TVP of emails to update from the manager
                    DataTable emailsTable = DataTable(emails.Select(e => new { Code = e }), addIndex: false);
                    var       emailsTvp   = new SqlParameter("Emails", emailsTable)
                    {
                        TypeName  = $"dbo.CodeList",
                        SqlDbType = SqlDbType.Structured
                    };

                    using (var trxAdmin = await _adminDb.Database.BeginTransactionAsync())
                    {
                        try
                        {
                            if (isActive)
                            {
                                // Insert efficiently with a SQL query
                                await _adminDb.Database.ExecuteSqlCommandAsync($@"
    INSERT INTO dbo.[TenantMemberships] 
    SELECT Id, @TenantId FROM [dbo].[GlobalUsers] WHERE Email IN (SELECT Code from @Emails);
", emailsTvp, tenantId);
                            }
                            else
                            {
                                // Delete efficiently with a SQL query
                                await _adminDb.Database.ExecuteSqlCommandAsync($@"
    DELETE FROM dbo.[TenantMemberships] 
    WHERE TenantId = @TenantId AND UserId IN (
        SELECT Id FROM [dbo].[GlobalUsers] WHERE Email IN (SELECT Code from @Emails)
    );
", emailsTvp, tenantId);
                            }

                            // Commit both
                            trxAdmin.Commit();
                            trxApp.Commit();
                        }
                        catch (Exception ex)
                        {
                            trxApp.Rollback();
                            trxAdmin.Rollback();
                            throw ex;
                        }
                    }
                }
                catch (Exception ex)
                {
                    trxApp.Rollback();
                    throw ex;
                }
            }

            // Determine whether entities should be returned
            if (!returnEntities)
            {
                // IF no returned items are expected, simply return 200 OK
                return(Ok());
            }
            else
            {
                // Load the entities using their Ids
                var affectedDbEntitiesQ         = _db.LocalUsers.Where(e => ids.Contains(e.Id)); // _db.LocalUsers.FromSql("SELECT * FROM [dbo].[LocalUsers] WHERE Id IN (SELECT Id FROM @Ids)", idsTvp);
                var affectedDbEntitiesExpandedQ = Expand(affectedDbEntitiesQ, expand);
                var affectedDbEntities          = await affectedDbEntitiesExpandedQ.ToListAsync();

                var affectedEntities = _mapper.Map <List <LocalUser> >(affectedDbEntities);

                // sort the entities the way their Ids came, as a good practice
                LocalUser[] sortedAffectedEntities = new LocalUser[ids.Count];
                Dictionary <int, LocalUser> affectedEntitiesDic = affectedEntities.ToDictionary(e => e.Id.Value);
                for (int i = 0; i < ids.Count; i++)
                {
                    var       id     = ids[i];
                    LocalUser entity = null;
                    if (affectedEntitiesDic.ContainsKey(id))
                    {
                        entity = affectedEntitiesDic[id];
                    }

                    sortedAffectedEntities[i] = entity;
                }

                // Prepare a proper response
                var response = new EntitiesResponse <LocalUser>
                {
                    Data           = sortedAffectedEntities,
                    CollectionName = GetCollectionName(typeof(LocalUser))
                };

                // Commit and return
                return(Ok(response));
            }
        }
Exemplo n.º 25
0
 public TenantUserInfo GetCurrentInfo()
 {
     return(GetInfo(_tenantIdProvider.GetTenantId().Value));
 }
Exemplo n.º 26
0
        /// <summary>
        /// This trick makes it possible to injected the ApplicationContext into other components via DI as usual
        /// but it automatically configures itself with the correct options. Inspired from this Microsoft sample: https://bit.ly/2TIEFMA
        /// </summary>
        /// <param name="shardResolver">The service that resolves the shard connection string</param>
        /// <param name="tenantIdProvider">The service that retrieves tenants Ids from the headers</param>
        /// <returns></returns>
        private static DbContextOptions <ApplicationContext> CreateDbContextOptions(
            IShardResolver shardResolver, ITenantIdProvider tenantIdProvider,
            IUserProvider userService, ITenantUserInfoAccessor accessor)
        {
            // Prepare the options based on the connection created with the shard manager
            var    optionsBuilder   = new DbContextOptionsBuilder <ApplicationContext>();
            string connectionString = shardResolver.GetShardConnectionString();

            if (tenantIdProvider is DesignTimeTenantIdProvider)
            {
                // Only for design time when running "ef migrations" command from the CLI
                optionsBuilder = optionsBuilder.UseSqlServer(connectionString);
            }
            else
            {
                int tenantId = tenantIdProvider.GetTenantId() ?? throw new Controllers.Misc.BadRequestException("Tenant Id was not supplied");

                // Unless this is a fake design time resolver, apply row level security and pass context info
                SqlConnection sqlConnection = new SqlConnection(connectionString);

                SqlCommand cmd = sqlConnection.CreateCommand();
                cmd.CommandText = @"
    -- Set the global values of the session context
    EXEC sp_set_session_context @key=N'TenantId', @value=@TenantId;
    EXEC sp_set_session_context @key=N'Culture', @value=@Culture;
    EXEC sp_set_session_context @key=N'NeutralCulture', @value=@NeutralCulture;

    -- Get the User Id
    DECLARE 
        @UserId INT, 
        @ExternalId NVARCHAR(450), 
        @Email NVARCHAR(255), 
        @SettingsVersion UNIQUEIDENTIFIER, 
        @PermissionsVersion UNIQUEIDENTIFIER,
        @ViewsAndSpecsVersion UNIQUEIDENTIFIER,
        @UserSettingsVersion UNIQUEIDENTIFIER,
        @PrimaryLanguageId NVARCHAR(255),
        @PrimaryLanguageSymbol NVARCHAR(255),
        @SecondaryLanguageId NVARCHAR(255),
        @SecondaryLanguageSymbol NVARCHAR(255)
;

    SELECT
        @UserId = Id,
        @ExternalId = ExternalId,
        @Email = Email,
        @PermissionsVersion = PermissionsVersion,
        @UserSettingsVersion = UserSettingsVersion
    FROM [dbo].[LocalUsers] 
    WHERE TenantId = @TenantId AND IsActive = 1 AND (ExternalId = @ExternalUserId OR Email = @UserEmail);

    -- Set LastAccess (Works only if @UserId IS NOT NULL)
    UPDATE [dbo].[LocalUsers] SET LastAccess = SYSDATETIMEOFFSET() WHERE Id = @UserId;

    -- Get hashes
    SELECT 
        @SettingsVersion = SettingsVersion,
        @ViewsAndSpecsVersion = ViewsAndSpecsVersion,
        @PrimaryLanguageId = PrimaryLanguageId,
        @PrimaryLanguageSymbol = PrimaryLanguageSymbol,
        @SecondaryLanguageId = SecondaryLanguageId,
        @SecondaryLanguageSymbol = SecondaryLanguageSymbol
    FROM [dbo].[Settings]
    WHERE TenantId = @TenantId 

    -- Set the User Id
    EXEC sp_set_session_context @key=N'UserId', @value=@UserId;

    -- Return the user information
    SELECT 
        @UserId AS userId, 
        @ExternalId AS ExternalId, 
        @Email AS Email, 
        @SettingsVersion AS SettingsVersion, 
        @PermissionsVersion AS PermissionsVersion,
        @UserSettingsVersion AS UserSettingsVersion,
        @ViewsAndSpecsVersion AS ViewsAndSpecsVersion,
        @PrimaryLanguageId AS PrimaryLanguageId,
        @PrimaryLanguageSymbol AS PrimaryLanguageSymbol,
        @SecondaryLanguageId AS SecondaryLanguageId,
        @SecondaryLanguageSymbol AS SecondaryLanguageSymbol;
";
                cmd.Parameters.AddWithValue("@TenantId", tenantId);
                cmd.Parameters.AddWithValue("@ExternalUserId", userService.GetUserId());
                cmd.Parameters.AddWithValue("@UserEmail", userService.GetUserEmail());
                cmd.Parameters.AddWithValue("@Culture", CultureInfo.CurrentUICulture.Name);
                cmd.Parameters.AddWithValue("@NeutralCulture", CultureInfo.CurrentUICulture.IsNeutralCulture ? CultureInfo.CurrentUICulture.Name : CultureInfo.CurrentUICulture.Parent.Name);

                sqlConnection.Open();
                using (var reader = cmd.ExecuteReader())
                {
                    if (reader.Read())
                    {
                        int i    = 0;
                        var info = new TenantUserInfo
                        {
                            UserId                  = reader.IsDBNull(i) ? (int?)null : reader.GetInt32(i++),
                            ExternalId              = reader.IsDBNull(i) ? null : reader.GetString(i++),
                            Email                   = reader.IsDBNull(i) ? null : reader.GetString(i++),
                            SettingsVersion         = reader.IsDBNull(i) ? null : reader.GetGuid(i++).ToString(),
                            PermissionsVersion      = reader.IsDBNull(i) ? null : reader.GetGuid(i++).ToString(),
                            UserSettingsVersion     = reader.IsDBNull(i) ? null : reader.GetGuid(i++).ToString(),
                            ViewsAndSpecsVersion    = reader.IsDBNull(i) ? null : reader.GetGuid(i++).ToString(),
                            PrimaryLanguageId       = reader.IsDBNull(i) ? null : reader.GetString(i++),
                            PrimaryLanguageSymbol   = reader.IsDBNull(i) ? null : reader.GetString(i++),
                            SecondaryLanguageId     = reader.IsDBNull(i) ? null : reader.GetString(i++),
                            SecondaryLanguageSymbol = reader.IsDBNull(i) ? null : reader.GetString(i++),
                        };

                        // Provide the user throughout the current session
                        accessor.SetInfo(tenantId, info);
                    }
                    else
                    {
                        throw new Controllers.Misc.BadRequestException("Something went wrong while querying the user ID from the Database");
                    }
                }

                // Prepare the options based on the connection created with the shard manager
                optionsBuilder = optionsBuilder.UseSqlServer(sqlConnection);
            }

            return(optionsBuilder
                   .ReplaceService <IMigrationsSqlGenerator, CustomSqlServerMigrationsSqlGenerator>()
                   .Options);
        }