diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index a2bf261e..4392d6c2 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -3658,7 +3658,7 @@ public List> getProjectItemsList(Container c, User u, int pr { UserSchema schema = getSndUserSchema(c, u); - SQLFragment sql = new SQLFragment("SELECT pi.ProjectItemId, pi.superPkgId, p.pkgId, p.description, p.modified, p.narrative FROM "); + SQLFragment sql = new SQLFragment("SELECT pi.ProjectItemId, pi.superPkgId, pi.Active, p.pkgId, p.description, p.modified, p.narrative FROM "); sql.append(schema.getTable(SNDSchema.PROJECTITEMS_TABLE_NAME, null, true, false), "pi"); sql.append(" JOIN "); sql.append(schema.getTable(SNDSchema.PROJECTS_TABLE_NAME, null, true, false), "pr"); @@ -4044,14 +4044,12 @@ private Map> getBulkEventData(Container c, Map> nextLevelSuperPkgs = getNextLevelEventDataSuperPkgs(eventData, childEventData, currentLevelSuperPkgs, includeEmptySubPackages, isEmptyEventDataAndHasChildPackages); + Map> nextLevelSuperPkgs = getNextLevelEventDataSuperPkgs(eventData, childEventData, currentLevelSuperPkgs, includeEmptySubPackages, hasEmptySubpackages); if (nextLevelSuperPkgs != null && !nextLevelSuperPkgs.isEmpty()) { // Recursion for next child level of sub packages @@ -4187,7 +4185,7 @@ private Map> getNextLevelEventDataSuperPkgs( eventDataSuperPkgIds = new ArrayList<>(); } - AtomicInteger emptyEventDataId = new AtomicInteger(0); + AtomicInteger emptyEventDataId = new AtomicInteger(0); if (includeEmptySubPackages && nextLevelEventDataSuperPkgs.containsKey(eventData.getEventId())) { childSuperPkgs.values().stream() diff --git a/src/org/labkey/snd/SNDModule.java b/src/org/labkey/snd/SNDModule.java index 5df2d94b..37a5813d 100644 --- a/src/org/labkey/snd/SNDModule.java +++ b/src/org/labkey/snd/SNDModule.java @@ -42,11 +42,13 @@ import org.labkey.snd.security.roles.SNDBasicSubmitterRole; import org.labkey.snd.security.roles.SNDDataAdminRole; import org.labkey.snd.security.roles.SNDDataReviewerRole; +import org.labkey.snd.security.roles.SNDEditorRole; import org.labkey.snd.security.roles.SNDPackageEditorRole; import org.labkey.snd.security.roles.SNDPackageViewerRole; import org.labkey.snd.security.roles.SNDProjectEditorRole; import org.labkey.snd.security.roles.SNDProjectViewerRole; import org.labkey.snd.security.roles.SNDReaderRole; +import org.labkey.snd.security.roles.SNDViewerRole; import java.util.Collection; import java.util.Collections; @@ -97,6 +99,8 @@ protected void init() RoleManager.registerRole(new SNDPackageEditorRole(), true); RoleManager.registerRole(new SNDProjectViewerRole(), true); RoleManager.registerRole(new SNDProjectEditorRole(), true); + RoleManager.registerRole(new SNDViewerRole(), true); + RoleManager.registerRole(new SNDEditorRole(), true); } diff --git a/src/org/labkey/snd/SNDUserSchema.java b/src/org/labkey/snd/SNDUserSchema.java index a8f318cb..b4c9e458 100644 --- a/src/org/labkey/snd/SNDUserSchema.java +++ b/src/org/labkey/snd/SNDUserSchema.java @@ -29,7 +29,6 @@ import org.labkey.api.query.SimpleUserSchema; import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; -import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.roles.Role; import org.labkey.snd.query.AttributeDataTable; import org.labkey.snd.query.CategoriesTable; @@ -44,6 +43,7 @@ import org.labkey.snd.query.PackagesTable; import org.labkey.snd.query.ProjectsTable; import org.labkey.snd.query.SuperPackagesTable; +import org.labkey.snd.security.permissions.SNDViewerPermission; import java.util.Collections; import java.util.Map; @@ -136,7 +136,11 @@ public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) @Override public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) { - return new EventsTable(schema, SNDSchema.getInstance().getTableInfoEvents(), cf).init(); + if (schema.getContainer().hasPermission(schema.getUser(), SNDViewerPermission.class, schema.getContextualRoles())) + { + return new EventsTable(schema, SNDSchema.getInstance().getTableInfoEvents(), cf).init(); + } + return null; } }, EventNotes @@ -144,7 +148,7 @@ public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) @Override public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) { - if (schema.getContainer().hasPermission(schema.getUser(), AdminPermission.class, schema.getContextualRoles())) + if (schema.getContainer().hasPermission(schema.getUser(), SNDViewerPermission.class, schema.getContextualRoles())) { return new EventNotesTable(schema, SNDSchema.getInstance().getTableInfoEventNotes(), cf).init(); } @@ -157,7 +161,7 @@ public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) @Override public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) { - if (schema.getContainer().hasPermission(schema.getUser(), AdminPermission.class, schema.getContextualRoles())) + if (schema.getContainer().hasPermission(schema.getUser(), SNDViewerPermission.class, schema.getContextualRoles())) { return new EventDataTable(schema, SNDSchema.getInstance().getTableInfoEventData(), cf).init(); } @@ -170,7 +174,7 @@ public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) @Override public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) { - if (schema.getContainer().hasPermission(schema.getUser(), AdminPermission.class, schema.getContextualRoles())) + if (schema.getContainer().hasPermission(schema.getUser(), SNDViewerPermission.class, schema.getContextualRoles())) { return new AttributeDataTable(schema, cf); } @@ -183,7 +187,7 @@ public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) @Override public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) { - if (schema.getContainer().hasPermission(schema.getUser(), AdminPermission.class, schema.getContextualRoles())) + if (schema.getContainer().hasPermission(schema.getUser(), SNDViewerPermission.class, schema.getContextualRoles())) { return new PackageAttributeTable(schema, cf); } @@ -212,7 +216,7 @@ public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) @Override public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) { - if (schema.getContainer().hasPermission(schema.getUser(), AdminPermission.class, schema.getContextualRoles())) + if (schema.getContainer().hasPermission(schema.getUser(), SNDViewerPermission.class, schema.getContextualRoles())) { return new EventsCacheTable(schema, SNDSchema.getInstance().getTableInfoEventsCache(), cf).init(); } diff --git a/src/org/labkey/snd/query/AttributeDataTable.java b/src/org/labkey/snd/query/AttributeDataTable.java index ec4768b2..4bb2d036 100644 --- a/src/org/labkey/snd/query/AttributeDataTable.java +++ b/src/org/labkey/snd/query/AttributeDataTable.java @@ -44,7 +44,6 @@ import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; import org.labkey.api.security.UserPrincipal; -import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.Permission; import org.labkey.api.settings.AppProps; import org.labkey.api.snd.SNDService; @@ -52,6 +51,7 @@ import org.labkey.snd.SNDManager; import org.labkey.snd.SNDSchema; import org.labkey.snd.SNDUserSchema; +import org.labkey.snd.security.permissions.SNDViewerPermission; import java.io.IOException; import java.util.ArrayList; @@ -138,7 +138,7 @@ public SQLFragment getFromSQL(String alias) @Override public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) { - return getContainer().hasPermission(user, AdminPermission.class, getUserSchema().getContextualRoles()); + return getContainer().hasPermission(user, SNDViewerPermission.class, getUserSchema().getContextualRoles()); } @Override diff --git a/src/org/labkey/snd/query/EventDataTable.java b/src/org/labkey/snd/query/EventDataTable.java index c3f7f290..ae258ae8 100644 --- a/src/org/labkey/snd/query/EventDataTable.java +++ b/src/org/labkey/snd/query/EventDataTable.java @@ -37,12 +37,12 @@ import org.labkey.api.query.SimpleUserSchema; import org.labkey.api.security.User; import org.labkey.api.security.UserPrincipal; -import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.Permission; import org.labkey.api.settings.AppProps; import org.labkey.api.snd.SNDService; import org.labkey.snd.SNDManager; import org.labkey.snd.SNDUserSchema; +import org.labkey.snd.security.permissions.SNDViewerPermission; import java.io.IOException; import java.sql.SQLException; @@ -68,7 +68,7 @@ public EventDataTable(SNDUserSchema schema, TableInfo table, ContainerFilter cf) @Override public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) { - return getContainer().hasPermission(user, AdminPermission.class, getUserSchema().getContextualRoles()); + return getContainer().hasPermission(user, SNDViewerPermission.class, getUserSchema().getContextualRoles()); } @Override diff --git a/src/org/labkey/snd/query/EventNotesTable.java b/src/org/labkey/snd/query/EventNotesTable.java index f4da1635..8e097a45 100644 --- a/src/org/labkey/snd/query/EventNotesTable.java +++ b/src/org/labkey/snd/query/EventNotesTable.java @@ -29,11 +29,11 @@ import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; import org.labkey.api.security.UserPrincipal; -import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.Permission; import org.labkey.api.snd.SNDService; import org.labkey.snd.SNDManager; import org.labkey.snd.SNDUserSchema; +import org.labkey.snd.security.permissions.SNDViewerPermission; import java.io.IOException; import java.util.List; @@ -113,6 +113,6 @@ public int mergeRows(User user, Container container, DataIteratorBuilder rows, B @Override public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) { - return getContainer().hasPermission(user, AdminPermission.class, getUserSchema().getContextualRoles()); + return getContainer().hasPermission(user, SNDViewerPermission.class, getUserSchema().getContextualRoles()); } } diff --git a/src/org/labkey/snd/query/EventsCacheTable.java b/src/org/labkey/snd/query/EventsCacheTable.java index 4b319ae5..c0ec01e7 100644 --- a/src/org/labkey/snd/query/EventsCacheTable.java +++ b/src/org/labkey/snd/query/EventsCacheTable.java @@ -22,10 +22,10 @@ import org.labkey.api.query.QueryUpdateService; import org.labkey.api.query.SimpleUserSchema; import org.labkey.api.security.UserPrincipal; -import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.Permission; import org.labkey.snd.SNDUserSchema; import org.labkey.api.snd.PlainTextNarrativeDisplayColumn; +import org.labkey.snd.security.permissions.SNDViewerPermission; import java.util.ArrayList; import java.util.List; @@ -56,7 +56,7 @@ public EventsCacheTable(SNDUserSchema schema, TableInfo table, ContainerFilter c @Override public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) { - return getContainer().hasPermission(user, AdminPermission.class, getUserSchema().getContextualRoles()); + return getContainer().hasPermission(user, SNDViewerPermission.class, getUserSchema().getContextualRoles()); } @Override diff --git a/src/org/labkey/snd/query/EventsTable.java b/src/org/labkey/snd/query/EventsTable.java index 0dbaf40d..5b511a67 100644 --- a/src/org/labkey/snd/query/EventsTable.java +++ b/src/org/labkey/snd/query/EventsTable.java @@ -16,6 +16,7 @@ package org.labkey.snd.query; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerFilter; @@ -30,6 +31,8 @@ import org.labkey.api.query.SimpleUserSchema.SimpleTable; import org.labkey.api.query.ValidationException; import org.labkey.api.security.User; +import org.labkey.api.security.UserPrincipal; +import org.labkey.api.security.permissions.Permission; import org.labkey.api.snd.Event; import org.labkey.api.snd.SNDService; import org.labkey.snd.NarrativeAuditProvider; @@ -38,6 +41,7 @@ import org.labkey.snd.SNDUserSchema; import org.labkey.snd.security.QCStateActionEnum; import org.labkey.snd.security.SNDSecurityManager; +import org.labkey.snd.security.permissions.SNDViewerPermission; import java.io.IOException; import java.sql.SQLException; @@ -209,4 +213,9 @@ protected Map deleteRow(User user, Container container, Map perm) + { + return getContainer().hasPermission(user, SNDViewerPermission.class, getUserSchema().getContextualRoles()); + } } diff --git a/src/org/labkey/snd/query/PackageAttributeTable.java b/src/org/labkey/snd/query/PackageAttributeTable.java index 91344567..06311efe 100644 --- a/src/org/labkey/snd/query/PackageAttributeTable.java +++ b/src/org/labkey/snd/query/PackageAttributeTable.java @@ -25,13 +25,13 @@ import org.labkey.api.query.QueryUpdateService; import org.labkey.api.query.QueryUpdateServiceException; import org.labkey.api.security.UserPrincipal; -import org.labkey.api.security.permissions.AdminPermission; import org.labkey.api.security.permissions.Permission; import org.labkey.api.snd.PackageDomainKind; import org.labkey.api.util.HtmlString; import org.labkey.snd.SNDManager; import org.labkey.snd.SNDSchema; import org.labkey.snd.SNDUserSchema; +import org.labkey.snd.security.permissions.SNDViewerPermission; import java.sql.SQLException; import java.util.ArrayList; @@ -251,7 +251,7 @@ public SQLFragment getFromSQL(String alias) @Override public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) { - return getContainer().hasPermission(user, AdminPermission.class, getUserSchema().getContextualRoles()); + return getContainer().hasPermission(user, SNDViewerPermission.class, getUserSchema().getContextualRoles()); } } diff --git a/src/org/labkey/snd/security/SNDSecurityManager.java b/src/org/labkey/snd/security/SNDSecurityManager.java index 86c5309e..41583c0c 100644 --- a/src/org/labkey/snd/security/SNDSecurityManager.java +++ b/src/org/labkey/snd/security/SNDSecurityManager.java @@ -35,6 +35,8 @@ import org.labkey.api.security.SecurityPolicy; import org.labkey.api.security.SecurityPolicyManager; import org.labkey.api.security.User; +import org.labkey.api.security.impersonation.ImpersonationContext; +import org.labkey.api.security.impersonation.RoleImpersonationContextFactory; import org.labkey.api.security.permissions.Permission; import org.labkey.api.security.roles.Role; import org.labkey.api.snd.Category; @@ -206,7 +208,24 @@ public Map getAllSecurityRoles() private boolean hasPermission(User u, Category category, QCStateActionEnum action, QCStateEnum qcState) { Permission perm = action.getPermission(qcState); - return perm != null && category.hasPermission(u, perm.getClass()); + if (perm == null) + { + return false; + } + + Set roles = Set.of(); + + // SND has permissions bound to SND categories which can be assigned to packages (domains). Impersonating roles is used + // in automated and manual testing to verify this behavior. The behavior of role impersonation was changed in core + // labkey to only check for roles related to containers. This is a workaround to go back to checking all roles. + ImpersonationContext impersonationContext = u.getImpersonationContext(); + if (impersonationContext instanceof RoleImpersonationContextFactory.RoleImpersonationContext context) + { + roles = context.getRoles().getRoles(); + } + + return SecurityManager.hasAllPermissions(this.getClass().getName() + ":" + category.getResourceName(), + category, u, Set.of(perm.getClass()), roles); } diff --git a/src/org/labkey/snd/security/permissions/SNDEditorPermission.java b/src/org/labkey/snd/security/permissions/SNDEditorPermission.java new file mode 100644 index 00000000..14af6fdf --- /dev/null +++ b/src/org/labkey/snd/security/permissions/SNDEditorPermission.java @@ -0,0 +1,16 @@ +package org.labkey.snd.security.permissions; + +import org.labkey.api.security.permissions.AbstractPermission; + + +/** + * Created by thawkins on 8/6/2024. + */ +public class SNDEditorPermission extends AbstractPermission +{ + public SNDEditorPermission() + { + super("SNDEditorPermission", "This is the base permission required to Edit SND Data."); + } + +} diff --git a/src/org/labkey/snd/security/permissions/SNDViewerPermission.java b/src/org/labkey/snd/security/permissions/SNDViewerPermission.java new file mode 100644 index 00000000..d5ab0cc0 --- /dev/null +++ b/src/org/labkey/snd/security/permissions/SNDViewerPermission.java @@ -0,0 +1,16 @@ +package org.labkey.snd.security.permissions; + +import org.labkey.api.security.permissions.AbstractPermission; + + +/** + * Created by thawkins on 8/6/2024. + */ +public class SNDViewerPermission extends AbstractPermission +{ + public SNDViewerPermission() + { + super("SNDViewerPermission", "This is the base permission required to view SND Data."); + } + +} \ No newline at end of file diff --git a/src/org/labkey/snd/security/roles/SNDEditorRole.java b/src/org/labkey/snd/security/roles/SNDEditorRole.java new file mode 100644 index 00000000..442540f4 --- /dev/null +++ b/src/org/labkey/snd/security/roles/SNDEditorRole.java @@ -0,0 +1,23 @@ +package org.labkey.snd.security.roles; + +import org.labkey.api.security.roles.AbstractModuleScopedRole; +import org.labkey.snd.SNDModule; +import org.labkey.snd.security.permissions.SNDEditorPermission; +import org.labkey.snd.security.permissions.SNDViewerPermission; + +/** + * Created by thawkins on 8/6/2024. + */ +public class SNDEditorRole extends AbstractModuleScopedRole +{ + public SNDEditorRole() + { + super("SND Data Editors", "Users with this role are permitted to Edit SND data.", + SNDModule.class, + SNDViewerPermission.class, + SNDEditorPermission.class + ); + + + } +} \ No newline at end of file diff --git a/src/org/labkey/snd/security/roles/SNDViewerRole.java b/src/org/labkey/snd/security/roles/SNDViewerRole.java new file mode 100644 index 00000000..caf54d88 --- /dev/null +++ b/src/org/labkey/snd/security/roles/SNDViewerRole.java @@ -0,0 +1,21 @@ +package org.labkey.snd.security.roles; + +import org.labkey.api.security.roles.AbstractModuleScopedRole; +import org.labkey.snd.SNDModule; +import org.labkey.snd.security.permissions.SNDViewerPermission; + +/** + * Created by thawkins on 8/6/2024. + */ +public class SNDViewerRole extends AbstractModuleScopedRole +{ + public SNDViewerRole() + { + super("SND Data Viewers", "Users with this role are permitted to view SND data.", + SNDModule.class, + SNDViewerPermission.class + ); + + + } +} \ No newline at end of file diff --git a/webapp/snd/test/data.js b/webapp/snd/test/data.js index d91a6743..1746c34a 100644 --- a/webapp/snd/test/data.js +++ b/webapp/snd/test/data.js @@ -560,7 +560,8 @@ name: 'Valid Save Event with super package and trigger unit type conversion', roles: ['org.labkey.api.security.roles.ReaderRole', 'org.labkey.api.security.roles.EditorRole', - 'org.labkey.snd.security.roles.SNDBasicSubmitterRole'], + 'org.labkey.snd.security.roles.SNDBasicSubmitterRole', + 'org.labkey.snd.security.roles.SNDEditorRole'], jsonData: { eventId: 1800001, subjectId: "2", @@ -1677,7 +1678,8 @@ name: 'Failed Insert Event: Wrong permissions. Reader inserting Completed event.', roles: ['org.labkey.api.security.roles.ReaderRole', 'org.labkey.api.security.roles.EditorRole', - 'org.labkey.snd.security.roles.SNDReaderRole'], + 'org.labkey.snd.security.roles.SNDReaderRole', + 'org.labkey.snd.security.roles.SNDEditorRole'], jsonData: { eventId: 1800007, subjectId: "25", @@ -1714,7 +1716,8 @@ name: 'Failed Update Event: Wrong permission. Basic submitter updating to rejected', roles: ['org.labkey.api.security.roles.ReaderRole', 'org.labkey.api.security.roles.EditorRole', - 'org.labkey.snd.security.roles.SNDBasicSubmitterRole'], + 'org.labkey.snd.security.roles.SNDBasicSubmitterRole', + 'org.labkey.snd.security.roles.SNDEditorRole'], jsonData: { eventId: 1800008, subjectId: "25", @@ -1751,7 +1754,8 @@ name: 'Failed Insert Event: Wrong permission. Reviewer inserting In Progress data.', roles: ['org.labkey.api.security.roles.ReaderRole', 'org.labkey.api.security.roles.EditorRole', - 'org.labkey.snd.security.roles.SNDDataReviewerRole'], + 'org.labkey.snd.security.roles.SNDDataReviewerRole', + 'org.labkey.snd.security.roles.SNDEditorRole'], jsonData: { eventId: 1800008, subjectId: "25", diff --git a/webapp/snd/test/driver.js b/webapp/snd/test/driver.js index 1994e95d..44a8720a 100644 --- a/webapp/snd/test/driver.js +++ b/webapp/snd/test/driver.js @@ -1113,6 +1113,9 @@ else if (LABKEY.Utils.isObject(result)) { finish(undefined, result); } + else if (json?.event?.exception?.message) { + finish('Test "' + test.name + '" failed. ' + json.event.exception.message); + } else { finish('Test "' + test.name + '" failed. Test should specify a reason for failure or return true.'); } diff --git a/webapp/snd/test/tests.js b/webapp/snd/test/tests.js index e5fa1ecf..fc63cf8f 100644 --- a/webapp/snd/test/tests.js +++ b/webapp/snd/test/tests.js @@ -348,7 +348,7 @@ return true; } - LABKEY.handleFailure(response, name + " - Stack Trace"); + LABKEY.handleSndFailure(response, name + " - Stack Trace"); return false; } } @@ -563,7 +563,7 @@ return true; } - LABKEY.handleFailure(response, name + " - Stack Trace"); + LABKEY.handleSndFailure(response, name + " - Stack Trace"); return false; } } @@ -586,7 +586,7 @@ return true; } - LABKEY.handleFailure(response, name + " - Stack Trace"); + LABKEY.handleSndFailure(response, name + " - Stack Trace"); return false; } } @@ -630,7 +630,7 @@ return true; } - LABKEY.handleFailure(response, name + " - Stack Trace"); + LABKEY.handleSndFailure(response, name + " - Stack Trace"); return false; } } @@ -655,7 +655,7 @@ return true; } - LABKEY.handleFailure(response, name + " - Stack Trace"); + LABKEY.handleSndFailure(response, name + " - Stack Trace"); return false; } } @@ -692,7 +692,8 @@ name: 'Delete Event: Correct permission. Data admin role.', roles: ['org.labkey.api.security.roles.ReaderRole', 'org.labkey.api.security.roles.EditorRole', - 'org.labkey.snd.security.roles.SNDDataAdminRole'], + 'org.labkey.snd.security.roles.SNDDataAdminRole', + 'org.labkey.snd.security.roles.SNDViewerRole'], run : function() { return{ @@ -713,7 +714,7 @@ return true; } - LABKEY.handleFailure(response, name + " - Stack Trace"); + LABKEY.handleSndFailure(response, name + " - Stack Trace"); return false; } } @@ -782,7 +783,7 @@ return true; } - LABKEY.handleFailure(response, test + " - Stack Trace"); + LABKEY.handleSndFailure(response, test + " - Stack Trace"); return false; } };