Skip to content

Commit 3ab5f39

Browse files
committed
Merge branch 'next' into release.24.12
2 parents ba5e9e9 + cfd9e19 commit 3ab5f39

File tree

21 files changed

+1581
-3480
lines changed

21 files changed

+1581
-3480
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ Fixes:
33
- [push] Fixed bug where IOS credentials get mixed up while sending messages from different apps at the same time
44
- [push] Fixed bug where it crashes in connection pool growth because of a type mismatch in an if condition
55

6+
Features:
7+
- [user-management] Global admins can now disable 2FA for individual users
8+
69
Dependencies:
710
- Bump express from 4.21.1 to 4.21.2
811
- Bump mocha from 10.2.0 to 10.8.2

api/api.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ plugins.connectToAllDatabases().then(function() {
109109
password_rotation: 3,
110110
password_autocomplete: true,
111111
robotstxt: "User-agent: *\nDisallow: /",
112-
dashboard_additional_headers: "X-Frame-Options:deny\nX-XSS-Protection:1; mode=block\nStrict-Transport-Security:max-age=31536000 ; includeSubDomains\nX-Content-Type-Options: nosniff",
113-
api_additional_headers: "X-Frame-Options:deny\nX-XSS-Protection:1; mode=block\nAccess-Control-Allow-Origin:*",
112+
dashboard_additional_headers: "X-Frame-Options:deny\nX-XSS-Protection:1; mode=block\nStrict-Transport-Security:max-age=31536000; includeSubDomains; preload\nX-Content-Type-Options: nosniff",
113+
api_additional_headers: "X-Frame-Options:deny\nX-XSS-Protection:1; mode=block\nStrict-Transport-Security:max-age=31536000; includeSubDomains; preload\nAccess-Control-Allow-Origin:*",
114114
dashboard_rate_limit_window: 60,
115115
dashboard_rate_limit_requests: 500,
116116
proxy_hostname: "",

bin/commands/scripts/apidocs.sh

+2
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,7 @@ then
1212
elif [ "$1" = "generate" ]; then
1313
echo 'yes'
1414
echo "$DIR/../../../../plugins/"
15+
npm install apidoc;
16+
npm install apidoc-template;
1517
"$DIR/../../../node_modules/.bin/apidoc" -c "$DIR/../../../apidoc.json" -f "api/.*\\.js$" -i "$DIR/../../../plugins/" -o "$DIR/../../../frontend/express/public/apidoc/" -t "$DIR/../../../node_modules/apidoc-template/template/";
1618
fi

bin/commands/scripts/docs.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ elif [ "$1" = "generate" ]; then
2020
npx jsdoc "$DIR/../../../frontend/express/app.js" "$DIR/../../../frontend/express/config.sample.js" "$DIR/../../../frontend/express/version.info.js" "$DIR/../../../frontend/express/locale.conf.js" "$DIR/../../../frontend/express/libs/" -R "$DIR/../../../README.md" -c "$DIR/../../../jsdoc_conf.json" -d "$DIR/../../../frontend/express/public/docs/app" ;
2121

2222
#apidoc
23-
npx apidoc -i "$DIR/../../../" -o "$DIR/../../../frontend/express/public/docs/apidoc" -f ".*\\.js$" -e "node_modules" ;
23+
npm install apidoc; npm install apidoc-template; npx apidoc -i "$DIR/../../../" -o "$DIR/../../../frontend/express/public/docs/apidoc" -f ".*\\.js$" -e "node_modules" ;
2424

2525
#add redirect for main folder
2626
echo "<html><head><meta http-equiv='Refresh' content='0; url=./api/index.html'/><script type='javascript'>window.location = './api/index.html';</script></head></html>" > "$DIR/../../../frontend/express/public/docs/index.html"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
Script should be placed in ./bin/scripts/member-managament/disable_2fa.js
3+
4+
Script is used to disable user(s) 2FA by email.
5+
*/
6+
7+
var pluginManager = require('../../../plugins/pluginManager.js');
8+
9+
const dry_run = false; //if set true, there will be only information outputted about users in the email list, but 2FA disable operation will not be triggered.
10+
//const EMAILS = ["[email protected]", "[email protected]"];
11+
const EMAILS = [''];
12+
13+
if (dry_run) {
14+
console.log("This is a dry run");
15+
console.log("Members will only be listed, 2FA will not be disabled");
16+
}
17+
18+
pluginManager.dbConnection().then(async(countlyDb) => {
19+
try {
20+
// Find the users by email
21+
let users = [];
22+
users = await getUsers(countlyDb, EMAILS);
23+
24+
console.log(`The following ${users.length} user(s) 2FA will be disabled: `);
25+
console.log(JSON.stringify(users));
26+
if (!dry_run) {
27+
await countlyDb.collection('members').updateMany({_id: {$in: users.map(user=>user._id)}},
28+
{
29+
$set: {"two_factor_auth.enabled": false},
30+
$unset: {"two_factor_auth.secret_token": ""}
31+
});
32+
console.log("All done");
33+
}
34+
}
35+
catch (error) {
36+
console.log("ERROR: ");
37+
console.log(error);
38+
}
39+
finally {
40+
countlyDb.close();
41+
}
42+
});
43+
44+
function getUsers(db, emails) {
45+
const query = {};
46+
if (emails?.length) {
47+
query.email = {
48+
$in: emails
49+
};
50+
}
51+
return db.collection('members').find(query, {
52+
projection: { _id: 1, email: 1 }
53+
}).toArray();
54+
}

frontend/express/app.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ plugins.setConfigs("security", {
171171
password_rotation: 3,
172172
password_autocomplete: true,
173173
robotstxt: "User-agent: *\nDisallow: /",
174-
dashboard_additional_headers: "X-Frame-Options:deny\nX-XSS-Protection:1; mode=block\nStrict-Transport-Security:max-age=31536000 ; includeSubDomains\nX-Content-Type-Options: nosniff",
175-
api_additional_headers: "X-Frame-Options:deny\nX-XSS-Protection:1; mode=block\nAccess-Control-Allow-Origin:*",
174+
dashboard_additional_headers: "X-Frame-Options:deny\nX-XSS-Protection:1; mode=block\nStrict-Transport-Security:max-age=31536000; includeSubDomains; preload\nX-Content-Type-Options: nosniff",
175+
api_additional_headers: "X-Frame-Options:deny\nX-XSS-Protection:1; mode=block\nStrict-Transport-Security:max-age=31536000; includeSubDomains; preload\nAccess-Control-Allow-Origin:*",
176176
dashboard_rate_limit_window: 60,
177177
dashboard_rate_limit_requests: 500
178178
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# If you would like to report a security issue with Countly Server, Countly SDKs
2+
# please get in touch via the below method
3+
Contact: mailto:[email protected]
4+
Expires: 2025-03-14T00:00:00.000Z
5+
Preferred-Languages: en
6+
Canonical: https://securitytxt.org/.well-known/security.txt
7+
Policy: https://countly.com/legal/privacy-policy

frontend/express/public/core/user-management/javascripts/countly.models.js

+16
Original file line numberDiff line numberDiff line change
@@ -215,5 +215,21 @@
215215
callback(err.responseJSON.result);
216216
});
217217
};
218+
countlyUserManagement.disableTwoFactorAuth = function(id, callback) {
219+
return $.ajax({
220+
type: "GET",
221+
url: countlyGlobal.path + "/i/two-factor-auth",
222+
data: {
223+
method: "admin_disable",
224+
uid: id
225+
},
226+
success: function() {
227+
callback();
228+
},
229+
error: function(err) {
230+
callback(err.responseJSON.result);
231+
}
232+
});
233+
};
218234

219235
})((window.countlyUserManagement = window.countlyUserManagement || {}));

frontend/express/public/core/user-management/javascripts/countly.views.js

+19
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
},
6666
roleMap: roleMap,
6767
showLogs: countlyGlobal.plugins.indexOf('systemlogs') > -1,
68+
twoFactorAuth: countlyGlobal.plugins.indexOf('two-factor-auth') > -1,
6869
tableDynamicCols: tableDynamicCols,
6970
userManagementPersistKey: 'userManagement_table_' + countlyCommon.ACTIVE_APP_ID,
7071
isGroupPluginEnabled: isGroupPluginEnabled
@@ -114,6 +115,9 @@
114115
}
115116
},
116117
methods: {
118+
is2faEnabled: function(row) {
119+
return countlyGlobal.member.global_admin && this.twoFactorAuth && row.two_factor_auth && row.two_factor_auth.enabled;
120+
},
117121
handleCommand: function(command, index) {
118122
switch (command) {
119123
case "delete-user":
@@ -157,6 +161,21 @@
157161
});
158162
});
159163
break;
164+
case 'disable-2fa':
165+
countlyUserManagement.disableTwoFactorAuth(index, function(err) {
166+
if (err) {
167+
CountlyHelpers.notify({
168+
message: CV.i18n('two-factor-auth.faildisable_title'),
169+
type: 'error'
170+
});
171+
return;
172+
}
173+
CountlyHelpers.notify({
174+
message: CV.i18n('two-factor-auth.disable_title'),
175+
type: 'success'
176+
});
177+
});
178+
break;
160179
}
161180
},
162181
handleSubmitFilter: function(newFilter) {

frontend/express/public/core/user-management/templates/data-table.html

+3
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ <h4>{{i18n('management-users.view-title')}}</h4>
7575
sortable="true" prop="username" :label="i18n('management-users.username')">
7676
<template v-slot="rowScope">
7777
<span class="text-medium" :data-test-id="'datatable-users-username-' + rowScope.$index">{{rowScope.row.username}}</span>
78+
</template>
7879
</el-table-column>
7980
<el-table-column
8081
v-if="col.value === 'role'"
@@ -90,6 +91,7 @@ <h4>{{i18n('management-users.view-title')}}</h4>
9091
sortable="true" prop="email" :label="i18n('management-users.email')">
9192
<template v-slot="rowScope">
9293
<span class="text-medium" :data-test-id="'datatable-users-email-' + rowScope.$index">{{rowScope.row.email}}</span>
94+
</template>
9395
</el-table-column>
9496
<el-table-column v-if="col.value === 'group'" sortable="true" prop="groupNames" :label="i18n('management-users.group')">
9597
<template v-slot="rowScope">
@@ -129,6 +131,7 @@ <h4>{{i18n('management-users.view-title')}}</h4>
129131
<el-dropdown-item v-if="showLogs" command="show-logs" :data-test-id="'datatable-users-more-button-view-logs-select-' + rowScope.$index">{{ i18n('management-users.view-user-logs') }}</el-dropdown-item>
130132
<el-dropdown-item command="reset-logins" :data-test-id="'datatable-users-more-button-reset-logins-select-' + rowScope.$index">{{ i18n('management-users.reset-failed-logins') }}</el-dropdown-item>
131133
<el-dropdown-item command="delete-user" :data-test-id="'datatable-users-more-button-delete-user-select-' + rowScope.$index">{{ i18n('management-users.delete-user') }}</el-dropdown-item>
134+
<el-dropdown-item v-if="is2faEnabled(rowScope.row)" command="disable-2fa" :data-test-id="'datatable-users-more-button-disable-2fa-select-' + rowScope.$index">{{ i18n('management-users.disable-2fa-user') }}</el-dropdown-item>
132135
</cly-more-options>
133136
</template>
134137
</el-table-column>

0 commit comments

Comments
 (0)