Skip to content
Commits on Source (4)
......@@ -6,9 +6,15 @@
<span v-if="group.active">{{group.groupName}}</span>
</li>
</ol>
<a v-if="currentGroup" :href="'group/status?groupId=' + currentGroup.groupId" :download="currentGroup.groupName + '.csv'" id="csv-status-download" title="Download CSV">
<font-awesome-icon icon="download"></font-awesome-icon>
</a>
<span id="breadcrumb-buttons">
<a href="#" v-on:click.stop.prevent="openEditGroupModal(currentGroup)" title="Edit" v-if="currentGroup && currentGroup.groupId !== 'ROOT'">
<font-awesome-icon icon="edit"></font-awesome-icon>
</a>
&nbsp;
<a v-if="currentGroup" :href="'group/status?groupId=' + currentGroup.groupId" :download="currentGroup.groupName + '.csv'" title="Download CSV">
<font-awesome-icon icon="download"></font-awesome-icon>
</a>
</span>
</nav>
</template>
......@@ -54,6 +60,10 @@ export default {
} else {
this.$store.dispatch('changeTab', 0);
}
},
openEditGroupModal: function(group) {
group.leaf = this.model.leaf;
this.$parent.$refs.editGroupModal.openEditGroupModal(group, false);
}
}
}
......@@ -65,7 +75,7 @@ export default {
position: relative;
}
#csv-status-download {
#breadcrumb-buttons {
position: absolute;
right: 18px;
top: 12px;
......
......@@ -23,13 +23,11 @@
<p v-if="model.groupsPanel.items.length === 0">No groups</p>
</div>
<Paginator :paginatedPanel="model.groupsPanel" :onUpdate="updatePagination" :paginatorInput="input" />
<EditGroupModal ref="editGroupModal" />
<ConfirmRemoveGroupModal ref="confirmRemoveGroupModal" />
</b-tab>
</template>
<script>
import EditGroupModal from './modals/EditGroupModal.vue';
import ConfirmRemoveGroupModal from './modals/ConfirmRemoveGroupModal.vue';
import Paginator from './Paginator.vue';
import { mapState } from 'vuex';
......@@ -39,7 +37,6 @@ import debounce from 'debounce'; // for delaying the input event (search filter)
export default {
name: 'GroupsPanel',
components: {
EditGroupModal,
ConfirmRemoveGroupModal,
Paginator
},
......@@ -52,7 +49,7 @@ export default {
this.$store.dispatch('openGroup', group.groupId);
},
openEditGroupModal: function(group) {
this.$refs.editGroupModal.openEditGroupModal(group);
this.$parent.$parent.$refs.editGroupModal.openEditGroupModal(group, true);
},
openRemoveGroupModal: function(group) {
this.$refs.confirmRemoveGroupModal.openRemoveGroupModal(group);
......
......@@ -18,6 +18,7 @@
<AddGroupModal />
<AddMemberModal />
<AddPermissionModal />
<EditGroupModal ref="editGroupModal" />
</div>
</template>
......@@ -30,6 +31,7 @@ import InvitedRegistrationPanel from './InvitedRegistrationPanel.vue'
import AddGroupModal from './modals/AddGroupModal.vue'
import AddMemberModal from './modals/AddMemberModal.vue'
import AddPermissionModal from './modals/AddPermissionModal.vue'
import EditGroupModal from './modals/EditGroupModal.vue';
import { mapState } from 'vuex';
export default {
......@@ -42,7 +44,8 @@ export default {
InvitedRegistrationPanel,
AddGroupModal,
AddMemberModal,
AddPermissionModal
AddPermissionModal,
EditGroupModal
},
computed: mapState({
model: state => state.model,
......
......@@ -7,7 +7,7 @@
<div class="col-md-4">
<b-row>
<b-col sm="5">
<label for="page-size">Page size:</label>
<label for="page-size" class="mt-2">Page size:</label>
</b-col>
<b-col sm="6">
<b-form-select id="page-size" v-model="paginatorInput.paginatorPageSize" :options="pageSizeOptions" v-on:change="changePageSize"></b-form-select>
......
......@@ -11,7 +11,7 @@
<b-navbar-nav class="ml-auto">
<b-nav-item href="help/index.html" target="blank_" class="mr-4">Help</b-nav-item>
<b-nav-form>
<b-form-input size="sm" class="mr-sm-2" placeholder="Search" v-model="input.genericSearch.filter" @keydown.native.enter.prevent="genericSearch"></b-form-input>
<b-form-input size="sm" class="mr-sm-2" placeholder="Search" v-model.trim="input.genericSearch.filter" @keydown.native.enter.prevent="genericSearch"></b-form-input>
<b-button size="sm" class="my-2 my-sm-0" type="button" v-on:click="genericSearch()">Search</b-button>
</b-nav-form>
<b-nav-item-dropdown :text="user" right v-if="user">
......
......@@ -5,7 +5,7 @@
<b-form-input v-model="newGroupName" id="new-group-name-input" ref="newGroupNameInput" class="w-75" aria-describedby="new-group-name-input-feedback" :state="newGroupNameState" v-on:input="resetError" @keydown.native.enter="addGroup">
</b-form-input>
<b-form-invalid-feedback id="new-group-name-input-feedback" class="text-right">{{newGroupNameError}}</b-form-invalid-feedback>
<b-form-checkbox class="mt-3 ml-3" v-model="leaf">is leaf</b-form-checkbox>
<b-form-checkbox class="mt-3 ml-3" v-model="allowChildGroups">allow child groups</b-form-checkbox>
</b-form>
</b-modal>
</template>
......@@ -27,13 +27,13 @@ export default {
return {
newGroupName: '',
newGroupNameError: '',
leaf: true
allowChildGroups: false
};
},
methods: {
resetModal: function() {
this.newGroupName = null;
this.leaf = true;
this.allowChildGroups = false;
this.resetError();
},
afterShow: function() {
......@@ -49,7 +49,7 @@ export default {
if (!this.newGroupName) {
this.newGroupNameError = "Group name is required";
} else {
client.addGroup(this.newGroupName, this.leaf, this.$store.state.input)
client.addGroup(this.newGroupName, !this.allowChildGroups, this.$store.state.input)
.then(res => {
this.$store.commit('updateGroupsPanel', res);
this.$bvModal.hide('add-group-modal');
......
......@@ -2,10 +2,10 @@
<b-modal id="edit-group-modal" title="Edit group" ok-title="Update" @ok="updateGroup">
<b-form inline>
<label class="w-25" for="new-group-name-input">Group name:</label>
<b-form-input v-model="newGroupName" id="new-group-name-input" class="w-75" aria-describedby="new-group-name-input-feedback" :state="newGroupNameState" v-on:input="resetError">
<b-form-input v-model="newGroupName" id="new-group-name-input" class="w-75" aria-describedby="new-group-name-input-feedback" :state="newGroupNameState" v-on:input="resetError" @keydown.native.enter.prevent="updateGroup">
</b-form-input>
<b-form-invalid-feedback id="new-group-name-input-feedback" class="text-right">{{newGroupNameError}}</b-form-invalid-feedback>
<b-form-checkbox class="mt-3 ml-3" v-model="leaf">is leaf</b-form-checkbox>
<b-form-checkbox class="mt-3 ml-3" v-model="allowChildGroups">allow child groups</b-form-checkbox>
</b-form>
</b-modal>
</template>
......@@ -29,18 +29,20 @@ export default {
oldGroupName: '',
newGroupName: '',
newGroupNameError: '',
leaf: false
allowChildGroups: false,
reloadChildren: false
};
},
methods: {
resetError: function() {
this.newGroupNameError = null;
},
openEditGroupModal: function(group) {
openEditGroupModal: function(group, reloadChildren) {
this.newGroupName = group.groupName;
this.groupId = group.groupId;
this.leaf = group.leaf;
this.allowChildGroups = !group.leaf;
this.$bvModal.show('edit-group-modal');
this.reloadChildren = reloadChildren;
},
updateGroup: function(event) {
// Prevent modal from closing
......@@ -56,9 +58,16 @@ export default {
return;
}
client.updateGroup(this.groupId, this.newGroupName, this.leaf, this.$store.state.input)
client.updateGroup(this.groupId, this.newGroupName, !this.allowChildGroups, this.$store.state.input)
.then(res => {
this.$store.commit('updateGroupsPanel', res);
if (this.reloadChildren) {
this.$store.commit('updateGroupsPanel', res);
} else {
this.$store.dispatch('updateCurrentGroup', {
newGroupName: this.newGroupName,
leaf: !this.allowChildGroups
});
}
this.$bvModal.hide('edit-group-modal');
})
.catch(res => {
......
......@@ -140,6 +140,13 @@ export default new Vuex.Store({
break;
}
},
updateCurrentGroup({ dispatch, state }, data) {
state.model.breadcrumbs[state.model.breadcrumbs.length - 1].groupName = data.newGroupName;
state.model.leaf = data.leaf;
if (state.input.tabIndex === 0 && state.model.leaf) {
dispatch('changeTab', 1);
}
},
openGroup({ commit, dispatch, state }, groupId) {
let input = state.input;
input.selectedGroupId = groupId;
......
......@@ -48,12 +48,6 @@
<artifactId>opencsv</artifactId>
<version>3.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
......
......@@ -160,8 +160,6 @@ public class InvitedRegistrationManager extends UserAwareComponent {
}
invitedRegistration.setUserId(getCurrentUserId());
// FIXME (workaround): separated update for user and done in order to use triggers
invitedRegistrationDAO.setRegistrationUser(invitedRegistration);
invitedRegistrationDAO.setRegistrationDone(invitedRegistration);
}
......
......@@ -42,15 +42,17 @@ public class GroupsDAO {
groupsHook.beforeCreate(group);
}
String sql = "INSERT INTO gms_group (id, name, path, is_leaf, created_by) VALUES (?, ?, ?, ?, ?)";
String sql = "INSERT INTO gms_group (id, name, path, is_leaf, locked, created_by) VALUES (?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(conn -> {
int i = 0;
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, group.getId());
ps.setString(2, group.getName());
ps.setObject(3, group.getPath(), Types.OTHER);
ps.setBoolean(4, group.isLeaf());
ps.setString(5, group.getCreatedBy());
ps.setString(++i, group.getId());
ps.setString(++i, group.getName());
ps.setObject(++i, group.getPath(), Types.OTHER);
ps.setBoolean(++i, group.isLeaf());
ps.setBoolean(++i, group.isLocked());
ps.setString(++i, group.getCreatedBy());
return ps;
});
......@@ -63,14 +65,16 @@ public class GroupsDAO {
groupsHook.beforeUpdate(group);
}
String sql = "UPDATE gms_group SET name = ?, path = ?, is_leaf = ? WHERE id = ?";
String sql = "UPDATE gms_group SET name = ?, path = ?, is_leaf = ?, locked = ? WHERE id = ?";
jdbcTemplate.update(conn -> {
int i = 0;
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, group.getName());
ps.setObject(2, group.getPath(), Types.OTHER);
ps.setBoolean(3, group.isLeaf());
ps.setString(4, group.getId());
ps.setString(++i, group.getName());
ps.setObject(++i, group.getPath(), Types.OTHER);
ps.setBoolean(++i, group.isLeaf());
ps.setBoolean(++i, group.isLocked());
ps.setString(++i, group.getId());
return ps;
});
......@@ -97,12 +101,7 @@ public class GroupsDAO {
return ps;
}, resultSet -> {
if (resultSet.next()) {
GroupEntity group = new GroupEntity();
group.setId(resultSet.getString("id"));
group.setName(resultSet.getString("name"));
group.setPath(resultSet.getString("path"));
group.setLeaf(resultSet.getBoolean("is_leaf"));
group.setLocked(resultSet.getBoolean("locked"));
GroupEntity group = getGroupFromResultSet(resultSet);
return Optional.of(group);
}
return Optional.empty();
......
......@@ -100,9 +100,9 @@ public class InvitedRegistrationDAO {
return Optional.ofNullable(registration);
}
public void setRegistrationUser(InvitedRegistration invitedRegistration) {
public void setRegistrationDone(InvitedRegistration invitedRegistration) {
String sql = "UPDATE invited_registration_request SET \"user\" = ? WHERE id = ?";
String sql = "UPDATE invited_registration_request SET \"user\" = ?, done = true WHERE id = ?";
jdbcTemplate.update(conn -> {
PreparedStatement ps = conn.prepareStatement(sql);
......@@ -112,17 +112,6 @@ public class InvitedRegistrationDAO {
});
}
public void setRegistrationDone(InvitedRegistration invitedRegistration) {
String sql = "UPDATE invited_registration_request SET done = true WHERE id = ?";
jdbcTemplate.update(conn -> {
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, invitedRegistration.getId());
return ps;
});
}
/**
* Called before deleting a group.
*/
......
......@@ -11,6 +11,7 @@ import it.inaf.ia2.gms.persistence.LoggingDAO;
import it.inaf.ia2.gms.persistence.MembershipsDAO;
import it.inaf.ia2.gms.persistence.PermissionsDAO;
import it.inaf.ia2.gms.persistence.model.GroupEntity;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
......@@ -70,6 +71,7 @@ public class GroupsService {
group.setPath(path);
group.setLeaf(leaf);
group.setCreatedBy(createdBy);
group.setCreationTime(new Date());
groupsDAO.createGroup(group);
loggingDAO.logAction("Added group: parent_path=" + parent.getPath() + ", group_name=" + groupName);
......
......@@ -54,9 +54,9 @@
<img src="img/gms-admin-add-group-modal.jpg" alt="" class="mb-3" />
</p>
<p>If you select the "is leaf" checkbox, the group will be a leaf of the tree: this means that
it will not be possible to create sub-groups inside it. You can change this setting in any moment
clicking on the "Edit group" button (pencil icon <img src="img/pencil-icon.jpg" alt="" />).</p>
<p>If you select the "allow child groups" checkbox it will be possible to create sub-groups inside it.
You can change this setting in any moment clicking on the "Edit group" button (pencil icon
<img src="img/pencil-icon.jpg" alt="" />).</p>
<p>You can delete groups using trash icons (<img src="img/trash-icon.jpg" alt="" />).
A dialog will ask for confirming the operation before deleting the group.</p>
......
......@@ -36,6 +36,10 @@
If multiple users need to manage memberships of the same group you can ask administrators to enable them.
</p>
<p><strong>IMPORTANT</strong>: A user is searchable after he/she has performed the first login on one
of our services. Check <a href="https://sso.ia2.inaf.it/" target="blank_">SSO help page</a>
for more information about IA2 authentication.</p>
<p>You can delete memberships using the trash icons (<img src="img/trash-icon.jpg" alt="" />).
<h2 class="mt-4">Seeing information about users</h2>
......
......@@ -98,10 +98,6 @@ public class GroupsDAOTest {
assertTrue(group.isPresent());
assertEquals(lbtInaf, group.get());
// Find unexisting group
group = dao.findGroupById("not-found");
assertFalse(group.isPresent());
// Sub list
List<GroupEntity> groups = dao.getDirectSubGroups(root.getPath());
assertEquals(2, groups.size());
......@@ -146,7 +142,7 @@ public class GroupsDAOTest {
assertTrue(childrenMap.get(lbt.getId()));
assertFalse(childrenMap.get(tng.getId()));
// Rename
// Update
String newName = "renamed";
tng.setName(newName);
dao.updateGroup(tng);
......@@ -196,6 +192,46 @@ public class GroupsDAOTest {
return UUID.randomUUID().toString().replaceAll("-", "");
}
@Test
public void testFields() {
GroupEntity group = new GroupEntity();
group.setId("group_id");
group.setName("group_name");
group.setPath("group_path");
group.setLeaf(true);
group.setLocked(true);
group.setCreatedBy("creator_id");
dao.createGroup(group);
GroupEntity savedGroup = dao.findGroupById("group_id").get();
assertEquals("group_id", savedGroup.getId());
assertEquals("group_name", savedGroup.getName());
assertEquals("group_path", savedGroup.getPath());
assertTrue(savedGroup.isLeaf());
assertTrue(savedGroup.isLocked());
assertEquals("creator_id", savedGroup.getCreatedBy());
group.setName("new_name");
group.setLeaf(false);
group.setLocked(false);
dao.updateGroup(group);
savedGroup = dao.findGroupById("group_id").get();
assertEquals("new_name", savedGroup.getName());
assertFalse(savedGroup.isLeaf());
assertFalse(savedGroup.isLocked());
}
@Test
public void testGetInexistentGroupById() {
assertTrue(dao.findGroupById("not-found").isEmpty());
}
@Test
public void testGroupCompleteNamesEmptyInput() {
assertTrue(dao.getGroupCompleteNamesFromId(new HashSet<>()).isEmpty());
......
package it.inaf.ia2.gms.service;
import it.inaf.ia2.gms.persistence.GroupsDAO;
import it.inaf.ia2.gms.persistence.InvitedRegistrationDAO;
import it.inaf.ia2.gms.persistence.LoggingDAO;
import it.inaf.ia2.gms.persistence.MembershipsDAO;
import it.inaf.ia2.gms.persistence.PermissionsDAO;
import it.inaf.ia2.gms.persistence.model.GroupEntity;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.mockito.ArgumentMatchers.argThat;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class GroupsServiceTest {
@Mock
private GroupsDAO groupsDAO;
@Mock
private PermissionsDAO permissionsDAO;
@Mock
private MembershipsDAO membershipsDAO;
@Mock
private InvitedRegistrationDAO invitedRegistrationDAO;
@Mock
private LoggingDAO loggingDAO;
@InjectMocks
private GroupsService groupsService;
private final GroupEntity root = group("ROOT", "ROOT", "");
@Test
public void testAddGroup() {
GroupEntity group1 = groupsService.addGroup(root, "group1", false, "creator_id");
verify(groupsDAO, times(1)).createGroup(argThat(g -> "group1".equals(g.getName())));
assertNotNull(group1.getId());
assertEquals("group1", group1.getName());
assertFalse(group1.isLeaf());
assertEquals("creator_id", group1.getCreatedBy());
assertNotNull(group1.getCreationTime());
GroupEntity group2 = groupsService.addGroup(group1, "group2", true, "creator_id");
verify(groupsDAO, times(1)).createGroup(argThat(g -> "group2".equals(g.getName())));
assertNotNull(group2.getId());
assertEquals("group2", group2.getName());
assertTrue(group2.isLeaf());
assertEquals(group1.getId() + "." + group2.getId(), group2.getPath());
}
private GroupEntity group(String id, String name, String path) {
GroupEntity group = new GroupEntity();
group.setId(id);
group.setName(name);
group.setPath(path);
return group;
}
}