Skip to content

Commit e414438

Browse files
author
Lionel Elie Mamane
committed
introduce a whitelist of uids exempted from ip check
1 parent aab692c commit e414438

File tree

7 files changed

+170
-31
lines changed

7 files changed

+170
-31
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ source_dir=$(build_dir)/source
77
sign_dir=$(build_dir)/sign
88
package_name=$(app_name)
99
cert_dir=$(HOME)/.nextcloud/certificates
10-
version+=1.0.2
10+
version+=3.1.0
1111

1212
all: appstore
1313

@@ -47,4 +47,4 @@ appstore: clean
4747
@if [ -f $(cert_dir)/$(app_name).key ]; then \
4848
echo "Signing package…"; \
4949
openssl dgst -sha512 -sign $(cert_dir)/$(app_name).key $(build_dir)/$(app_name)-$(version).tar.gz | openssl base64; \
50-
fi
50+
fi

appinfo/app.php

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,9 @@
3737
$isLoginpage
3838
);
3939

40-
if(!$loginHookListener->isLoginAllowed()) {
41-
if($isLoginpage) {
42-
header('Location: ' . \OC::$WEBROOT . '/index.php/apps/limit_login_to_ip/denied');
43-
exit();
44-
}
45-
46-
\OCP\Util::connectHook(
47-
'OC_User',
48-
'pre_login',
49-
$loginHookListener,
50-
'handleLoginRequest'
51-
);
52-
}
40+
\OCP\Util::connectHook(
41+
'OC_User',
42+
'pre_login',
43+
$loginHookListener,
44+
'handleLoginRequest'
45+
);

appinfo/info.xml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
33
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
44
<id>limit_login_to_ip</id>
5-
<name>Restrict login to IP addresses</name>
6-
<summary>Allows administrators to restrict logins to their instance to specific IP ranges.</summary>
5+
<name>Restrict logins to whitelisted IP addresses or users</name>
6+
<summary>Allows administrators to restrict logins to their instance by user or specific IP ranges.</summary>
77
<description><![CDATA[This app allows administrators to restrict login to their
8-
Nextcloud server to specific IP ranges. Note that existing sessions will be kept
9-
open.
8+
Nextcloud server to specific IP ranges, unless the user is exempted.
9+
Note that existing sessions will be kept open.
1010
11-
The allowed IP ranges can be administrated using the OCC command line interface
12-
or graphically using the admin settings. If you plan to use the OCC tool, the
13-
following commands would be applicable.
11+
The allowed IP ranges and exempted users can be administrated using
12+
the OCC command line interface or graphically using the admin
13+
settings. If you plan to use the OCC tool, the following commands
14+
would be applicable.
1415
1516
To whitelist `127.0.0.0/24`:
1617
@@ -19,6 +20,10 @@ To whitelist `127.0.0.0/24`:
1920
To whitelist `127.0.0.0/24` and also `192.168.0.0/24`:
2021
2122
- `occ config:app:set limit_login_to_ip whitelisted.ranges --value 127.0.0.0/24,192.168.0.0/24`
23+
24+
To whitelist `peter` and `john`:
25+
26+
- `occ config:app:set limit_login_to_ip whitelisted.uids --value peter,john`
2227
]]></description>
2328
<version>3.1.0</version>
2429
<licence>agpl</licence>

css/settings.scss

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,28 @@
2020

2121
#limit-login-to-ip-list .action-column a {
2222
display: inline-block;
23-
}
23+
}
24+
25+
#limit-login-to-ip-uidlist-spinner {
26+
margin-top: 25px;
27+
margin-bottom: 25px;
28+
margin-left: auto;
29+
margin-right: auto;
30+
}
31+
32+
#limit-login-to-ip-uidlist {
33+
min-width: 262px;
34+
}
35+
36+
#limit-login-to-ip-uidlist td span {
37+
padding: 10px 15px;
38+
display: inline-block;
39+
}
40+
41+
#limit-login-to-ip-uidlist .action-column {
42+
width: 46px;
43+
}
44+
45+
#limit-login-to-ip-uidlist .action-column a {
46+
display: inline-block;
47+
}

js/settings.js

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
(function(OCA) {
2222
OCA.LimitLoginToIp = OCA.LimitLoginToIp || {};
2323
OCA.LimitLoginToIp.Ranges = [];
24+
OCA.LimitLoginToIp.UIDs = [];
2425

2526
/**
2627
* @namespace OCA.LimitLoginToIp.Settings
@@ -58,7 +59,42 @@
5859
actionCell.innerHTML = actionCellValue.outerHTML;
5960
});
6061

61-
OCA.LimitLoginToIp.Settings.setEnabledState(true);
62+
OCA.LimitLoginToIp.Settings.setIPEnabledState(true);
63+
}
64+
}
65+
);
66+
OCP.AppConfig.getValue(
67+
'limit_login_to_ip',
68+
'whitelisted.uids',
69+
'',
70+
{
71+
success: function(data) {
72+
var textData = $(data).find('data data').text();
73+
var UIDs = textData.split(',');
74+
var table = document.getElementById('limit-login-to-ip-uidlist');
75+
table.innerHTML = '';
76+
77+
OCA.LimitLoginToIp.UIDs = UIDs;
78+
UIDs.forEach(function(uid) {
79+
var row = table.insertRow(0);
80+
var actionCell = row.insertCell(0);
81+
actionCell.className = 'action-column';
82+
var uidCell = row.insertCell(0);
83+
84+
var uidCellValue = document.createElement('span');
85+
uidCellValue.innerText = uid;
86+
uidCell.innerHTML = uidCellValue.outerHTML;
87+
88+
var actionCellValue = document.createElement('span');
89+
var deleteLink = document.createElement('a');
90+
deleteLink.className = 'icon-delete has-tooltip';
91+
deleteLink.title = t('limit_login_to_ip', 'Delete');
92+
deleteLink.setAttribute('data', uid);
93+
actionCellValue.innerHTML = deleteLink.outerHTML;
94+
actionCell.innerHTML = actionCellValue.outerHTML;
95+
});
96+
97+
OCA.LimitLoginToIp.Settings.setUIDEnabledState(true);
6298
}
6399
}
64100
);
@@ -89,14 +125,49 @@
89125
OCA.LimitLoginToIp.Settings.storeRanges();
90126
},
91127

92-
setEnabledState: function(isEnabled) {
128+
storeUIDs: function() {
129+
var uids = OCA.LimitLoginToIp.UIDs.join();
130+
OCP.AppConfig.setValue(
131+
'limit_login_to_ip',
132+
'whitelisted.uids',
133+
uids,
134+
{
135+
success: function () {
136+
OCA.LimitLoginToIp.Settings.load();
137+
}
138+
}
139+
);
140+
},
141+
142+
addUID: function(uid) {
143+
OCA.LimitLoginToIp.UIDs.push(uid);
144+
OCA.LimitLoginToIp.Settings.storeUIDs();
145+
},
146+
147+
removeUID: function(uid) {
148+
var index = OCA.LimitLoginToIp.UIDs.indexOf(uid);
149+
OCA.LimitLoginToIp.UIDs.splice(index, 1);
150+
OCA.LimitLoginToIp.Settings.storeUIDs();
151+
},
152+
153+
setIPEnabledState: function(isEnabled) {
93154
if(isEnabled !== true) {
94155
$('#limit-login-to-ip-list-spinner').removeClass('hidden');
95156
} else {
96157
$('#limit-login-to-ip-list-spinner').addClass('hidden');
97158
}
98159

99160
$('#limit-login-to-ip-input-fields input').prop('disabled', !isEnabled);
161+
},
162+
163+
setUIDEnabledState: function(isEnabled) {
164+
if(isEnabled !== true) {
165+
$('#limit-login-to-ip-uidlist-spinner').removeClass('hidden');
166+
} else {
167+
$('#limit-login-to-ip-uidlist-spinner').addClass('hidden');
168+
}
169+
170+
$('#limit-login-to-ip-uid-input-fields input').prop('disabled', !isEnabled);
100171
}
101172
};
102173

@@ -108,7 +179,7 @@
108179
$('#limit-login-to-ip-submit').click(function() {
109180
var ipAddress = $('#limit-login-to-ip-whitelist').val();
110181
var range = $('#limit-login-to-ip-whitelist_mask').val();
111-
182+
112183
// ipAddress validation
113184
// https://www.regexpal.com/?fam=104038
114185
var regexFilter = '((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))';
@@ -118,16 +189,30 @@
118189
if ( regexMatch == null ) {
119190
return false;
120191
}
121-
122-
OCA.LimitLoginToIp.Settings.setEnabledState(false);
192+
193+
OCA.LimitLoginToIp.Settings.setIPEnabledState(false);
123194
OCA.LimitLoginToIp.Settings.addRange(ipAddress + '/' + range);
124195
$('#limit-login-to-ip-whitelist').val('');
125196
$('#limit-login-to-ip-whitelist_mask').val();
126197
});
127198

128199
$('#limit-login-to-ip-list').on('click', 'a', function() {
129200
var rangeToRemove = $(this).attr('data');
130-
OCA.LimitLoginToIp.Settings.setEnabledState(false);
201+
OCA.LimitLoginToIp.Settings.setIPEnabledState(false);
131202
OCA.LimitLoginToIp.Settings.removeRange(rangeToRemove);
132203
});
204+
205+
$('#limit-login-to-ip-uid-submit').click(function() {
206+
var uid = $('#limit-login-to-ip-uid-whitelist').val();
207+
208+
OCA.LimitLoginToIp.Settings.setUIDEnabledState(false);
209+
OCA.LimitLoginToIp.Settings.addUID(uid);
210+
$('#limit-login-to-ip-uid-whitelist').val('');
211+
});
212+
213+
$('#limit-login-to-ip-uidlist').on('click', 'a', function() {
214+
var uidToRemove = $(this).attr('data');
215+
OCA.LimitLoginToIp.Settings.setUIDEnabledState(false);
216+
OCA.LimitLoginToIp.Settings.removeUID(uidToRemove);
217+
});
133218
})();

lib/LoginHookListener.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ private function matchCidr($ip, $range) {
108108
/**
109109
* @return bool
110110
*/
111-
public function isLoginAllowed() {
111+
public function isIPWhiteListed() {
112112
$allowedRanges = $this->config->getAppValue('limit_login_to_ip', 'whitelisted.ranges', '');
113113
if($allowedRanges === '') {
114114
return true;
@@ -125,7 +125,27 @@ public function isLoginAllowed() {
125125
return false;
126126
}
127127

128-
public function handleLoginRequest() {
128+
/**
129+
* @return bool
130+
*/
131+
public function isUidWhiteListed($uid) {
132+
$allowedUids = $this->config->getAppValue('limit_login_to_ip', 'whitelisted.uids', '');
133+
if($allowedUids === '') {
134+
return false;
135+
}
136+
137+
$allowedUids = explode(',', $allowedUids);
138+
139+
return in_array($uid, $allowedUids, true);
140+
}
141+
142+
public function handleLoginRequest(array $params) {
143+
if ( !$this->isUidWhiteListed($params['uid']) && !$this->isIPWhiteListed() ) {
144+
$this->denyLoginRequest();
145+
}
146+
}
147+
148+
public function denyLoginRequest() {
129149
// Web UI
130150
if($this->isLoginPage) {
131151
$url = $this->urlGenerator->linkToRouteAbsolute('limit_login_to_ip.LoginDenied.showErrorPage');

templates/admin-settings.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,16 @@
3838
<input type="number" id="limit-login-to-ip-whitelist_mask" name="limit-login-to-ip-whitelist_mask" placeholder="24" style="width: 50px;" disabled>
3939
<input type="button" id="limit-login-to-ip-submit" value="<?php p($l->t('Add')); ?>" disabled>
4040
</div>
41+
42+
<em><?php p($l->t('These users can connect from any IP address. These are matched against the authentication username.')) ?></em>
43+
44+
<br/>
45+
<img id="limit-login-to-ip-uidlist-spinner" src="<?php p(image_path('core', 'loading.gif')); ?>"/>
46+
<table id="limit-login-to-ip-uidlist">
47+
</table>
48+
49+
<div id="limit-login-to-ip-uid-input-fields">
50+
<input type="text" name="limit-login-to-ip-uid-whitelist" id="limit-login-to-ip-uid-whitelist" placeholder="username" style="width: 200px;" disabled/>
51+
<input type="button" id="limit-login-to-ip-uid-submit" value="<?php p($l->t('Add')); ?>" disabled>
52+
</div>
4153
</form>

0 commit comments

Comments
 (0)