Skip to content

Commit d93aa9a

Browse files
Updated regex validation and refactored code to validate NLM connection string
1 parent e2e4b07 commit d93aa9a

File tree

4 files changed

+175
-52
lines changed

4 files changed

+175
-52
lines changed

gui/src/components/LicensingGatherer/LicenseGatherer.spec.js

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import React from 'react';
44
import LicenseGatherer from './index';
55
import { render, fireEvent } from '../../test/utils/react-test';
6-
import fetchMock from 'fetch-mock';
76

87
describe('LicenseGatherer component', () => {
98

@@ -98,5 +97,36 @@ describe('LicenseGatherer component', () => {
9897
// Check if nlm iframe is rendered.
9998
const nlmTabContent = container.querySelector('#NLM');
10099
expect(nlmTabContent).toBeInTheDocument();
101-
});
100+
});
101+
102+
103+
test.each([
104+
['1234', true], ['hostname', true], ['1234hostname', true], ['1234,', true], ['hostname,', true],
105+
['1234@hostname', false], ['1234@hostname,4567@hostname', false], ['1234@hostname:4567@hostname', false],
106+
['1234@hostname,4567@hostname,456@hostname', false], ['1234@hostname,4567@hostname,456@hostname:789@hostname', false],
107+
['789@hostname:1234@hostname,4567@hostname,456@hostname', false], ['789@hostname:1234@hostname,4567@hostname,456@hostname:789@hostname', false],
108+
])(
109+
'Test to check for NLM connection string: %s if the \'disabled\' property of the Submit button is set to %s',
110+
(NLMConnStr, disabledValue) => {
111+
112+
const { container } = render(<LicenseGatherer />, { initialState: initialState });
113+
114+
const nlmTab = container.querySelector('#nlm-tab');
115+
const input = container.querySelector('#nlm-connection-string')
116+
const submitButton = container.querySelector('#submit')
117+
118+
expect(nlmTab).toBeInTheDocument();
119+
120+
// Click on nlm Tab
121+
fireEvent.click(nlmTab);
122+
123+
// Check if nlm iframe is rendered.
124+
const nlmTabContent = container.querySelector('#NLM');
125+
expect(nlmTabContent).toBeInTheDocument();
126+
127+
fireEvent.change(input, {target: {value: NLMConnStr}})
128+
// Check if Submit button is disabled for an invalid nlm connection string
129+
expect(submitButton.disabled).toBe(disabledValue)
130+
}
131+
);
102132
});

gui/src/components/LicensingGatherer/NLM.js

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,30 @@ import {
1313
// IS:
1414
// ^[0-9]+[@](\w|\_|\-|\.)+$
1515
// Server triad is of the form : port@host1,port@host2,port@host3
16-
const connStrRegex = /^[\d]+@[\w|\-|_|.]+$|^[\d]+@[\w|\-|_|.]+,[\d]+@[\w|\-|_|.]+,[\d]+@[\w|\-|_|.]+$/
16+
const connStrRegex = /^[0-9]+[@](\w|\_|\-|\.)+/
17+
18+
function validateInput(nlm_connections_str) {
19+
/*
20+
nlm_connections_str must contain server names (each of the form port@hostname) separated by \':\' on unix or \';\' on windows(server triads however must be comma seperated)
21+
22+
Some valid nlm_connections_str values are:
23+
1) port@hostname
24+
3) port1@hostname1:port2@hostname2
25+
4) port1@hostname1:port2@hostname2:port3@hostname3
26+
5) port1@hostname1:port2@hostname2,port3@hostname3,port4@hostname4:port5@hostname5
27+
*/
28+
29+
let nlm_connection_strs = nlm_connections_str.split(/:|;|,/)
30+
31+
// All strings comply with port@hostname format
32+
for (let nlm_connection_str of nlm_connection_strs) {
33+
if (!connStrRegex.test(nlm_connection_str)){
34+
return false
35+
}
36+
}
37+
return true
38+
}
1739

18-
function validateInput(str) {
19-
return connStrRegex.test(str);
20-
}
2140

2241
function NLM() {
2342
const dispatch = useDispatch();
@@ -51,7 +70,7 @@ function NLM() {
5170
<span className="glyphicon form-control-feedback glyphicon-remove"></span>
5271
<span className="glyphicon form-control-feedback glyphicon-ok"></span>
5372
</div>
54-
<input type="submit" value="Submit" className="btn btn_color_blue" disabled={!valid} />
73+
<input type="submit" id="submit" value="Submit" className="btn btn_color_blue" disabled={!valid} />
5574
</form>
5675
</div>
5776
);

matlab_proxy/util/mwi/validators.py

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@
1515
import socket
1616
import sys
1717

18-
import matlab_proxy
1918
import pkg_resources
2019

20+
import matlab_proxy
21+
2122
from . import environment_variables as mwi_env
2223
from . import logger as mwi_logger
2324

2425
logger = mwi_logger.get()
2526

2627

27-
def validate_mlm_license_file(nlm_conn_str):
28+
def validate_mlm_license_file(nlm_connections_str):
2829
"""Validates and returns input if it passes validation.
2930
Throws exception when validation fails.
3031
The connection string should be in the form of port@hostname
@@ -39,44 +40,60 @@ def validate_mlm_license_file(nlm_conn_str):
3940
Returns:
4041
String: Returns the same argument passed to this function if its valid.
4142
"""
43+
44+
"""
45+
nlm_connections_str can either be a valid path to a file or a
46+
string with comma seperated values, each of the form port@hostname
47+
48+
Some valid nlm_connections_str values are:
49+
1) port@hostname
50+
2) port@hostname,
51+
3) port1@hostname1,port2@hostname2
52+
4) port1@hostname1,port2@hostname2,
53+
5) port1@hostname1,port2@hostname2,port3@hostname3,
54+
"""
4255
import re
4356

4457
from .exceptions import NetworkLicensingError
4558

46-
if nlm_conn_str is None:
59+
if nlm_connections_str is None:
4760
return None
4861

49-
# TODO: The JS validation of this setting does not allow file path locations
50-
# The JS validation occurs before reaching the set_licensing_info endpoint.
51-
5262
# Regular expression to match port@hostname,
5363
# where port is any number and hostname is alphanumeric
5464
# regex = Start of Line, Any number of 0-9 digits , @, any number of nonwhite space characters with "- _ ." allowed
5565
# "^[0-9]+[@](\w|\_|\-|\.)+$"
56-
# Server triad is of the form : port@host1,port@host2,port@host3
57-
regex = "(^[0-9]+[@](\w|\_|\-|\.)+$)|(^[0-9]+[@](\w|\_|\-|\.)+),([0-9]+[@](\w|\_|\-|\.)+),([0-9]+[@](\w|\_|\-|\.)+$)"
58-
if not re.search(regex, nlm_conn_str):
59-
logger.debug("NLM info is not in the form of port@hostname")
60-
if not os.path.isfile(nlm_conn_str):
61-
logger.debug("NLM info is not a valid path to a license file")
62-
error_message = (
63-
f"MLM_LICENSE_FILE validation failed for {nlm_conn_str}. "
64-
f"If set, the MLM_LICENSE_FILE environment variable must be a string which is either of the form port@hostname"
65-
f" OR path to a valid license file."
66-
)
67-
logger.error(error_message)
68-
raise NetworkLicensingError(error_message)
69-
else:
66+
# Server triad is of the form : port@host1 or port@host1,port@host2,port@host3
67+
nlm_connection_str_regex = "(^[0-9]+[@](\w|\_|\-|\.)+$)"
68+
error_message = (
69+
f"MLM_LICENSE_FILE validation failed for {nlm_connections_str}. "
70+
f"If set, the MLM_LICENSE_FILE environment variable must contain server names (each of the form port@hostname) separated by ':' on unix or ';' on windows(server triads however must be comma seperated)"
71+
f" OR path to a valid license file."
72+
)
73+
74+
nlm_connection_strs = re.split(":|;|,", nlm_connections_str)
75+
76+
logger.debug(
77+
"Validating individual parts of the environment variable MLM_LICENSE_FILE"
78+
)
79+
for nlm_connection_str in nlm_connection_strs:
80+
# Individual parts of the MLM_LICENSE_FILE can either be a valid path to a license file or a server name.
81+
82+
if os.path.isfile(nlm_connection_str):
7083
logger.info(
71-
f"MLM_LICENSE_FILE with value: {nlm_conn_str} is a path to a file. MATLAB will attempt to use it."
84+
f"{nlm_connections_str} is a path to a file. MATLAB will attempt to use it."
7285
)
73-
else:
74-
logger.info(
75-
f"MLM_LICENSE_FILE with value: {nlm_conn_str} is a license server, MATLAB will attempt to connect to it."
76-
)
7786

78-
# Validation passed
79-
return nlm_conn_str
87+
else:
88+
match = re.search(nlm_connection_str_regex, nlm_connection_str)
89+
90+
if match:
91+
logger.debug(f"Successfully validated {nlm_connection_str}")
92+
else:
93+
logger.error("NLM_info is not of the form port@hostname")
94+
raise NetworkLicensingError(error_message)
95+
96+
return nlm_connections_str
8097

8198

8299
def validate_app_port_is_free(port):

tests/util/mwi/test_validators.py

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,36 +15,93 @@
1515
from matlab_proxy.util.mwi.exceptions import NetworkLicensingError
1616

1717

18-
def test_validate_mlm_license_file_for_invalid_string(monkeypatch):
18+
@pytest.mark.parametrize(
19+
"MLM_LICENSE_FILE",
20+
[
21+
("/path/to/a/non-existent/file"),
22+
("1234"),
23+
("hostname"),
24+
("1234hostname"),
25+
(""),
26+
],
27+
ids=[
28+
"Invalid path to a license file",
29+
"NLM string with just port number",
30+
"NLM string with just hostname",
31+
"NLM string with just port number and hostname",
32+
"empty value",
33+
],
34+
)
35+
def test_validate_mlm_license_file_invalid_value(MLM_LICENSE_FILE, monkeypatch):
1936
"""Check if validator raises expected exception"""
20-
# Delete the environment variables if they do exist
37+
2138
env_name = mwi_env.get_env_name_network_license_manager()
22-
invalid_string = "/Invalid/String/"
23-
monkeypatch.setenv(env_name, invalid_string)
39+
40+
monkeypatch.setenv(env_name, MLM_LICENSE_FILE)
2441
nlm_conn_str = os.getenv(env_name)
42+
2543
with pytest.raises(NetworkLicensingError) as e_info:
26-
conn_str = validators.validate_mlm_license_file(nlm_conn_str)
27-
assert invalid_string in str(e_info.value)
44+
validators.validate_mlm_license_file(nlm_conn_str)
45+
assert MLM_LICENSE_FILE in str(e_info.value)
2846

2947

30-
def test_validate_mlm_license_file_for_valid_server_syntax(monkeypatch):
31-
"""Check if port@hostname passes validation"""
48+
@pytest.fixture(name="temporary_license_file")
49+
def temporary_license_file_fixture(tmp_path):
50+
"""Pytest fixture which returns a valid path to temporary license file."""
51+
import time
52+
53+
temp_license_file_path = tmp_path / f'{str(time.time()).replace(".", "")}.lic'
54+
temp_license_file_path.touch()
55+
56+
return temp_license_file_path
57+
58+
59+
def test_validate_mlm_license_file_valid_license_file_path(
60+
temporary_license_file, monkeypatch
61+
):
62+
"""Check if a valid license path has been supplied to MLM_LICENSE_FILE env var"""
3263
env_name = mwi_env.get_env_name_network_license_manager()
33-
license_manager_address = "1234@1.2_any-alphanumeric"
34-
monkeypatch.setenv(env_name, license_manager_address)
35-
conn_str = validators.validate_mlm_license_file(os.getenv(env_name))
36-
assert conn_str == license_manager_address
64+
monkeypatch.setenv(env_name, str(temporary_license_file))
65+
66+
validated_file_path = validators.validate_mlm_license_file(os.getenv(env_name))
67+
assert str(temporary_license_file) == validated_file_path
3768

3869

39-
def test_validate_mlm_license_file_for_valid_server_triad_syntax(monkeypatch):
70+
@pytest.mark.parametrize(
71+
"MLM_LICENSE_FILE",
72+
[
73+
("1234@1.2_any-alphanumeric"),
74+
("1234@1.2_any-alphanumeric:1234@1.2_any-alphanumeric"),
75+
(
76+
"1234@1.2_any-alphanumeric:1234@1.2_any-alphanumeric;1234@1.2_any-alphanumeric"
77+
),
78+
"1234@1.2_any-alphanumeric,1234@1.2_any-alphanumeric,1234@1.2_any-alphanumeric",
79+
(
80+
"1234@1.2_any-alphanumeric:1234@1.2_any-alphanumeric,1234@1.2_any-alphanumeric,1234@1.2_any-alphanumeric"
81+
),
82+
(
83+
"1234@1.2_any-alphanumeric,1234@1.2_any-alphanumeric,1234@1.2_any-alphanumeric:1234@1.2_any-alphanumeric"
84+
),
85+
(
86+
"1234@1.2_any-alphanumeric:1234@1.2_any-alphanumeric,1234@1.2_any-alphanumeric,1234@1.2_any-alphanumeric:1234@1.2_any-alphanumeric"
87+
),
88+
],
89+
ids=[
90+
"1 NLM server",
91+
"2 NLM servers",
92+
"3 NLM servers",
93+
"Just a server triad",
94+
"1 NLM server prefixed to a server triad",
95+
"1 NLM server suffixed to a server triad",
96+
"1 NLM server prefixed and another suffixed to a server triad",
97+
],
98+
)
99+
def test_validate_mlm_license_file_for_valid_nlm_string(MLM_LICENSE_FILE, monkeypatch):
40100
"""Check if port@hostname passes validation"""
41101
env_name = mwi_env.get_env_name_network_license_manager()
42-
license_manager_address = (
43-
"1234@1.2_any-alphanumeric,1234@1.2_any-alphanumeric,1234@1.2_any-alphanumeric"
44-
)
45-
monkeypatch.setenv(env_name, license_manager_address)
102+
monkeypatch.setenv(env_name, MLM_LICENSE_FILE)
46103
conn_str = validators.validate_mlm_license_file(os.getenv(env_name))
47-
assert conn_str == license_manager_address
104+
assert conn_str == MLM_LICENSE_FILE
48105

49106

50107
def test_validate_mlm_license_file_None():

0 commit comments

Comments
 (0)