private RealmDto MakeDto(Realm r, bool details = false) { var result = new RealmDto { Name = r.Name, Owner = r.Owner.Name, Uri = Url.Link("GetRealmByName", new { realmName = r.Name }), Compiler = MakeDto(r.Factory), Privacy = r.PrivacyLevel, }; if (details) { result.Runtime = new RuntimeOptionsDto { Platform = "Glulx", }; result.ManifestUri = Url.Link("GetRealmAssetManifest", new { realmName = r.Name }); result.Acl = Array.ConvertAll(r.AccessList, e => new RealmAclEntryDto { User = e.Player.Name, Access = e.Level }); } return(result); }
public async Task <IHttpActionResult> PostNewRealmAsync(RealmDto newRealm) { // verify permission if (!Request.CheckAccess(GunchoResources.RealmActions.Create, GunchoResources.Realm, newRealm.Name)) { return(Forbidden()); } // fill in defaults if (newRealm.Compiler == null) { newRealm.Compiler = new CompilerOptionsDto() { Language = config.DefaultCompilerLanguage, Version = config.DefaultCompilerVersion, }; } newRealm.Owner = User.Identity.Name; // validate request var factory = realmsService.GetRealmFactories().SingleOrDefault(f => CompilerEqualsFactory(newRealm.Compiler, f)); if (factory == null) { ModelState.AddModelError("Compiler", "Invalid compiler settings."); } if (!ModelState.IsValid) { return(BadRequest(ModelState)); } if (realmsService.GetRealmByName(newRealm.Name) != null) { return(Conflict()); } // create the realm var realm = await realmsService.CreateRealmAsync(playersService.GetPlayerByName(User.Identity.Name), newRealm.Name, factory); if (realm == null) { // that's weird! if (realmsService.GetRealmByName(newRealm.Name) != null) { return(Conflict()); } else { return(BadRequest(ModelState)); } } // invoke the PUT handler to update any other settings var innerResult = (await PutRealmByNameAsync(newRealm.Name, newRealm)) as OkNegotiatedContentResult <RealmDto>; if (innerResult != null) { // TODO: if PUT failed, delete the realm and return an error return(Created(Url.Link("GetRealmByName", new { realmName = newRealm.Name }), innerResult.Content)); } innerResult = GetRealmByName(newRealm.Name) as OkNegotiatedContentResult <RealmDto>; return(Created(Url.Link("GetRealmByName", new { realmName = newRealm.Name }), GetRealmByName(newRealm.Name))); }
public async Task <IHttpActionResult> PutRealmByNameAsync(string realmName, RealmDto newSettings) { // TODO: use ETags for concurrency control var realm = realmsService.GetRealmByName(realmName); if (realm == null) { return(NotFound()); } if (!Request.CheckAccess(GunchoResources.RealmActions.Edit, GunchoResources.Realm, realmName)) { return(Forbidden()); } var checks = new Queue <Check <Realm> >(); var updates = new Queue <Action <Realm> >(); // TODO: don't modify Realm objects, do everything through service methods (see ProfilesController) if (newSettings.Name != null && newSettings.Name != realm.Name) { checks.Enqueue(new Check <Realm>( r => realmsService.IsValidNameChange(r.Name, newSettings.Name), "Name", "Invalid realm name.")); checks.Enqueue(new Check <Realm>( r => Request.CheckAccess( GunchoResources.RealmActions.Edit, GunchoResources.Realm, r.Name, GunchoResources.Field, GunchoResources.RealmFields.Name), "Name", "Permission denied.")); updates.Enqueue(r => r.Name = newSettings.Name); } if (newSettings.Owner != realm.Owner.Name) { checks.Enqueue(new Check <Realm>( r => Request.CheckAccess( GunchoResources.RealmActions.Edit, GunchoResources.Realm, r.Name, GunchoResources.Field, GunchoResources.RealmFields.Owner), "Owner", "Permission denied.")); checks.Enqueue(new Check <Realm>( r => playersService.GetPlayerByName(newSettings.Owner) != null, "Owner", "No such player.")); updates.Enqueue(r => r.Owner = playersService.GetPlayerByName(newSettings.Owner) ?? r.Owner); } // acl if (newSettings.Acl != null && !AclEqualsDto(realm.AccessList, newSettings.Acl)) { checks.Enqueue(new Check <Realm>( r => Request.CheckAccess( GunchoResources.RealmActions.Edit, GunchoResources.Realm, r.Name, GunchoResources.Field, GunchoResources.RealmFields.Acl), "Acl", "Permission denied.")); checks.Enqueue(new Check <Realm>( r => newSettings.Acl.All(e => playersService.GetPlayerByName(e.User) != null), "Acl", "No such player(s).")); updates.Enqueue(r => { try { r.AccessList = DtoToAcl(newSettings.Acl); } catch (InvalidOperationException) { // don't change ACL if some player names are invalid } }); } // privacy if (newSettings.Privacy != realm.PrivacyLevel) { checks.Enqueue(new Check <Realm>( r => Request.CheckAccess( GunchoResources.RealmActions.Edit, GunchoResources.Realm, r.Name, GunchoResources.Field, GunchoResources.RealmFields.Privacy), "Privacy", "Permission denied.")); updates.Enqueue(r => r.PrivacyLevel = newSettings.Privacy); } // compiler if (!CompilerEqualsFactory(newSettings.Compiler, realm.Factory)) { checks.Enqueue(new Check <Realm>( r => realmsService.GetRealmFactories().Count(f => CompilerEqualsFactory(newSettings.Compiler, f)) == 1, "Compiler", "Invalid compiler settings.")); updates.Enqueue(r => r.Factory = realmsService.GetRealmFactories().Single(f => CompilerEqualsFactory(newSettings.Compiler, f))); // TODO: recompile realm if compiler setting is changed } if (!ModelState.IsValid) { return(BadRequest(ModelState)); } var result = await realmsService.TransactionalUpdateAsync( realm, r => { bool ok = true; foreach (var check in checks) { if (!check.CheckFunc(r)) { ok = false; ModelState.AddModelError(check.ModelKey, check.ErrorMsg); } } if (!ok) { return(false); } foreach (var update in updates) { update(r); } return(true); }); if (result == false) { return(BadRequest(ModelState)); } return(GetRealmByName(realmName)); }