Example #1
0
 public AdminFactServiceBehavior(
     IServiceContextAccessor context,
     AdminRepository adminRepo,
     AdminVersions versions,
     ILogger <AdminServiceBehavior> logger) : base(context, adminRepo, versions, logger)
 {
 }
        public ApplicationServiceBehavior(
            IServiceContextAccessor context,
            IApplicationRepositoryFactory repositoryFactory,
            ApplicationVersions versions,
            AdminRepository adminRepo,
            ILogger <ApplicationServiceBehavior> logger)
        {
            _versions  = versions;
            _adminRepo = adminRepo;
            _logger    = logger;

            // Extract information from the Context Accessor
            _isServiceAccount = context.IsServiceAccount;
            if (_isServiceAccount)
            {
                _externalId = context.ExternalClientId ??
                              throw new InvalidOperationException($"The external client ID was not supplied.");
            }
            else
            {
                // This is a human user, so the external Id and email are required
                _externalId = context.ExternalUserId ??
                              throw new InvalidOperationException($"The external user ID was not supplied.");
                _externalEmail = context.ExternalEmail ??
                                 throw new InvalidOperationException($"The external user email was not supplied.");
            }

            _tenantId = context.TenantId ?? throw new ServiceException($"Tenant id was not supplied.");
            _appRepo  = repositoryFactory.GetRepository(_tenantId);
            _isSilent = context.IsSilent;
        }
        public AdminServiceBehavior(
            IServiceContextAccessor context,
            AdminRepository adminRepo,
            AdminVersions versions,
            ILogger <AdminServiceBehavior> logger)
        {
            _versions = versions;
            _logger   = logger;

            _isServiceAccount = context.IsServiceAccount;
            if (_isServiceAccount)
            {
                _externalId = context.ExternalClientId ??
                              throw new InvalidOperationException($"The external client ID was not supplied.");
            }
            else
            {
                // This is a human user, so the external Id and email are required
                _externalId = context.ExternalUserId ??
                              throw new InvalidOperationException($"The external user ID was not supplied.");
                _externalEmail = context.ExternalEmail ??
                                 throw new InvalidOperationException($"The external user email was not supplied.");
            }

            _adminRepo = adminRepo;
            _isSilent  = context.IsSilent;
        }
Example #4
0
 public PermissionsService(
     ApplicationServiceBehavior behavior,
     IServiceContextAccessor accessor,
     IPermissionsCache permissionsCache) : base(accessor)
 {
     _behavior         = behavior;
     _permissionsCache = permissionsCache;
 }
Example #5
0
 public AdminPermissionsService(
     AdminServiceBehavior behavior,
     IServiceContextAccessor accessor,
     AdminRepository repo) : base(accessor)
 {
     _behavior = behavior;
     _repo     = repo;
 }
 public AdminSettingsService(
     AdminServiceBehavior behavior,
     AdminRepository repo,
     IServiceContextAccessor accessor) : base(accessor)
 {
     _behavior = behavior;
     _repo     = repo;
 }
Example #7
0
 public DefinitionsService(
     ApplicationServiceBehavior behavior,
     IServiceContextAccessor contextAccessor,
     IDefinitionsCache definitionsCache) : base(contextAccessor)
 {
     _behavior         = behavior;
     _definitionsCache = definitionsCache;
 }
Example #8
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ServiceBase"/> class.
 /// </summary>
 /// <param name="contextAccessor"></param>
 public ServiceBase(IServiceContextAccessor contextAccessor)
 {
     ExternalUserId   = contextAccessor.ExternalUserId;
     ExternalEmail    = contextAccessor.ExternalEmail;
     IsServiceAccount = contextAccessor.IsServiceAccount;
     TenantId         = contextAccessor.TenantId;
     Today            = contextAccessor.Today;
     Calendar         = contextAccessor.Calendar;
 }
Example #9
0
        public async Task Invoke(HttpContext context, IServiceContextAccessor serviceContextAccessor)
        {
            try
            {
                // Collect data to go into the service context
                if (!Int32.TryParse(context?.Request?.Headers["ServiceVersion"].FirstOrDefault(), out int version))
                {
                    version = metadataCollection.MaxVersion;
                }
                var metadata = metadataCollection[version];

                // The userDomain is meant to identify the tenant of the user who is logging in
                UserDomain userDomain = null;
                if (context.Request.Host.Host == "localhost" || context.Request.Host.Host == "127.0.0.1")
                {
                    userDomain = new UserDomain {
                        Name = context.Request.Query["userdomain"]
                    };
                }
                else
                {
                    userDomain = new UserDomain {
                        Name = context.Request.Host.Host.Replace('.', '_')
                    };
                }

                // Build the service context
                var serviceContext = new ServiceContext
                {
                    Metadata   = metadata,
                    UserDomain = userDomain
                };

                // Make the service context available throughout the request-handling code
                serviceContextAccessor.ServiceContext = serviceContext;

                await next.Invoke(context);
            }
            catch (ServiceException se)
            {
                logger.LogError(se.InnerException, se.Message);
                context.Response.StatusCode = (int)se.HttpStatusCode;
                await context.Response.WriteAsync(se.Message);
            }
            catch (SerializationException sze)
            {
                logger.LogError(sze, sze.Message);
                context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                await context.Response.WriteAsync(sze.Message);
            }
            catch (Exception e)
            {
                logger.LogError(e, e.Message);
                context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                await context.Response.WriteAsync("Unexpected server error");
            }
        }
 public FactServiceDependencies(
     IStringLocalizer <Strings> localizer,
     MetadataProvider metadata,
     TemplateService templateService,
     IServiceContextAccessor contextAccessor)
 {
     Localizer       = localizer;
     Metadata        = metadata;
     TemplateService = templateService;
     ContextAccessor = contextAccessor;
 }
 public ReconciliationService(
     ApplicationServiceBehavior behavior,
     IServiceContextAccessor accessor,
     MetadataProvider metadata,
     IPermissionsCache permissionsCache,
     IStringLocalizer <Strings> localizer) : base(accessor)
 {
     _behavior         = behavior;
     _metadata         = metadata;
     _permissionsCache = permissionsCache;
     _localizer        = localizer;
 }
 public ApplicationSettingsServiceDependencies(
     IServiceContextAccessor context,
     ISettingsCache settingsCache,
     IPermissionsCache permissionsCache,
     ApplicationServiceBehavior behavior,
     MetadataProvider metadataProvider)
 {
     Context          = context;
     SettingsCache    = settingsCache;
     PermissionsCache = permissionsCache;
     Behavior         = behavior;
     MetadataProvider = metadataProvider;
 }
Example #13
0
 public CompaniesService(
     IServiceContextAccessor accessor,
     AdminRepository db,
     IApplicationRepositoryFactory factory,
     ISettingsCache settingsCache,
     NullServiceBehavior behavior,
     ILogger <CompaniesService> logger) : base(accessor)
 {
     _adminRepo     = db;
     _factory       = factory;
     _settingsCache = settingsCache;
     _logger        = logger;
     _behavior      = behavior;
 }
Example #14
0
        /// <summary>
        /// Initializes the service with the contextual information in <paramref name="ctx"/>, this
        /// method must be invoked before executing any request that relies on this contextual information.
        /// The method also runs any custom initialization logic that is supplied by the implementing service.
        /// Any subsequent calls to this method will have no effect.
        /// </summary>
        protected async Task Initialize(CancellationToken cancellation = default)
        {
            // Don't run initialize more than once for the same context accessor
            if (_contextAccessorWhenInitialized != _contextAccessor)
            {
                _contextAccessorWhenInitialized = _contextAccessor;

                if (Behavior is null)
                {
                    throw new InvalidOperationException($"Bug: {GetType().Name}.{nameof(Behavior)} returned null.");
                }

                _userId = await Behavior.OnInitialize(_contextAccessor, cancellation);
            }
        }
Example #15
0
        public virtual async Task <int> OnInitialize(IServiceContextAccessor context, CancellationToken cancellation)
        {
            IsInitialized = true;
            bool isSilent = context.IsSilent;

            _isAnonymous = context.IsAnonymous;

            // Determine if this is a userless account
            if (_isAnonymous)
            {
                // (1) Call OnConnect...
                _tenantId = context.TenantId ?? throw new ServiceException($"Tenant id was not supplied.");
                _appRepo  = _repositoryFactory.GetRepository(_tenantId);

                // Retrieve the settings version and the definitions version
                var result = await _appRepo.OnConnect(
                    externalUserId : null,
                    userEmail : null,
                    isServiceAccount : false,
                    setLastActive : !isSilent,
                    cancellation : cancellation);

                // (2) Set the versions and mark this initializer as initialized
                _versions.SettingsVersion    = result.SettingsVersion.ToString();
                _versions.DefinitionsVersion = result.DefinitionsVersion.ToString();
                _versions.AreSet             = true;

                return(0); // No user Id
            }
            else
            {
                // (1) Extract information from the Context Accessor
                bool   isServiceAccount = context.IsServiceAccount;
                string externalId;
                string externalEmail = null;
                if (context.IsServiceAccount)
                {
                    externalId = context.ExternalClientId ??
                                 throw new InvalidOperationException($"The external client ID was not supplied.");
                }
                else
                {
                    // This is a human user, so the external Id and email are required
                    externalId = context.ExternalUserId ??
                                 throw new InvalidOperationException($"The external user ID was not supplied.");
                    externalEmail = context.ExternalEmail ??
                                    throw new InvalidOperationException($"The external user email was not supplied.");
                }

                _tenantId = context.TenantId ?? throw new ServiceException($"Tenant id was not supplied.");
                _appRepo  = _repositoryFactory.GetRepository(_tenantId);


                // (2) Call OnConnect...
                // The client sometimes makes ambient (silent) API calls, not in response to
                // user interaction, such calls should not update LastAccess of that user
                var result = await _appRepo.OnConnect(
                    externalUserId : externalId,
                    userEmail : externalEmail,
                    isServiceAccount : isServiceAccount,
                    setLastActive : !isSilent,
                    cancellation : cancellation);

                // (3) Make sure the user is a member of this tenant
                if (result.UserId == null)
                {
                    // Either 1) the user is not a member in the database, or 2) the database does not exist
                    // Either way we return the not-member exception so as not to convey information to an attacker
                    throw new ForbiddenException(notMember: true);
                }

                // Extract values from the result
                var userId       = result.UserId.Value;
                var dbExternalId = result.ExternalId;
                var dbEmail      = result.Email;

                // (4) If the user exists but new, set the External Id
                if (dbExternalId == null)
                {
                    // Only possible with human users
                    // 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)
                    await _adminRepo.DirectoryUsers__SetExternalIdByEmail(externalEmail, externalId);
                }

                // (5) Handle edge case
                else if (dbExternalId != externalId)
                {
                    // Only possible with human users

                    // 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 artificially 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.
                    throw new InvalidOperationException($"The sign-in email '{dbEmail}' already exists but with a different external Id. TenantId: {TenantId}.");
                }

                // (6) If the user's email address has changed at the identity server, update it locally
                else if (dbEmail != externalEmail && !isServiceAccount)
                {
                    await _appRepo.Users__SetEmailByUserId(userId, externalEmail);

                    await _adminRepo.DirectoryUsers__SetEmailByExternalId(externalId, externalEmail);

                    _logger.LogWarning($"A user's email has been updated from '{dbEmail}' to '{externalEmail}'. TenantId: {TenantId}.");
                }

                // (7) Set the versions and mark this initializer as initialized
                _versions.SettingsVersion     = result.SettingsVersion.ToString();
                _versions.DefinitionsVersion  = result.DefinitionsVersion.ToString();
                _versions.UserSettingsVersion = result.UserSettingsVersion?.ToString();
                _versions.PermissionsVersion  = result.PermissionsVersion?.ToString();
                _versions.AreSet = true;

                _userEmail = dbEmail;
                _userId    = userId;

                // (8) Return the user Id
                return(userId);
            }
        }
        public async Task <int> OnInitialize(IServiceContextAccessor context, CancellationToken cancellation)
        {
            // (1) Extract context
            var    isServiceAccount = context.IsServiceAccount;
            bool   isSilent         = context.IsSilent;
            string externalId;
            string externalEmail = null;

            if (isServiceAccount)
            {
                externalId = context.ExternalClientId ??
                             throw new InvalidOperationException($"The external client ID was not supplied.");
            }
            else
            {
                // This is a human user, so the external Id and email are required
                externalId = context.ExternalUserId ??
                             throw new InvalidOperationException($"The external user ID was not supplied.");
                externalEmail = context.ExternalEmail ??
                                throw new InvalidOperationException($"The external user email was not supplied.");
            }

            // (2) Call OnConnect...
            var result = await _adminRepo.OnConnect(
                externalUserId : externalId,
                userEmail : externalEmail,
                isServiceAccount : isServiceAccount,
                setLastActive : !isSilent,
                cancellation : cancellation);

            // (3) Make sure the user is a member of the admin database
            if (result.UserId == null)
            {
                throw new ForbiddenException(notMember: true);
            }

            var userId       = result.UserId.Value;
            var dbExternalId = result.ExternalId;
            var dbEmail      = result.Email;

            // (4) If the user exists but new, set the External Id
            if (dbExternalId == null)
            {
                using var trx = TransactionFactory.ReadCommitted();

                await _adminRepo.AdminUsers__SetExternalIdByUserId(userId, externalId);

                await _adminRepo.DirectoryUsers__SetExternalIdByEmail(externalEmail, externalId);

                trx.Complete();
            }

            else if (dbExternalId != 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.
                throw new InvalidOperationException($"The sign-in email '{dbEmail}' already exists but with a different external Id. TenantId: Admin.");
            }

            // (5) If the user's email address has changed at the identity server, update it locally
            else if (dbEmail != externalEmail && !isServiceAccount)
            {
                using var trx = TransactionFactory.ReadCommitted();

                await _adminRepo.AdminUsers__SetEmailByUserId(userId, externalEmail);

                await _adminRepo.DirectoryUsers__SetEmailByExternalId(externalId, externalEmail);

                _logger.LogWarning($"An admin user's email has been updated from '{dbEmail}' to '{externalEmail}'.");

                trx.Complete();
            }

            // (6) Set the versions and mark this initializer as initialized
            _versions.UserSettingsVersion = result.UserSettingsVersion?.ToString();
            _versions.PermissionsVersion  = result.PermissionsVersion?.ToString();
            _versions.AreSet = true;

            _userEmail = dbEmail;
            _userId    = userId;

            IsInitialized = true;

            // (7) Return the user Id
            return(userId);
        }
Example #17
0
 public Task <int> OnInitialize(IServiceContextAccessor contextAccessor, CancellationToken _) => Task.FromResult(0);
Example #18
0
 public StatusController(StatusService service, IServiceContextAccessor accessor)
 {
     _service  = service;
     _accessor = accessor;
 }
Example #19
0
 public ExceptionsFilter(ILogger <ExceptionsFilter> logger, IServiceContextAccessor accessor, IStringLocalizer <Strings> localizer)
 {
     _logger    = logger;
     _accessor  = accessor;
     _localizer = localizer;
 }
Example #20
0
 public StatusService(ApplicationServiceBehavior behavior, IServiceContextAccessor accessor) : base(accessor)
 {
     _behavior = behavior;
 }
Example #21
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ServiceBase"/> class.
 /// </summary>
 /// <param name="contextAccessor"></param>
 public ServiceBase(IServiceContextAccessor contextAccessor)
 {
     // Default
     SetContext(contextAccessor);
 }
Example #22
0
 /// <summary>
 /// Overrides the default <see cref="IServiceContextAccessor"/> with a custom one.
 /// </summary>
 public void SetContext(IServiceContextAccessor contextAccessor)
 {
     _contextAccessor = contextAccessor;
 }
 public async Task <int> OnInitialize(IServiceContextAccessor contextAccessor, CancellationToken cancellation)
 {
     return(await _adminBehavior.OnInitialize(contextAccessor, cancellation));
 }
Example #24
0
 public WampOperation(ILogger <WampOperation> logger, IServiceContextAccessor serviceContextAccessor, IServiceScopeFactory scopeFactory)
 {
     this.logger = logger;
     this.serviceContextAccessor = serviceContextAccessor;
     this.scopeFactory           = scopeFactory;
 }