public async Task <bool> Handle(DataAccessRequest <IEnvironment> message, IOutboundPort <GenericDataResponse <IEnvironment> > outputPort) { switch (message.Strategy) { case DataAccessRequest <IEnvironment> .AcquisitionStrategy.All: // Acquire all associated environments, null-cascade throughout var user = await _userStore.GetUserById(message.UserId); if (user != null) { await _userStore.LoadEnvironments(user); } var envs = user?.Environments; foreach (var env in envs) { await _envStore.LoadControllerFor(env); await _envStore.LoadPetFor(env); } // Compose a response. var response = new GenericDataResponse <IEnvironment> { Result = envs?.ToList() }; outputPort.Handle(response); return(envs != null); default: throw new ArgumentOutOfRangeException(nameof(message.Strategy)); } }
public async Task <bool> Handle(MigratePetRequest message, IOutboundPort <BlankResponse> outputPort) { // load user - verify they have access to both env and pet specified. var user = await _userStore.GetUserById(message.UserId); if (user == null) { return(false); } await _userStore.LoadEnvironments(user); await _userStore.LoadPets(user); var canProceed = user.Pets.Select(p => p.Id).Contains(message.PetId) && user.Environments.Select(e => e.Id).Contains(message.EnvId); if (!canProceed) { return(false); } var pet = user.Pets.First(p => p.Id == message.PetId); var targetEnv = user.Environments.First(env => env.Id == message.EnvId); await _petStore.MigratePet(pet, targetEnv); return(true); }
public async Task <bool> Handle(CreatePetRequest message, IOutboundPort <NewEntityResponse <int> > outputPort) { var user = await _userStore.GetUserById(message.User); if (user == null) { return(false); } // Compose a new Pet instance: var pet = _entityFactory.GetPetBuilder() .SetName(message.Name) .SetSpecies(await _store.GetSpeciesById(message.SpeciesType)) .SetMorph(message.Morph) .SetOwner(user) .Build(); var id = await _store.Create(pet); var response = new NewEntityResponse <int> { Id = id }; outputPort.Handle(response); return(true); }
public async Task <bool> Handle(CreateSpeciesRequest message, IOutboundPort <NewEntityResponse <int> > outputPort) { var species = _entityFactory.GetSpeciesBuilder() .SetName(message.Name) .SetScientificName(message.ScientificName) .SetLatitude(message.Latitude) .SetLongitude(message.Longitude) .Build(); // validate some stuff: if (string.IsNullOrWhiteSpace(species.Name) || string.IsNullOrEmpty(species.ScientificName) || Math.Abs(species.DefaultLatitude) > 90.0 || Math.Abs(species.DefaultLongitude) > 180.0) { return(false); } var response = new NewEntityResponse <int> { Id = await _petStore.Create(species) }; outputPort.Handle(response); return(true); }
public async Task <bool> Handle(DataAccessRequest <ISpecies> message, IOutboundPort <GenericDataResponse <ISpecies> > outputPort) { // no need to validate user - species data not user specific. var response = new GenericDataResponse <ISpecies>(); switch (message.Strategy) { case DataAccessRequest <ISpecies> .AcquisitionStrategy.All: response.Result = await _petStore.GetSpeciesInfo(); break; case DataAccessRequest <ISpecies> .AcquisitionStrategy.Range: response.Result = (await _petStore.GetSpeciesInfo()) .Where(message.SelectionPredicate) .ToArray(); break; case DataAccessRequest <ISpecies> .AcquisitionStrategy.Single: response.Result = new[] { (await _petStore.GetSpeciesInfo()).FirstOrDefault(message.SelectionPredicate) }; break; default: throw new ArgumentOutOfRangeException(nameof(message)); } outputPort.Handle(response); return(true); }
public async Task <bool> Handle(ModifyUserRolesRequest message, IOutboundPort <BlankResponse> outputPort) { // fetch the user instance. var user = await _userStore.GetUserById(message.UserId); if (user == null) { return(false); } switch (message.Mode) { case ModifyUserRolesRequest.ModificationMode.Add: await _userStore.AddToRoles(user, message.DesiredRoles); return(true); case ModifyUserRolesRequest.ModificationMode.Set: await _userStore.RemoveRolesFromUser(user); await _userStore.AddToRoles(user, message.DesiredRoles); return(true); default: return(false); } }
public async Task <bool> Handle(DataAccessRequest <IEnvDataSample> message, IOutboundPort <GenericDataResponse <IEnvDataSample> > outputPort) { // verify the user exists: var user = await _userStore.GetUserById(message.UserId); if (user == null) { return(false); } await _userStore.LoadEnvironments(user); var response = new GenericDataResponse <IEnvDataSample>(); var samples = new List <IEnvDataSample>(); foreach (var env in user.Environments) { await _environmentStore.LoadSamplesFor(env); switch (message.Strategy) { case DataAccessRequest <IEnvDataSample> .AcquisitionStrategy.All: samples.AddRange(env.EnvDataSamples); break; case DataAccessRequest <IEnvDataSample> .AcquisitionStrategy.Range: samples.AddRange(env.EnvDataSamples.Where(message.SelectionPredicate)); break; case DataAccessRequest <IEnvDataSample> .AcquisitionStrategy.Single: // this one is a bit different, if we find a match amongst any environments, // shortcut a return after processing it. var sample = env.EnvDataSamples.FirstOrDefault(message.SelectionPredicate); if (sample != null) { response.Result = new[] { sample }; outputPort.Handle(response); return(true); } break; default: throw new ArgumentOutOfRangeException(nameof(message), "Strategy not recognized."); } } // in the event of strategy being single, samples will be empty, which is a suitable response. response.Result = samples; outputPort.Handle(response); return(true); }
public async Task <bool> Handle(SampleAccessRequest message, IOutboundPort <GenericDataResponse <IEnvDataSample> > outputPort) { var response = new GenericDataResponse <IEnvDataSample>(); var user = await _userStore.GetUserById(message.UserId); switch (message.Selector) { case SampleAccessRequest.SelectionCriteria.User: if (user == null) { return(false); } response.Result = await _sampleStore.GetRangeByUser(user, message.RangeStart, message.RangeEnd); break; case SampleAccessRequest.SelectionCriteria.Environment: // ensure the user has access to see this environment's data: await _userStore.LoadEnvironments(user); var env = user.Environments.FirstOrDefault(e => e.Id == message.EnvId); if (env == null) { return(false); // env doesn't exist or user can't access it. } response.Result = await _sampleStore.GetRangeByEnv(env, message.RangeStart, message.RangeEnd); break; case SampleAccessRequest.SelectionCriteria.Pet: // ensure the user owns the pet in question: await _userStore.LoadPets(user); var pet = user.Pets.FirstOrDefault(p => p.Id == message.PetId); if (pet == null) { return(false); // pet doesn't exist or user can't access its data. } response.Result = await _sampleStore.GetRangeByPet(pet, message.RangeStart, message.RangeEnd); break; default: throw new ArgumentException("Invalid selection mode", nameof(message)); } outputPort.Handle(response); return(true); }
public async Task <bool> Handle(BlobStorageRequest message, IOutboundPort <BlobUriResponse> outputPort) { // Trust user guid? Ideally only authenticated controllers are getting here. if (message.UserId == Guid.Empty) { return(false); } // compose blob name if necessary if (string.IsNullOrWhiteSpace(message.BlobName) && message.Mode == BlobStorageRequest.Operation.Write) { message.BlobName = $"{message.UserId}_{DateTime.UtcNow}_{Guid.NewGuid()}"; } // can't read a non-existent blob. if (string.IsNullOrWhiteSpace(message.BlobName)) { return(false); } if (message.Mode == BlobStorageRequest.Operation.Write) { if (message.Content == null) { return(false); // can't write with no content. } await _blobStore.WriteBlob(message.Category, message.BlobName, null, message.Content); } var uri = await _blobStore.GetBlobReadUri(message.Category, message.BlobName, 60, 60); // call through to blob store method. var response = new BlobUriResponse { Uri = uri, ExpiresIn = 60 }; // send back response outputPort.Handle(response); return(true); }
public async Task <bool> Handle(LoginRequest message, IOutboundPort <LoginResponse> outputPort) { var user = await _userStore.GetUserByName(message.Username); if (user == null || !await _userStore.CheckPassword(user, message.Password)) { return(false); } var roles = await _userStore.GetRoles(user); var roleClaims = new List <Claim>(); roleClaims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r))); var identity = _claimsComposer.ComposeIdentity(user, roleClaims); var response = new LoginResponse { UserName = user.Name, AccessToken = new AccessToken { Token = _minter.Mint(identity, TokenType.UserAccess), ExpiresIn = _minter.Options.TokenLifespan, }, RefreshToken = new RefreshToken { Token = _minter.Mint(identity, TokenType.Refresh), ExpiresAt = (DateTime.UtcNow + TimeSpan.FromSeconds(_minter.Options.RefreshTokenLifespan)), IssuedTo = user.Guid, IssuedBy = Dns.GetHostName(), AccessCapacity = RoleValues.User } }; // Push the new refresh token to data store user.RefreshTokens.Add(response.RefreshToken); await _userStore.UpdateUser(user); outputPort.Handle(response); return(true); }
public async Task <bool> Handle(DataAccessRequest <IController> message, IOutboundPort <GenericDataResponse <IController> > outputPort) { // Load user, verify they exist. var user = await _userStore.GetUserById(message.UserId); if (user == null) { return(false); } // pipe on back through the port. var response = new GenericDataResponse <IController> { // Load controllers (Note: userStore impls must include controller.Environments relation here) // associated to that user. Result = await _userStore.LoadControllers(user) }; outputPort.Handle(response); return(true); }
public async Task <bool> Handle(DataAccessRequest <IPet> message, IOutboundPort <GenericDataResponse <IPet> > outputPort) { var user = await _userStore.GetUserById(message.UserId); if (user == null) { return(false); } await _userStore.LoadPets(user); // Load species data - its trivial to include. foreach (var pet in user.Pets) { await _petStore.LoadSpecies(pet); } var response = new GenericDataResponse <IPet>(); switch (message.Strategy) { case DataAccessRequest <IPet> .AcquisitionStrategy.All: response.Result = user.Pets.ToImmutableList(); outputPort.Handle(response); return(true); case DataAccessRequest <IPet> .AcquisitionStrategy.Range: response.Result = user.Pets.Where(message.SelectionPredicate).ToImmutableList(); outputPort.Handle(response); return(true); case DataAccessRequest <IPet> .AcquisitionStrategy.Single: response.Result = new List <IPet>(new[] { user.Pets.FirstOrDefault(message.SelectionPredicate) }); outputPort.Handle(response); return(true); default: throw new ArgumentOutOfRangeException(nameof(message), "Unrecognized strategy"); } }
public async Task <bool> Handle(NewSampleRequest message, IOutboundPort <BlankResponse> outputPort) { // Verify that the specified user maps to the sample's environment. (e.g., can this user add samples to that // environment?) var user = await _userStore.GetUserById(message.UserId); if (user != null) { await _userStore.LoadEnvironments(user); } var env = user?.Environments.FirstOrDefault(e => e.Id == message.Sample.Environment); if (env == null) { return(false); // environment not owned by user or user not found. } await _envStore.LoadPetFor(env); // Fill in EnvDataSample entity instance. var sample = _entityFactory.GetSampleBuilder() .AddHotGlassMeasurement(message.Sample.HotGlass ?? 0) .AddHotMatMeasurement(message.Sample.HotMat ?? 0) .AddMidGlassMeasurement(message.Sample.MidGlass ?? 0) .AddColdGlassMeasurement(message.Sample.ColdGlass ?? 0) .AddColdMatMeasurement(message.Sample.ColdMat ?? 0) .SetInhabitant(env.Inhabitant) .SetEnvironment(env) .Build(); await _envStore.AddSample(sample); if (env.Inhabitant != null) { await _petStore.UpdateLatestSample(env.Inhabitant, sample); } return(true); }
public async Task <bool> Handle(ProviderGrantRequest message, IOutboundPort <LoginResponse> outputPort) { // Verify the user is valid var user = await _userStore.GetUserById(message.OnBehalfOf); if (user == null) { return(false); } // TODO: verify - in some manner - that this action request is valid beyond 'has a real user' // Mint a data access token. var response = new LoginResponse { UserName = null, // not necessary for Daemons AccessToken = new AccessToken { Token = _minter.Mint(_claimsComposer.ComposeIdentity(user), TokenType.DaemonAccess), ExpiresIn = _minter.Options.TokenLifespan }, RefreshToken = new RefreshToken { Token = _minter.Mint(_claimsComposer.ComposeIdentity(user), TokenType.Refresh), ExpiresAt = (DateTime.UtcNow + TimeSpan.FromSeconds(_minter.Options.RefreshTokenLifespan)), IssuedTo = user.Guid, IssuedBy = Dns.GetHostName(), AccessCapacity = AccessLevelValues.Daemon } }; user.RefreshTokens.Add(response.RefreshToken); await _userStore.UpdateUser(user); // signal success outputPort.Handle(response); return(true); }
public async Task <bool> Handle(CreateUserRequest message, IOutboundPort <NewUserResponse> outputPort) { // ensure username, email, are unused. if (await _userStore.GetUserByName(message.UserName) != null) { return(false); } // create a new entity instance. var user = _entityFactory.GetUserBuilder() .AddName(message.UserName) .AddEmail(message.Email) .Build(); var guid = await _userStore.CreateUser(user, message.Password); // use backing store to persist. NewUserResponse response = new NewUserResponse(); response.Id = (guid == Guid.Empty) ? null : guid.ToString(); outputPort.Handle(response); return(true); }
public async Task <bool> Handle(RegisterEnvironmentRequest message, IOutboundPort <NewEntityResponse <Guid> > outputPort) { var response = new NewEntityResponse <Guid>(); var user = await _userStore.GetUserById(message.Owner); if (user != null) { await _userStore.LoadEnvironments(user); } switch (message.CreateMode) { case RegisterEnvironmentRequest.Mode.Touch: // Check if user already has it: var env = user?.Environments?.FirstOrDefault(e => e.Id == message.MfgId); // check if we have this environment already - and that its associated to user // if not, we'll fall through to Create logic where we'll attempt creating it - erroring if already // present. if (env != null) { // This next line should be impossible - its just here to keep linting happy. if (env.Id == null) { throw new Exception("Malformed input on this entity"); } // we already have it and its associated to user. // should be same as message.MfgId response.Id = env.Id.Value; break; } else if (user == null) { return(false); // nothing to do, this is a mis-use. } else { // Apparently we gotta explicitly mimic fallthrough... goto case RegisterEnvironmentRequest.Mode.Create; } case RegisterEnvironmentRequest.Mode.Create: // Environments need an initial user: if (user == null) { return(false); } // check if it exists (blind firing a create call // may be a pretty hard crash.) var searchResult = await _envStore.GetById(message.MfgId); if (searchResult != null) { return(false); } // Compose a new entry entity var envEntity = _entityFactory.GetEnvironmentBuilder() .SetId(message.MfgId) .SetDescription(message.Description) .SetModelInfo(message.Model) .Build(); response.Id = await _envStore.Create(envEntity); // Associate to user: await _userStore.AddAssociationToEnv(user, envEntity); break; default: throw new ArgumentOutOfRangeException(nameof(message)); } outputPort.Handle(response); return(true); }
public async Task <bool> Handle(NodeUpdateRequest message, IOutboundPort <GenericDataResponse <IController> > outputPort) { var user = await _userStore.GetUserById(message.UserId); if (user == null) { return(false); // specified user doesn't exist, do nothing on their behalf. } var controller = await _controllerStore.GetById(message.ControllerId); if (message.Operation == NodeUpdateRequest.Mode.Create) { // if the controller exists - then it was owned by another user // if they haven't relinquished it then we don't want to re-author its owner. if (controller?.Owner != null) { if (controller.Owner != user) { return(false); } } // add controller (or a new instance) to the user: controller ??= _entityFactory.GetControllerBuilder() .SetId(message.ControllerId) .Build(); await _userStore.AddAssociationToController(user, controller); } else if (message.Operation == NodeUpdateRequest.Mode.PeerUpdate) { if (controller == null) { return(false); } // call through to RegisterEnvironmentUseCase? foreach (Guid envId in message.PeerIds) { // try loading the environment, update its association var env = await _environmentStore.GetById(envId); // Behavior here is TBD. Should environment devices just be 'whoever has posession it' // if so, should existing data associated to that environment be dumped? // if not, hos should we permit giving them out? etc. // Point is, I have some system-level planning to do here, but I need *something* // here for the time being: if (env == null) { continue; } if (env.Controller != controller) { await _controllerStore.ReParentEnvironment(controller, env); } // for now: not creating new Env entities in this use case. Expect it to be // handled separately } } var response = new GenericDataResponse <IController> { Result = new[] { controller } }; outputPort.Handle(response); return(true); }
public async Task <bool> Handle(PetImageRequest message, IOutboundPort <BlobUriResponse> outputPort) { // verify that the user has access to the specified pet: var user = await _userStore.GetUserById(message.UserId); if (user == null) { return(false); } await _userStore.LoadPets(user); var pet = user.Pets.FirstOrDefault(p => p.Id == message.PetId); if (pet == null) { return(false); } // All pet images go to the image container/category. const string category = Constants.StorageCategories.Images; string blobName; // set based on execution path. if (message.Update) { // Generate a new blob name (always) for pet images - ensures no conflict. blobName = $"{message.UserId}_{message.PetId}_{Guid.NewGuid()}"; // upload the blob to storage. await _blobStore.WriteBlob(category, blobName, message.MimeType, message.Content); // associate a new blob entry to the pet var entity = _entityFactory.GetBlobRecordBuilder() .SetCategory(category) .SetName(blobName) .Build(); await _petStore.UpdateImage(pet, entity); } else { // load the current record from pet: await _petStore.LoadImageRecord(pet); blobName = pet.ProfileImage?.BlobName; } // check that there exists a blob associated to pet by this point. if (blobName == null) { return(false); } var response = new BlobUriResponse { Uri = await _blobStore.GetBlobReadUri(category, blobName, 10, 30), ExpiresIn = 10 }; outputPort.Handle(response); return(true); }
public async Task <bool> Handle(TokenExchangeRequest message, IOutboundPort <LoginResponse> outputPort) { // Verify user exists. var user = await _store.GetUserById(Guid.Parse(message.UserId)); if (user == null) { return(false); } await _store.LoadRefreshTokens(user); // Verify refresh token specified by message is associated to user. var persistedToken = user.RefreshTokens .FirstOrDefault(rt => String.Compare(rt.Token, message.EncodedRefreshToken, StringComparison.Ordinal) == 0); if (persistedToken == null) { return(false); } var roles = await _store.GetRoles(user); var roleClaims = new List <Claim>(); roleClaims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r))); // Mint a new access token based on the access capacity specified by the refresh token. string mintedAccessToken; if (persistedToken.AccessCapacity == AccessLevelValues.User) { mintedAccessToken = _minter.Mint(_claimsComposer.ComposeIdentity(user, roleClaims), TokenType.UserAccess); } else if (persistedToken.AccessCapacity == AccessLevelValues.Daemon) { mintedAccessToken = _minter.Mint(_claimsComposer.ComposeIdentity(user), TokenType.DaemonAccess); } else { // Malformed? return(false); } // if a new refresh token is needed, mint a new one. var mintedRefreshToken = message.EncodedRefreshToken; // compose and submit response. var response = new LoginResponse { UserName = (persistedToken.AccessCapacity == AccessLevelValues.User) ? user.Name: null, AccessToken = new AccessToken { Token = mintedAccessToken, ExpiresIn = _minter.Options.TokenLifespan, }, RefreshToken = new RefreshToken { Token = mintedRefreshToken, ExpiresAt = (DateTime.UtcNow + TimeSpan.FromSeconds(_minter.Options.RefreshTokenLifespan)), IssuedTo = user.Guid, IssuedBy = Dns.GetHostName() } }; // TODO: if a new refresh token was issued, then push it to data store. outputPort.Handle(response); return(true); }