casdoor 1.6.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "casdoor",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "description": "The Casdoor extension can help browser users automatically log into applications protected by Casdoor single-sign-on (SSO) system.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,6 +33,10 @@
33
33
  skip: "不用了",
34
34
  saved: "已保存到 Casdoor",
35
35
  close: "关闭",
36
+ missingSelector: "管理员配置的{field}选择器“{selector}”在这个页面上没有找到,已改用自动识别",
37
+ usernameField: "用户名输入框",
38
+ passwordField: "密码输入框",
39
+ submitField: "登录按钮",
36
40
  }
37
41
  : {
38
42
  filled: "Casdoor filled in the account {user} of {app}",
@@ -44,6 +48,10 @@
44
48
  skip: "No thanks",
45
49
  saved: "Saved to Casdoor",
46
50
  close: "Close",
51
+ missingSelector: "The {field} selector \"{selector}\" set by the admin matches nothing on this page, so it was found automatically",
52
+ usernameField: "username input",
53
+ passwordField: "password input",
54
+ submitField: "sign-in button",
47
55
  };
48
56
 
49
57
  function format(text, values) {
@@ -78,19 +86,36 @@
78
86
  return element.getClientRects().length > 0;
79
87
  }
80
88
 
89
+ // a class attribute pasted as it is, like "el-button login-btn el-button--primary", is taken as those classes
90
+ function selectorCandidates(selector) {
91
+ const value = selector.trim();
92
+ const candidates = [value];
93
+ if (/^[\w-]+(\s+[\w-]+)*$/.test(value)) {
94
+ candidates.push("." + value.split(/\s+/).join("."));
95
+ }
96
+ return candidates;
97
+ }
98
+
81
99
  function query(selector) {
82
- if (!selector) {
100
+ if (!selector || !selector.trim()) {
83
101
  return null;
84
102
  }
85
- try {
86
- return document.querySelector(selector);
87
- } catch (error) {
88
- return null;
103
+ for (const candidate of selectorCandidates(selector)) {
104
+ try {
105
+ const element = document.querySelector(candidate);
106
+ if (element) {
107
+ return element;
108
+ }
109
+ } catch (error) {
110
+ // not a valid selector, try the next way of reading it
111
+ }
89
112
  }
113
+ return null;
90
114
  }
91
115
 
92
116
  const usernameHints = /user|login|account|email|mail|phone|mobile|tel|name|uid|uname|loginid|工号|账号|帳號|用户|用戶|手机|邮箱/i;
93
- const submitHints = /^\s*(登\s*录|登\s*入|登\s*陆|log\s*in|sign\s*in|login|signin|submit|确\s*定|提\s*交|进入系统)\s*$/i;
117
+ // "登录", "登录系统", "立即登录", "Sign in", ...
118
+ const submitHints = /^\s*(?:(?:立即|马上|安全|用户|账号)?\s*(?:登\s*录|登\s*入|登\s*陆)\s*(?:系统|平台)?|进入系统|log\s*in|log\s*on|sign\s*in|login|logon|signin|submit|确\s*定|提\s*交)\s*$/i;
94
119
 
95
120
  function findPassword(site) {
96
121
  const configured = query(site.passwordSelector);
@@ -136,8 +161,14 @@
136
161
  return button;
137
162
  }
138
163
  }
139
- const clickable = [...document.querySelectorAll("button, input[type=button], input[type=submit], a, [role=button], div[onclick], span[onclick]")];
140
- return clickable.find((element) => isVisible(element) && submitHints.test(element.value || element.textContent || "")) || null;
164
+ // buttons first, so that a link or a tab with the same words loses to the real button
165
+ for (const selector of ["button, input[type=button], input[type=submit]", "[role=button]", "a, div[onclick], span[onclick]"]) {
166
+ const found = [...document.querySelectorAll(selector)].find((element) => isVisible(element) && submitHints.test(element.value || element.textContent || ""));
167
+ if (found) {
168
+ return found;
169
+ }
170
+ }
171
+ return null;
141
172
  }
142
173
 
143
174
  function setValue(input, value) {
@@ -154,8 +185,11 @@
154
185
  for (const type of ["keydown", "keypress", "keyup"]) {
155
186
  input.dispatchEvent(new KeyboardEvent(type, {key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true}));
156
187
  }
157
- if (input.form) {
158
- input.form.requestSubmit();
188
+ // a single-page app (Vue, React, ...) signs in from its own Enter handler, submitting its <form> would only reload
189
+ // the page; a server-side form says where it goes
190
+ const form = input.form;
191
+ if (form && (form.hasAttribute("action") || form.hasAttribute("method"))) {
192
+ form.requestSubmit();
159
193
  }
160
194
  }
161
195
 
@@ -187,6 +221,7 @@
187
221
  title.style.cssText = "font-weight:600;font-size:12px;color:#5c6ac4;margin-bottom:4px;";
188
222
  title.textContent = "Casdoor";
189
223
  const body = document.createElement("div");
224
+ body.style.whiteSpace = "pre-line";
190
225
  body.textContent = text;
191
226
  box.append(title, body);
192
227
  if (buttons.length > 0) {
@@ -239,7 +274,14 @@
239
274
  showNotice(strings.retry, buttons, 0);
240
275
  return;
241
276
  }
242
- showNotice(format(strings.filled, {app: site.displayName, user: account.username}), buttons);
277
+ // the admin's selectors that match nothing, so that a wrong one doesn't go unnoticed
278
+ const missing = [
279
+ [site.usernameSelector, strings.usernameField],
280
+ [site.passwordSelector, strings.passwordField],
281
+ [site.autoSubmit ? site.submitSelector : "", strings.submitField],
282
+ ].filter(([selector]) => selector && selector.trim() && !query(selector));
283
+ const warnings = missing.map(([selector, field]) => format(strings.missingSelector, {field, selector}));
284
+ showNotice([format(strings.filled, {app: site.displayName, user: account.username}), ...warnings].join("\n"), buttons, warnings.length > 0 ? 15000 : 8000);
243
285
 
244
286
  if (site.autoSubmit && !username) {
245
287
  await sleep(400);