Issue #1412 Check file last modified times each time role data is requested
Change-Id: Ieae84ad3106e3aa0cf4d253a10c27e8c9c60ecaa Former-commit-id:088b0d4c08
[formerlyb1ce19e41d
] [formerly74d5a70c30
] [formerly037dabb683
[formerly74d5a70c30
[formerly b7e577237df9e157fdb218af48c41f6209ff2bfb]]] Former-commit-id:037dabb683
Former-commit-id: 4c1037f281d91482cf2e0798dc7d242a1877cfba [formerly6468029207
] Former-commit-id:fce3f8bec9
This commit is contained in:
parent
c80803948a
commit
837c1a0492
12 changed files with 343 additions and 35 deletions
|
@ -29,6 +29,8 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import com.raytheon.uf.common.localization.FileLocker.Type;
|
||||
import com.raytheon.uf.common.localization.ILocalizationAdapter.ListResponse;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel;
|
||||
|
@ -80,6 +82,7 @@ import com.raytheon.uf.common.status.UFStatus.Priority;
|
|||
* This was added as part of an effort to improve
|
||||
* localization performance but caused updated
|
||||
* files on the server not to be retrieved.
|
||||
* Jan 17, 2013 1412 djohnson Add jaxbMarshal.
|
||||
* </pre>
|
||||
*
|
||||
* @author njensen
|
||||
|
@ -635,6 +638,25 @@ public final class LocalizationFile implements Comparable<LocalizationFile> {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marshal the specified object into this file.
|
||||
*
|
||||
* @param obj
|
||||
* the object to marshal
|
||||
*
|
||||
* @param jaxbManager
|
||||
* the jaxbManager
|
||||
*/
|
||||
public void jaxbMarshal(Object obj, JAXBManager jaxbManager) throws LocalizationException{
|
||||
try {
|
||||
String xml = jaxbManager.marshalToXml(obj);
|
||||
write(xml.getBytes());
|
||||
} catch (JAXBException e) {
|
||||
throw new LocalizationException(
|
||||
"Unable to marshal the object to the file.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return context + IPathManager.SEPARATOR + path;
|
||||
|
|
|
@ -10,7 +10,8 @@ Require-Bundle: com.raytheon.uf.common.serialization;bundle-version="1.12.2",
|
|||
com.raytheon.uf.common.auth;bundle-version="1.12.1174",
|
||||
com.raytheon.uf.common.status;bundle-version="1.12.1174",
|
||||
com.raytheon.uf.common.localization;bundle-version="1.12.1174",
|
||||
com.raytheon.uf.common.serialization.comm;bundle-version="1.12.1174"
|
||||
com.raytheon.uf.common.serialization.comm;bundle-version="1.12.1174",
|
||||
org.apache.commons.lang;bundle-version="2.3.0"
|
||||
Export-Package: com.raytheon.uf.common.plugin.nwsauth,
|
||||
com.raytheon.uf.common.plugin.nwsauth.exception,
|
||||
com.raytheon.uf.common.plugin.nwsauth.user,
|
||||
|
|
|
@ -302,4 +302,21 @@ public class NwsRoleData implements ISerializableObject {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Application:").append(this.getApplication()).append("\n\n");
|
||||
sb.append("Users:\n").append(this.getUserList()).append("\n\n");
|
||||
sb.append("Permissions:\n").append(this.getPermissionList())
|
||||
.append("\n\n");
|
||||
sb.append("Roles:\n").append(this.getRoleList()).append("\n\n");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,4 +94,16 @@ public class PermissionXML implements ISerializableObject {
|
|||
public void setDescription(String description) {
|
||||
this.description = (description == null) ? null : description.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("id:").append(this.getId());
|
||||
sb.append("\ndescription:").append(this.getDescription());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,4 +117,19 @@ public class RoleXML implements ISerializableObject {
|
|||
public void addPermission(String permission) {
|
||||
this.permissionList.add(permission);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("roleId:").append(this.getRoleId());
|
||||
sb.append("\nroleDescription:").append(this.getRoleDescription());
|
||||
sb.append("\npermissionList:").append(this.getPermissionList());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ import javax.xml.bind.annotation.XmlAttribute;
|
|||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlElements;
|
||||
|
||||
import org.apache.commons.lang.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||
|
||||
import com.raytheon.uf.common.auth.user.IAuthenticationData;
|
||||
import com.raytheon.uf.common.auth.user.IUser;
|
||||
import com.raytheon.uf.common.auth.user.IUserId;
|
||||
|
@ -66,6 +69,17 @@ public class UserXML implements IUser, ISerializableObject {
|
|||
@XmlElements({ @XmlElement(name = "userRole", type = String.class) })
|
||||
private List<String> roleList = new ArrayList<String>();
|
||||
|
||||
public UserXML() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userId
|
||||
*/
|
||||
public UserXML(String userId) {
|
||||
setUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the userId
|
||||
*/
|
||||
|
@ -129,6 +143,30 @@ public class UserXML implements IUser, ISerializableObject {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof UserXML) {
|
||||
UserXML that = (UserXML) obj;
|
||||
|
||||
EqualsBuilder builder = new EqualsBuilder();
|
||||
builder.append(this.getUserId(), that.getUserId());
|
||||
return builder.isEquals();
|
||||
|
||||
}
|
||||
return super.equals(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder().append(this.getUserId()).toHashCode();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
|
@ -136,17 +174,10 @@ public class UserXML implements IUser, ISerializableObject {
|
|||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
final String nl = "\n";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.getUserId()).append(nl);
|
||||
|
||||
for (String role : this.roleList) {
|
||||
sb.append(" ").append(role).append(nl);
|
||||
}
|
||||
|
||||
for (String perm : permissionList) {
|
||||
sb.append(" ").append(perm).append(nl);
|
||||
}
|
||||
sb.append("userId:").append(this.getUserId());
|
||||
sb.append("\nroles:").append(this.getRoleList());
|
||||
sb.append("\npermissions:").append(this.getPermissionList());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ Require-Bundle: com.raytheon.uf.edex.auth;bundle-version="1.12.2",
|
|||
com.raytheon.uf.common.status;bundle-version="1.12.1174",
|
||||
com.raytheon.uf.common.localization,
|
||||
com.raytheon.uf.common.serialization.comm;bundle-version="1.12.1174",
|
||||
com.raytheon.uf.common.useradmin;bundle-version="1.0.0"
|
||||
com.raytheon.uf.common.useradmin;bundle-version="1.0.0",
|
||||
com.raytheon.uf.common.time;bundle-version="1.12.1174"
|
||||
Import-Package: com.raytheon.uf.common.localization,
|
||||
com.raytheon.uf.common.serialization,
|
||||
com.raytheon.uf.common.status,
|
||||
|
|
|
@ -19,9 +19,11 @@
|
|||
**/
|
||||
package com.raytheon.uf.edex.plugin.nwsauth;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
|
@ -40,6 +42,7 @@ import com.raytheon.uf.common.serialization.JAXBManager;
|
|||
import com.raytheon.uf.common.status.IUFStatusHandler;
|
||||
import com.raytheon.uf.common.status.UFStatus;
|
||||
import com.raytheon.uf.common.status.UFStatus.Priority;
|
||||
import com.raytheon.uf.common.time.util.TimeUtil;
|
||||
|
||||
/**
|
||||
* Uses localization data to determine role/permissions. Intentionally
|
||||
|
@ -51,7 +54,9 @@ import com.raytheon.uf.common.status.UFStatus.Priority;
|
|||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Jan 09, 2013 1412 djohnson Moved file writing from viz plugin to server-side.
|
||||
* Jan 09, 2013 1412 djohnson Moved file writing from viz plugin to server-side.
|
||||
* Jan 17, 2013 1412 djohnson Check files for having been modified each time data is requested,
|
||||
* in case they were written by another member of the cluster.
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
|
@ -69,14 +74,22 @@ class FileManager {
|
|||
|
||||
private final String ROLE_DIR = "roles";
|
||||
|
||||
private final Map<String, NwsRoleData> roleDataMap = new HashMap<String, NwsRoleData>();
|
||||
private final AtomicLong lastReadTime = new AtomicLong(-1L);
|
||||
|
||||
/**
|
||||
* Application name -> Role Data.
|
||||
*/
|
||||
private final ConcurrentMap<String, NwsRoleData> roleDataMap = new ConcurrentHashMap<String, NwsRoleData>();
|
||||
|
||||
/**
|
||||
* Application name -> LocalizationFile map.
|
||||
*/
|
||||
private final Map<String, LocalizationFile> roleFileMap = new HashMap<String, LocalizationFile>();
|
||||
private final ConcurrentMap<String, LocalizationFile> roleFileMap = new ConcurrentHashMap<String, LocalizationFile>();
|
||||
|
||||
private FileManager() {
|
||||
/**
|
||||
* Package-level visibility so tests can create new instances.
|
||||
*/
|
||||
FileManager() {
|
||||
readXML();
|
||||
}
|
||||
|
||||
|
@ -117,31 +130,54 @@ class FileManager {
|
|||
|
||||
private void readXML() {
|
||||
try {
|
||||
getJaxbManager();
|
||||
|
||||
IPathManager pm = PathManagerFactory.getPathManager();
|
||||
LocalizationContext[] contexts = new LocalizationContext[2];
|
||||
contexts[0] = pm.getContext(LocalizationType.COMMON_STATIC,
|
||||
LocalizationLevel.BASE);
|
||||
contexts[1] = pm.getContext(LocalizationType.COMMON_STATIC,
|
||||
LocalizationLevel.SITE);
|
||||
LocalizationFile[] roleFiles = pm.listFiles(contexts, ROLE_DIR,
|
||||
new String[] { ".xml" }, false, true);
|
||||
|
||||
LocalizationFile[] roleFiles = getUserRoleLocalizationFiles();
|
||||
boolean needToReadFiles = false;
|
||||
for (LocalizationFile lf : roleFiles) {
|
||||
NwsRoleData roleData = lf.jaxbUnmarshal(NwsRoleData.class,
|
||||
getJaxbManager());
|
||||
final long fileLastModified = lf.getFile().lastModified();
|
||||
final long lastTimeFilesWereRead = lastReadTime.get();
|
||||
|
||||
if (roleData != null) {
|
||||
this.roleDataMap.put(roleData.getApplication(), roleData);
|
||||
this.roleFileMap.put(roleData.getApplication(), lf);
|
||||
if (fileLastModified > lastTimeFilesWereRead) {
|
||||
needToReadFiles = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needToReadFiles) {
|
||||
for (LocalizationFile lf : roleFiles) {
|
||||
final long fileLastModified = lf.getFile().lastModified();
|
||||
final long lastTimeFilesWereRead = lastReadTime.get();
|
||||
|
||||
if (fileLastModified < lastTimeFilesWereRead) {
|
||||
continue;
|
||||
}
|
||||
NwsRoleData roleData = lf.jaxbUnmarshal(NwsRoleData.class,
|
||||
getJaxbManager());
|
||||
|
||||
if (roleData != null) {
|
||||
final String application = roleData.getApplication();
|
||||
this.roleDataMap.put(application, roleData);
|
||||
this.roleFileMap.put(application, lf);
|
||||
}
|
||||
}
|
||||
}
|
||||
lastReadTime.set(TimeUtil.currentTimeMillis());
|
||||
} catch (Exception e) {
|
||||
statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private LocalizationFile[] getUserRoleLocalizationFiles() {
|
||||
IPathManager pm = PathManagerFactory.getPathManager();
|
||||
LocalizationContext[] contexts = new LocalizationContext[2];
|
||||
contexts[0] = pm.getContext(LocalizationType.COMMON_STATIC,
|
||||
LocalizationLevel.BASE);
|
||||
contexts[1] = pm.getContext(LocalizationType.COMMON_STATIC,
|
||||
LocalizationLevel.SITE);
|
||||
LocalizationFile[] roleFiles = pm.listFiles(contexts, ROLE_DIR,
|
||||
new String[] { ".xml" }, false, true);
|
||||
return roleFiles;
|
||||
}
|
||||
|
||||
private JAXBManager getJaxbManager() throws JAXBException {
|
||||
if (jaxbManager == null) {
|
||||
jaxbManager = new JAXBManager(NwsRoleData.class,
|
||||
|
@ -154,13 +190,15 @@ class FileManager {
|
|||
* @return
|
||||
*/
|
||||
public Map<String, NwsRoleData> getRoleDataMap() {
|
||||
readXML();
|
||||
return roleDataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param roleDataWithChanges
|
||||
*/
|
||||
public void writeApplicationRoleData(Map<String, NwsRoleData> roleDataWithChanges) {
|
||||
public void writeApplicationRoleData(
|
||||
Map<String, NwsRoleData> roleDataWithChanges) {
|
||||
for (Entry<String, NwsRoleData> entry : roleDataWithChanges.entrySet()) {
|
||||
final String application = entry.getKey();
|
||||
roleDataMap.put(application, entry.getValue());
|
||||
|
|
|
@ -67,5 +67,6 @@
|
|||
<classpathentry combineaccessrules="false" kind="src" path="/com.raytheon.uf.common.stats"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/com.raytheon.uf.edex.stats"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/com.raytheon.uf.viz.plugin.nwsauth"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/com.raytheon.uf.edex.plugin.nwsauth"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
|
|
@ -165,7 +165,7 @@ public class TestPathManager extends PathManager {
|
|||
try {
|
||||
if (baselinedVersion.exists()) {
|
||||
if (baselinedVersion.isDirectory()) {
|
||||
savedFile.mkdirs();
|
||||
FileUtil.copyDirectory(baselinedVersion, savedFile);
|
||||
} else {
|
||||
FileUtil.copyFile(baselinedVersion, savedFile);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
/**
|
||||
* This software was developed and / or modified by Raytheon Company,
|
||||
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
||||
*
|
||||
* U.S. EXPORT CONTROLLED TECHNICAL DATA
|
||||
* This software product contains export-restricted data whose
|
||||
* export/transfer/disclosure is restricted by U.S. law. Dissemination
|
||||
* to non-U.S. persons whether in the United States or abroad requires
|
||||
* an export license or other authorization.
|
||||
*
|
||||
* Contractor Name: Raytheon Company
|
||||
* Contractor Address: 6825 Pine Street, Suite 340
|
||||
* Mail Stop B8
|
||||
* Omaha, NE 68106
|
||||
* 402.291.0100
|
||||
*
|
||||
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
||||
* further licensing information.
|
||||
**/
|
||||
package com.raytheon.uf.edex.plugin.nwsauth;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.raytheon.uf.common.localization.IPathManager;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel;
|
||||
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
|
||||
import com.raytheon.uf.common.localization.LocalizationFile;
|
||||
import com.raytheon.uf.common.localization.PathManagerFactory;
|
||||
import com.raytheon.uf.common.localization.PathManagerFactoryTest;
|
||||
import com.raytheon.uf.common.localization.exception.LocalizationException;
|
||||
import com.raytheon.uf.common.plugin.nwsauth.xml.NwsRoleData;
|
||||
import com.raytheon.uf.common.plugin.nwsauth.xml.UserXML;
|
||||
import com.raytheon.uf.common.serialization.JAXBManager;
|
||||
import com.raytheon.uf.common.time.util.TimeUtil;
|
||||
import com.raytheon.uf.common.time.util.TimeUtilTest;
|
||||
|
||||
/**
|
||||
* Test {@link FileManager}.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* SOFTWARE HISTORY
|
||||
*
|
||||
* Date Ticket# Engineer Description
|
||||
* ------------ ---------- ----------- --------------------------
|
||||
* Jan 17, 2013 1412 djohnson Initial creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @author djohnson
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
public class FileManagerTest {
|
||||
private static JAXBManager jaxbManager;
|
||||
|
||||
private final UserXML someUser = new UserXML("someUser");
|
||||
|
||||
private FileManager manager;
|
||||
|
||||
@BeforeClass
|
||||
public static void classSetup() throws JAXBException {
|
||||
jaxbManager = new JAXBManager(NwsRoleData.class);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
TimeUtilTest.freezeTime();
|
||||
PathManagerFactoryTest.initLocalization();
|
||||
manager = new FileManager();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
TimeUtilTest.resumeTime();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fileNewerOnDiskIsReadBeforeResponse()
|
||||
throws LocalizationException {
|
||||
|
||||
addUserToUserAdminFile();
|
||||
|
||||
verifyUserIsFoundWhenRoleDataRetrieved();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fileOlderOnDiskIsNotReadBeforeResponse()
|
||||
throws LocalizationException {
|
||||
|
||||
addUserToUserAdminFile();
|
||||
setUserAdminFileModifiedTimeToOneSecondAgo();
|
||||
|
||||
verifyUserIsNotFoundWhenRoleDataRetrieved();
|
||||
}
|
||||
|
||||
private void verifyUserIsFoundWhenRoleDataRetrieved() {
|
||||
final Map<String, NwsRoleData> roleDataMap = manager.getRoleDataMap();
|
||||
assertTrue(
|
||||
"Did not find the user added to the role data map!",
|
||||
roleDataMap.get("TestUserRoles").getUserList()
|
||||
.contains(someUser));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param someUser
|
||||
*/
|
||||
private void verifyUserIsNotFoundWhenRoleDataRetrieved() {
|
||||
final Map<String, NwsRoleData> roleDataMap = manager.getRoleDataMap();
|
||||
assertFalse(
|
||||
"Should not have found the user added to the role data map!",
|
||||
roleDataMap.get("TestUserRoles").getUserList()
|
||||
.contains(someUser));
|
||||
|
||||
}
|
||||
|
||||
private void addUserToUserAdminFile() throws LocalizationException {
|
||||
final LocalizationFile file = getTestUserAdminRolesLocalizationFile();
|
||||
NwsRoleData roleData = file.jaxbUnmarshal(NwsRoleData.class,
|
||||
jaxbManager);
|
||||
|
||||
roleData.getUserList().add(someUser);
|
||||
file.jaxbMarshal(roleData, jaxbManager);
|
||||
file.save();
|
||||
// The file was written out 1 second after we last read it
|
||||
file.getFile().setLastModified(
|
||||
TimeUtil.currentTimeMillis() + TimeUtil.MILLIS_PER_SECOND);
|
||||
}
|
||||
|
||||
private void setUserAdminFileModifiedTimeToOneSecondAgo() {
|
||||
// The file was written out 1 second before we last read it
|
||||
getTestUserAdminRolesLocalizationFile().getFile().setLastModified(
|
||||
TimeUtil.currentTimeMillis() - TimeUtil.MILLIS_PER_SECOND);
|
||||
}
|
||||
|
||||
private LocalizationFile getTestUserAdminRolesLocalizationFile() {
|
||||
IPathManager pathManager = PathManagerFactory.getPathManager();
|
||||
final LocalizationFile file = pathManager.getLocalizationFile(
|
||||
new LocalizationContext(LocalizationType.COMMON_STATIC,
|
||||
LocalizationLevel.SITE, "OAX"),
|
||||
"roles/testUserAdminRoles.xml");
|
||||
return file;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<nwsRoleData xmlns:ns2="group">
|
||||
<!-- AWIPS 2 User Admin Roles/Permissions file -->
|
||||
<application>TestUserRoles</application>
|
||||
<permission id="awips.user.admin">
|
||||
<description>
|
||||
This permission allows the user to access and edit AWIPS 2 User Administration
|
||||
</description>
|
||||
</permission>
|
||||
|
||||
<user userId="ALL">
|
||||
<userPermission>awips.user.admin</userPermission>
|
||||
</user>
|
||||
</nwsRoleData>
|
Loading…
Add table
Reference in a new issue