Skip to content

Commit 8fade29

Browse files
committed
Fixed #271
1 parent 481c040 commit 8fade29

File tree

17 files changed

+297
-55
lines changed

17 files changed

+297
-55
lines changed

client/src/api/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ export function searchOrganizations(pagination = {}) {
132132
return fetchJson(`/api/v1/organizations/search?${queryPart}`);
133133
}
134134

135+
export function searchOrganizationsLandingPage(query) {
136+
return fetchJson(`/api/v1/organizations/search-landing?query=${encodeURIComponent(query)}`);
137+
}
138+
135139
export function pendingApprovalOrganizations() {
136140
return fetchJson("/api/v1/organizations/status/pending");
137141
}

client/src/locale/en.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ const en = {
218218
createdAt: "Created",
219219
memberCount: "# Members",
220220
applicationCount: "# Apps",
221+
adminEmail: "Admin email",
221222
status: "Status",
222223
searchPlaceHolder: "Search for organisations...",
223224
confirmation: "Are you sure you want to change the status to <strong>{{status}}</strong> for organisation {{name}}?",

client/src/pages/Landing.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {useAppStore} from "../stores/AppStore";
44
import I18n from "../locale/I18n";
55
import {Loader} from "@surfnet/sds";
66
import {useNavigate} from "react-router-dom";
7-
import {newOrganization, searchOrganizations} from "../api/index.js";
7+
import {newOrganization, searchOrganizationsLandingPage} from "../api/index.js";
88
import {useDebouncedCallback} from 'use-debounce';
99
import {isEmpty} from "../utils/Utils.js";
1010
import InputField from "../components/InputField.jsx";
@@ -33,9 +33,9 @@ const Landing = ({refreshUser}) => {
3333
const navigate = useNavigate();
3434

3535
const debouncedFetch = useDebouncedCallback(val => {
36-
searchOrganizations({query: val, pageSize: 100_000, sort: "name"})
37-
.then(page => {
38-
setOrganizations(page.content);
36+
searchOrganizationsLandingPage(val)
37+
.then(res => {
38+
setOrganizations(res);
3939
setLoading(false);
4040
})
4141
}, 850);

client/src/pages/Organizations.jsx

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const Organizations = ({pendingApproval, tab}) => {
3232

3333
const [refresh, setRefresh] = useState(new Date());
3434
const [searching, setSearching] = useState(false);
35+
const [sortedBy, setSortedBy] = useState("DESC");
3536
const [paginationQueryParams, setPaginationQueryParams] = useState(defaultPagination("createdAt", "DESC"));
3637
const [totalElements, setTotalElements] = useState(0);
3738
const [organizations, setOrganizations] = useState([]);
@@ -47,7 +48,6 @@ export const Organizations = ({pendingApproval, tab}) => {
4748
if (pendingApproval) {
4849
pendingApprovalOrganizations().then(res => {
4950
setOrganizations(res);
50-
setLoading(false);
5151
})
5252
} else {
5353
searchOrganizations(paginationQueryParams)
@@ -58,7 +58,7 @@ export const Organizations = ({pendingApproval, tab}) => {
5858
});
5959
}
6060
},
61-
[refresh, paginationQueryParams, tab]);// eslint-disable-line react-hooks/exhaustive-deps
61+
[refresh, paginationQueryParams]);// eslint-disable-line react-hooks/exhaustive-deps
6262

6363
useEffect(() => {
6464
if (inputRef.current) {
@@ -67,9 +67,11 @@ export const Organizations = ({pendingApproval, tab}) => {
6767
}, [openOrganizationId]);
6868

6969
const search = (query, sorted, reverse, page) => {
70-
const isSortReversed = paginationQueryParams.sortDirection !== "DESC";
71-
const paginationQueryParamsChanged = sorted !== paginationQueryParams.sort || reverse !== isSortReversed ||
70+
const newSorted = reverse ? "ASC" : "DESC";
71+
const isReverseChanged = sortedBy !== newSorted;
72+
const paginationQueryParamsChanged = sorted !== paginationQueryParams.sort || isReverseChanged ||
7273
page !== paginationQueryParams.pageNumber;
74+
setSortedBy(newSorted);
7375
if ((!isEmpty(query) && query.trim().length > 2) || paginationQueryParamsChanged) {
7476
delayedAutocomplete(query, sorted, reverse, page);
7577
}
@@ -254,14 +256,9 @@ export const Organizations = ({pendingApproval, tab}) => {
254256
mapper: org => <div className="wrapper"><Checkbox value={!isEmpty(org.manageIdentifier)} disabled={true}/></div>
255257
},
256258
{
257-
key: "applicationCount",
258-
header: I18n.t("organizations.applicationCount"),
259-
mapper: org => org.applicationCount
260-
},
261-
{
262-
key: "memberCount",
263-
header: I18n.t("organizations.memberCount"),
264-
mapper: org => org.memberCount
259+
key: "adminEmail",
260+
header: I18n.t("organizations.adminEmail"),
261+
mapper: org => org.adminEmail
265262
},
266263
{
267264
key: "createdAt",

client/src/pages/Organizations.scss

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,8 @@
5757
width: 8%;
5858
}
5959

60-
&.applicationCount {
61-
text-align: center;
62-
width: 11%;
63-
}
64-
65-
&.memberCount {
66-
text-align: center;
67-
width: 11%;
60+
&.admin_email {
61+
width: 22%;
6862
}
6963

7064
&.status {

client/src/pages/System.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const System = () => {
4040
const tabChanged = (name) => {
4141
setCurrentTab(name);
4242
const path = encodeURIComponent(`/system/${name}`);
43+
//We need to force a refresh of the Organization component that is used twice
4344
navigate(`/refresh-route/${path}`);
4445
}
4546

client/src/pages/Users.jsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,27 @@ export const Users = () => {
2626

2727
const [searching, setSearching] = useState(false);
2828
const [loading, setLoading] = useState(false);
29+
const [sortedBy, setSortedBy] = useState("DESC");
2930
const [paginationQueryParams, setPaginationQueryParams] = useState(defaultPagination("createdAt", "DESC"));
3031
const [totalElements, setTotalElements] = useState(0);
3132
const [users, setUsers] = useState([]);
3233
const navigate = useNavigate();
3334

3435
useEffect(() => {
35-
searchUsers(paginationQueryParams)
36-
.then(page => {
37-
setUsers(page.content);
38-
setTotalElements(page.totalElements);
39-
setSearching(false);
40-
});
41-
},
42-
[paginationQueryParams]);
36+
searchUsers(paginationQueryParams)
37+
.then(page => {
38+
setUsers(page.content);
39+
setTotalElements(page.totalElements);
40+
setSearching(false);
41+
});
42+
}, [paginationQueryParams]);
4343

4444
const search = (query, sorted, reverse, page) => {
45-
const isSortReversed = paginationQueryParams.sortDirection !== "DESC";
46-
const paginationQueryParamsChanged = sorted !== paginationQueryParams.sort || reverse !== isSortReversed ||
45+
const newSorted = reverse ? "ASC" : "DESC";
46+
const isReverseChanged = sortedBy !== newSorted;
47+
const paginationQueryParamsChanged = sorted !== paginationQueryParams.sort || isReverseChanged ||
4748
page !== paginationQueryParams.pageNumber;
49+
setSortedBy(newSorted);
4850
if ((!isEmpty(query) && query.trim().length > 2) || paginationQueryParamsChanged) {
4951
delayedAutocomplete(query, sorted, reverse, page);
5052
}

client/src/utils/Pagination.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const defaultPagination = (sort = "name", sortDirection = "ASC") => {
3838
sort: sort,
3939
sortDirection: sortDirection
4040
};
41-
return {...dp};
41+
return dp;
4242
}
4343

4444
export const storePageNumber = nbr => {

server/src/main/java/access/api/OrganizationController.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ public ResponseEntity<Page<Map<String, Object>>> search(@Parameter(hidden = true
173173
@RequestParam(value = "sort", required = false, defaultValue = "name") String sort,
174174
@RequestParam(value = "sortDirection", required = false, defaultValue = "ASC") String sortDirection) {
175175
LOG.debug(String.format("/search/paginated for user %s", user.getEduPersonPrincipalName()));
176+
//Only used in the Systems tab and exclusively for superusers
177+
confirmSuperUser(user);
176178

177179
Pageable pageable = PageRequest.of(pageNumber, pageSize, Sort.by(Sort.Direction.fromString(sortDirection), sort));
178180

@@ -181,6 +183,14 @@ public ResponseEntity<Page<Map<String, Object>>> search(@Parameter(hidden = true
181183
return ResponseEntity.ok(usersPage);
182184
}
183185

186+
@GetMapping("/search-landing")
187+
public ResponseEntity<List<Map<String, Object>>> search(@Parameter(hidden = true) User user,
188+
@RequestParam(value = "query") String query) {
189+
LOG.debug(String.format("/landing-search for user %s", user.getEduPersonPrincipalName()));
190+
191+
List<Map<String, Object>> organizations = organizationRepository.searchWithKeyword(FullSearchQueryParser.parse(query));
192+
return ResponseEntity.ok(organizations);
193+
}
184194

185195
@GetMapping("/users/{id}")
186196
public ResponseEntity<Organization> usersOfOrganization(User user, @PathVariable("id") Long id) {
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package access.manage;
2+
3+
import java.math.BigInteger;
4+
import java.net.InetAddress;
5+
import java.net.UnknownHostException;
6+
import java.nio.ByteBuffer;
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
10+
/**
11+
* A class that enables to get an IP range from CIDR specification. It supports
12+
* both IPv4 and IPv6.
13+
*/
14+
public class CIDRInstance {
15+
16+
private final InetAddress inetAddress;
17+
private InetAddress startAddress;
18+
private InetAddress endAddress;
19+
private final int prefixLength;
20+
21+
public CIDRInstance(String cidr) throws UnknownHostException {
22+
// split CIDR to address and prefix part
23+
int index = cidr.indexOf("/");
24+
String addressPart = cidr.substring(0, index);
25+
String networkPart = cidr.substring(index + 1);
26+
27+
inetAddress = InetAddress.getByName(addressPart);
28+
prefixLength = Integer.parseInt(networkPart);
29+
30+
calculate();
31+
}
32+
33+
34+
private void calculate() throws UnknownHostException {
35+
ByteBuffer maskBuffer;
36+
int targetSize;
37+
if (inetAddress.getAddress().length == 4) {
38+
maskBuffer =
39+
ByteBuffer
40+
.allocate(4)
41+
.putInt(-1);
42+
targetSize = 4;
43+
} else {
44+
maskBuffer = ByteBuffer.allocate(16)
45+
.putLong(-1L)
46+
.putLong(-1L);
47+
targetSize = 16;
48+
}
49+
50+
BigInteger mask = (new BigInteger(1, maskBuffer.array())).not().shiftRight(prefixLength);
51+
52+
ByteBuffer buffer = ByteBuffer.wrap(inetAddress.getAddress());
53+
BigInteger ipVal = new BigInteger(1, buffer.array());
54+
55+
BigInteger startIp = ipVal.and(mask);
56+
BigInteger endIp = startIp.add(mask.not());
57+
58+
byte[] startIpArr = toBytes(startIp.toByteArray(), targetSize);
59+
byte[] endIpArr = toBytes(endIp.toByteArray(), targetSize);
60+
61+
this.startAddress = InetAddress.getByAddress(startIpArr);
62+
this.endAddress = InetAddress.getByAddress(endIpArr);
63+
64+
}
65+
66+
private byte[] toBytes(byte[] array, int targetSize) {
67+
int counter = 0;
68+
List<Byte> newArr = new ArrayList<Byte>();
69+
while (counter < targetSize && (array.length - 1 - counter >= 0)) {
70+
newArr.add(0, array[array.length - 1 - counter]);
71+
counter++;
72+
}
73+
74+
int size = newArr.size();
75+
for (int i = 0; i < (targetSize - size); i++) {
76+
77+
newArr.add(0, (byte) 0);
78+
}
79+
80+
byte[] ret = new byte[newArr.size()];
81+
for (int i = 0; i < newArr.size(); i++) {
82+
ret[i] = newArr.get(i);
83+
}
84+
return ret;
85+
}
86+
87+
public String getNetworkAddress() {
88+
return this.startAddress.getHostAddress();
89+
}
90+
91+
public String getBroadcastAddress() {
92+
return this.endAddress.getHostAddress();
93+
}
94+
95+
}

0 commit comments

Comments
 (0)