package divconq.service.db; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import org.joda.time.DateTime; import divconq.bus.IService; import divconq.bus.Message; import divconq.db.DataRequest; import divconq.db.IDatabaseManager; import divconq.db.ObjectFinalResult; import divconq.db.ObjectResult; import divconq.db.ReplicatedDataRequest; import divconq.db.common.AddGroupRequest; import divconq.db.common.AddUserRequest; import divconq.db.common.RequestFactory; import divconq.db.common.UpdateGroupRequest; import divconq.db.common.UpdateUserRequest; import divconq.db.common.UsernameLookupRequest; import divconq.db.query.LoadRecordRequest; import divconq.db.query.SelectDirectRequest; import divconq.db.query.SelectFields; import divconq.db.update.InsertRecordRequest; import divconq.db.update.RetireRecordRequest; import divconq.db.update.ReviveRecordRequest; import divconq.db.update.UpdateRecordRequest; import divconq.hub.Hub; import divconq.hub.HubEvents; import divconq.io.LocalFileStore; import divconq.lang.op.FuncResult; import divconq.lang.op.OperationContext; import divconq.lang.op.OperationContextBuilder; import divconq.lang.op.UserContext; import divconq.mod.ExtensionBase; import divconq.schema.DbProc; import divconq.struct.CompositeStruct; import divconq.struct.ListStruct; import divconq.struct.RecordStruct; import divconq.struct.Struct; import divconq.util.IOUtil; import divconq.util.StringUtil; import divconq.work.TaskRun; import divconq.xml.XAttribute; import divconq.xml.XElement; import divconq.xml.XmlReader; public class CoreDataServices extends ExtensionBase implements IService { @Override public void handle(TaskRun request) { Message msg = (Message) request.getTask().getParams(); String feature = msg.getFieldAsString("Feature"); String op = msg.getFieldAsString("Op"); RecordStruct rec = msg.getFieldAsRecord("Body"); OperationContext tc = OperationContext.get(); UserContext uc = tc.getUserContext(); IDatabaseManager db = Hub.instance.getDatabase(); if (db == null) { request.errorTr(443); request.complete(); return; } // ========================================================= // users // ========================================================= if ("Users".equals(feature)) { if ("LoadSelf".equals(op) || "LoadUser".equals(op)) { LoadRecordRequest req = new LoadRecordRequest() .withTable("dcUser") .withId("LoadUser".equals(op) ? rec.getFieldAsString("Id") : uc.getUserId()) .withNow() .withSelect(new SelectFields() .withField("Id") .withField("dcUsername", "Username") .withField("dcFirstName", "FirstName") .withField("dcLastName", "LastName") .withForeignField("dcGroup", "Groups", "dcName") .withField("dcEmail", "Email") .withField("dcBackupEmail", "BackupEmail") .withField("dcLocale", "Locale") .withField("dcChronology", "Chronology") .withField("dcDescription", "Description") .withField("dcConfirmed", "Confirmed") .withField("dcAuthorizationTag", "AuthorizationTags") ); db.submit(req, new ObjectFinalResult(request)); return; } if ("UpdateSelf".equals(op) || "UpdateUser".equals(op)) { final UpdateUserRequest req = new UpdateUserRequest("UpdateUser".equals(op) ? rec.getFieldAsString("Id") : uc.getUserId()); if (rec.hasField("Username")) req.setUsername(rec.getFieldAsString("Username")); if (rec.hasField("FirstName")) req.setFirstName(rec.getFieldAsString("FirstName")); if (rec.hasField("LastName")) req.setLastName(rec.getFieldAsString("LastName")); if (rec.hasField("Email")) req.setEmail(rec.getFieldAsString("Email")); if (rec.hasField("BackupEmail")) req.setBackupEmail(rec.getFieldAsString("BackupEmail")); if (rec.hasField("Locale")) req.setLocale(rec.getFieldAsString("Locale")); if (rec.hasField("Chronology")) req.setChronology(rec.getFieldAsString("Chronology")); if (rec.hasField("Password")) req.setPassword(rec.getFieldAsString("Password")); // not allowed for Self (see schema) if (rec.hasField("Confirmed")) req.setConfirmed(rec.getFieldAsBoolean("Confirmed")); // not allowed for Self (see schema) if (rec.hasField("Description")) req.setDescription(rec.getFieldAsString("Description")); // not allowed for Self (see schema) if (rec.hasField("AuthorizationTags")) req.setAuthorizationTags(rec.getFieldAsList("AuthorizationTags")); db.submit(req, new ObjectFinalResult(request)); return ; } if ("AddUser".equals(op)) { AddUserRequest req = new AddUserRequest(rec.getFieldAsString("Username")); if (rec.hasField("FirstName")) req.setFirstName(rec.getFieldAsString("FirstName")); if (rec.hasField("LastName")) req.setLastName(rec.getFieldAsString("LastName")); if (rec.hasField("Email")) req.setEmail(rec.getFieldAsString("Email")); if (rec.hasField("BackupEmail")) req.setBackupEmail(rec.getFieldAsString("BackupEmail")); if (rec.hasField("Locale")) req.setLocale(rec.getFieldAsString("Locale")); if (rec.hasField("Chronology")) req.setChronology(rec.getFieldAsString("Chronology")); if (rec.hasField("Password")) req.setPassword(rec.getFieldAsString("Password")); if (rec.hasField("Confirmed")) req.setConfirmed(rec.getFieldAsBoolean("Confirmed")); else req.setConfirmed(true); if (rec.hasField("ConfirmCode")) req.setConfirmCode(rec.getFieldAsString("ConfirmCode")); if (rec.hasField("Description")) req.setDescription(rec.getFieldAsString("Description")); if (rec.hasField("AuthorizationTags")) req.setAuthorizationTags(rec.getFieldAsList("AuthorizationTags")); db.submit(req, new ObjectFinalResult(request)); return; } if ("RetireSelf".equals(op) || "RetireUser".equals(op)) { db.submit(new RetireRecordRequest("dcUser", "RetireUser".equals(op) ? rec.getFieldAsString("Id") : uc.getUserId()), new ObjectResult() { @Override public void process(CompositeStruct result) { if ("RetireSelf".equals(op)) { // be sure we keep the domain id UserContext uc = request.getContext().getUserContext(); OperationContext.switchUser(request.getContext(), new OperationContextBuilder() .withGuestUserTemplate() .withDomainId(uc.getDomainId()) .toUserContext()); } request.complete(); } }); return ; } if ("ReviveUser".equals(op)) { db.submit(new ReviveRecordRequest("dcUser", rec.getFieldAsString("Id")), new ObjectFinalResult(request)); return ; } if ("SetUserAuthTags".equals(op)) { ListStruct users = rec.getFieldAsList("Users"); ListStruct tags = rec.getFieldAsList("AuthorizationTags"); db.submit(RequestFactory.makeSet("dcUser", "dcAuthorizationTag", users, tags), new ObjectFinalResult(request)); return ; } if ("AddUserAuthTags".equals(op)) { ListStruct users = rec.getFieldAsList("Users"); ListStruct tags = rec.getFieldAsList("AuthorizationTags"); db.submit(RequestFactory.addToSet("dcUser", "dcAuthorizationTag", users, tags), new ObjectFinalResult(request)); return ; } if ("RemoveUserAuthTags".equals(op)) { ListStruct users = rec.getFieldAsList("Users"); ListStruct tags = rec.getFieldAsList("AuthorizationTags"); db.submit(RequestFactory.removeFromSet("dcUser", "dcAuthorizationTag", users, tags), new ObjectFinalResult(request)); return ; } if ("UsernameLookup".equals(op)) { db.submit(new UsernameLookupRequest(rec.getFieldAsString("Username")), new ObjectFinalResult(request)); return ; } // use with discretion if ("ListUsers".equals(op)) { db.submit( new SelectDirectRequest() .withTable("dcUser") .withSelect(new SelectFields() .withField("Id") .withField("dcUsername", "Username") .withField("dcFirstName", "FirstName") .withField("dcLastName", "LastName") .withField("dcEmail", "Email")), new ObjectFinalResult(request)); return ; } } // ========================================================= // domains // ========================================================= if ("Domains".equals(feature)) { if ("LoadDomain".equals(op) || "MyLoadDomain".equals(op)) { LoadRecordRequest req = new LoadRecordRequest() .withTable("dcDomain") .withId("MyLoadDomain".equals(op) ? uc.getDomainId() : rec.getFieldAsString("Id")) .withNow() .withSelect(new SelectFields() .withField("Id") .withField("dcTitle", "Title") .withField("dcAlias", "Alias") .withField("dcDescription", "Description") .withField("dcObscureClass", "ObscureClass") .withField("dcName", "Names") ); req.withDomain("MyLoadDomain".equals(op) ? uc.getDomainId() : rec.getFieldAsString("Id")); db.submit(req, new ObjectFinalResult(request)); return; } if ("UpdateDomain".equals(op) || "MyUpdateDomain".equals(op)) { ReplicatedDataRequest req = new UpdateRecordRequest() .withTable("dcDomain") .withId("MyUpdateDomain".equals(op) ? uc.getDomainId() : rec.getFieldAsString("Id")) .withConditionallySetFields(rec, "Title", "dcTitle", "Alias", "dcAlias", "Description", "dcDescription", "ObscureClass", "dcObscureClass") .withConditionallySetList(rec, "Names", "dcName"); req.withDomain("MyUpdateDomain".equals(op) ? uc.getDomainId() : rec.getFieldAsString("Id")); db.submit(req, new ObjectResult() { @Override public void process(CompositeStruct result) { Hub.instance.fireEvent(HubEvents.DomainUpdated, rec.getFieldAsString("Id")); request.returnValue(result); } }); return ; } if ("AddDomain".equals(op)) { ReplicatedDataRequest req = new InsertRecordRequest() .withTable("dcDomain") .withConditionallySetFields(rec, "Title", "dcTitle", "Alias", "dcAlias", "Description", "dcDescription", "ObscureClass", "dcObscureClass") .withSetList("dcName", rec.getFieldAsList("Names")); db.submit(req, new ObjectResult() { @Override public void process(CompositeStruct result) { LocalFileStore fs = Hub.instance.getPublicFileStore(); if (fs != null) { Path dspath = fs.getFilePath().resolve("dcw/" + rec.getFieldAsString("Alias") + "/"); try { Files.createDirectories(dspath.resolve("files")); Files.createDirectories(dspath.resolve("galleries")); Files.createDirectories(dspath.resolve("www")); } catch (IOException x) { request.error("Unable to create directories for new domain: " + x); request.returnEmpty(); return; } Path cpath = dspath.resolve("config/settings.xml"); XElement domainsettings = new XElement("Settings", new XElement("Web", new XAttribute("UI", "Custom"), new XAttribute("SiteTitle", rec.getFieldAsString("Title")), new XAttribute("SiteAuthor", rec.getFieldAsString("Title")), new XAttribute("SiteCopyright", new DateTime().getYear() + ""), new XElement("Package", new XAttribute("Name", "dcWeb") ), new XElement("Package", new XAttribute("Name", "dc/dcCms") ) ) ); IOUtil.saveEntireFile(cpath, domainsettings.toString(true)); } Hub.instance.fireEvent(HubEvents.DomainAdded, ((RecordStruct)result).getFieldAsString("Id")); request.returnValue(result); } }); return; } if ("ImportDomain".equals(op)) { String alias = rec.getFieldAsString("Alias"); LocalFileStore fs = Hub.instance.getPublicFileStore(); if (fs == null) { request.error("Public file store not enabled."); request.complete(); return; } Path dspath = fs.getFilePath().resolve("dcw/" + alias + "/"); Path cpath = dspath.resolve("config/settings.xml"); if (Files.notExists(cpath)) { request.error("Settings file not present."); request.complete(); return; } FuncResult<XElement> sres = XmlReader.loadFile(cpath, false); if (sres.hasErrors()) { request.complete(); return; } XElement domainsettings = sres.getResult(); String title = domainsettings.getAttribute("Title"); String desc = ""; XElement del = domainsettings.find("Description"); if (del != null) desc = del.getValue(); String fdesc = desc; String obs = ""; XElement oel = domainsettings.find("ObscureClass"); if (oel != null) obs = oel.getValue(); if (StringUtil.isEmpty(obs)) obs = "divconq.util.BasicSettingsObfuscator"; String fobs = obs; ListStruct dnames = new ListStruct(); for (XElement del2 : domainsettings.selectAll("Domain")) dnames.addItem(del2.getAttribute("Name")); DataRequest req = new DataRequest("dcLoadDomains"); // must be in root .withRootDomain(); // use root for this request db.submit(req, new ObjectResult() { @Override public void process(CompositeStruct result) { // if this fails the hub cannot start if (this.hasErrors()) { request.complete(); return; } ListStruct domains = (ListStruct) result; for (Struct d : domains.getItems()) { RecordStruct drec = (RecordStruct) d; String did = drec.getFieldAsString("Id"); String dalais = drec.getFieldAsString("Alias"); if (!dalais.equals(alias)) continue; ReplicatedDataRequest req = new UpdateRecordRequest() .withTable("dcDomain") .withId(did) .withUpdateField("dcTitle", title) .withUpdateField("dcAlias", alias) .withUpdateField("dcDescription", fdesc) .withUpdateField("dcObscureClass", fobs) .withSetList("dcName", dnames); // updates execute on the domain directly req.withDomain(did); db.submit(req, new ObjectResult() { @Override public void process(CompositeStruct result) { Hub.instance.fireEvent(HubEvents.DomainUpdated, did); request.returnValue(new RecordStruct().withField("Id", did)); } }); return; } ReplicatedDataRequest req = new InsertRecordRequest() .withTable("dcDomain") .withUpdateField("dcTitle", title) .withUpdateField("dcAlias", alias) .withUpdateField("dcDescription", fdesc) .withUpdateField("dcObscureClass", fobs) .withSetList("dcName", dnames); db.submit(req, new ObjectResult() { @Override public void process(CompositeStruct result) { Hub.instance.fireEvent(HubEvents.DomainAdded, ((RecordStruct)result).getFieldAsString("Id")); request.returnValue(result); } }); } }); return; } if ("RetireDomain".equals(op)) { db.submit(new RetireRecordRequest("dcDomain", rec.getFieldAsString("Id")).withDomain(rec.getFieldAsString("Id")), new ObjectFinalResult(request)); return ; } if ("ReviveDomain".equals(op)) { db.submit(new ReviveRecordRequest("dcDomain", rec.getFieldAsString("Id")).withDomain(rec.getFieldAsString("Id")), new ObjectFinalResult(request)); return ; } } // ========================================================= // groups // ========================================================= if ("Groups".equals(feature)) { if ("LoadGroup".equals(op)) { LoadRecordRequest req = new LoadRecordRequest() .withTable("dcGroup") .withId(rec.getFieldAsString("Id")) .withNow() .withSelect(new SelectFields() .withField("Id") .withField("dcName", "Name") .withField("dcDescription", "Description") .withReverseForeignField("Users", "dcUser", "dcGroup", "dcUsername") .withField("dcAuthorizationTag", "AuthorizationTags") ); db.submit(req, new ObjectFinalResult(request)); return ; } if ("UpdateGroup".equals(op)) { final UpdateGroupRequest req = new UpdateGroupRequest(rec.getFieldAsString("Id")); if (rec.hasField("Name")) req.setName(rec.getFieldAsString("Name")); if (rec.hasField("Description")) req.setDescription(rec.getFieldAsString("Description")); if (rec.hasField("AuthorizationTags")) req.setAuthorizationTags(rec.getFieldAsList("AuthorizationTags")); db.submit(req, new ObjectFinalResult(request)); return ; } if ("AddGroup".equals(op)) { final AddGroupRequest req = new AddGroupRequest(rec.getFieldAsString("Name")); if (rec.hasField("Description")) req.setDescription(rec.getFieldAsString("Description")); if (rec.hasField("AuthorizationTags")) req.setAuthorizationTags(rec.getFieldAsList("AuthorizationTags")); db.submit(req, new ObjectFinalResult(request)); return ; } if ("RetireGroup".equals(op)) { db.submit(new RetireRecordRequest("dcGroup", rec.getFieldAsString("Id")), new ObjectFinalResult(request)); return ; } if ("ReviveGroup".equals(op)) { db.submit(new ReviveRecordRequest("dcGroup", rec.getFieldAsString("Id")), new ObjectFinalResult(request)); return ; } if ("SetGroupAuthTags".equals(op)) { final ListStruct groups = rec.getFieldAsList("Groups"); final ListStruct tags = rec.getFieldAsList("AuthorizationTags"); db.submit(RequestFactory.makeSet("dcGroup", "dcAuthorizationTag", groups, tags), new ObjectFinalResult(request)); return ; } if ("AddGroupAuthTags".equals(op)) { final ListStruct groups = rec.getFieldAsList("Groups"); final ListStruct tags = rec.getFieldAsList("AuthorizationTags"); db.submit(RequestFactory.addToSet("dcGroup", "dcAuthorizationTag", groups, tags), new ObjectFinalResult(request)); return ; } if ("RemoveGroupAuthTags".equals(op)) { final ListStruct groups = rec.getFieldAsList("Groups"); final ListStruct tags = rec.getFieldAsList("AuthorizationTags"); db.submit(RequestFactory.removeFromSet("dcGroup", "dcAuthorizationTag", groups, tags), new ObjectFinalResult(request)); return ; } // use with discretion if ("ListGroups".equals(op)) { db.submit( new SelectDirectRequest() .withTable("dcGroup") .withSelect(new SelectFields() .withField("Id") .withField("dcName", "Name")), new ObjectFinalResult(request)); return ; } if ("SetUsersToGroups".equals(op)) { final ListStruct groups = rec.getFieldAsList("Groups"); final ListStruct users = rec.getFieldAsList("Users"); db.submit(RequestFactory.makeSet("dcUser", "dcGroup", users, groups), new ObjectFinalResult(request)); return ; } if ("AddUsersToGroups".equals(op)) { final ListStruct groups = rec.getFieldAsList("Groups"); final ListStruct users = rec.getFieldAsList("Users"); db.submit(RequestFactory.addToSet("dcUser", "dcGroup", users, groups), new ObjectFinalResult(request)); return ; } if ("RemoveUsersFromGroups".equals(op)) { final ListStruct groups = rec.getFieldAsList("Groups"); final ListStruct users = rec.getFieldAsList("Users"); db.submit(RequestFactory.removeFromSet("dcUser", "dcGroup", users, groups), new ObjectFinalResult(request)); return ; } } // ========================================================= // globals // ========================================================= if ("Globals".equals(feature)) { if ("DollarO".equals(op)) { DataRequest req = new DataRequest("dcKeyQuery") .withParams(rec); db.submit(req, new ObjectFinalResult(request)); return ; } if ("Kill".equals(op)) { DataRequest req = new DataRequest("dcKeyKill") .withParams(rec); db.submit(req, new ObjectFinalResult(request)); return ; } } // ========================================================= // database directly // ========================================================= if ("Database".equals(feature)) { if ("ExecuteProc".equals(op)) { String proc = rec.getFieldAsString("Proc"); DbProc pdef = request.getContext().getSchema().getDbProc(proc); if (!request.getContext().getUserContext().isTagged(pdef.securityTags)) { request.errorTr(434); request.complete(); return; } DataRequest req = new DataRequest(proc) .withParams(rec.getFieldAsComposite("Params")); db.submit(req, new ObjectFinalResult(request)); return ; } } request.errorTr(441, this.serviceName(), feature, op); request.complete(); } }