Login form: Allow user to choose the connection URL

if `websocket_url` and `bosh_service_url` are not set and XEP-0156
lookup was unsuccessful.
This commit is contained in:
JC Brand 2021-12-04 23:42:42 +01:00
parent 0dee9897dd
commit 048560908e
6 changed files with 78 additions and 41 deletions

View File

@ -612,30 +612,24 @@ _converse.initConnection = function () {
if (api.settings.get("authentication") === _converse.PREBIND) {
throw new Error("authentication is set to 'prebind' but we don't have a BOSH connection");
}
if (! api.settings.get("websocket_url")) {
throw new Error("initConnection: you must supply a value for either the bosh_service_url or websocket_url or both.");
}
}
let connection_url = '';
const XMPPConnection = _converse.isTestEnv() ? MockConnection : Connection;
if (('WebSocket' in window || 'MozWebSocket' in window) && api.settings.get("websocket_url")) {
_converse.connection = new XMPPConnection(
api.settings.get("websocket_url"),
Object.assign(_converse.default_connection_options, api.settings.get("connection_options"))
);
connection_url = api.settings.get('websocket_url');
} else if (api.settings.get('bosh_service_url')) {
_converse.connection = new XMPPConnection(
api.settings.get('bosh_service_url'),
Object.assign(
_converse.default_connection_options,
api.settings.get("connection_options"),
{'keepalive': api.settings.get("keepalive")}
)
);
} else {
throw new Error("initConnection: this browser does not support "+
"websockets and bosh_service_url wasn't specified.");
connection_url = api.settings.get('bosh_service_url');
}
_converse.connection = new XMPPConnection(
connection_url,
Object.assign(
_converse.default_connection_options,
api.settings.get("connection_options"),
{'keepalive': api.settings.get("keepalive")}
)
);
setUpXMLLogging();
/**
* Triggered once the `Connection` constructor has been initialized, which

View File

@ -102,6 +102,9 @@ export class Connection extends Strophe.Connection {
const domain = Strophe.getDomainFromJid(jid);
await this.discoverConnectionMethods(domain);
}
if (!api.settings.get('bosh_service_url') && !api.settings.get("websocket_url")) {
throw new Error("You must supply a value for either the bosh_service_url or websocket_url or both.");
}
super.connect(jid, password, callback || this.onConnectStatusChanged, BOSH_WAIT);
}

View File

@ -4,24 +4,6 @@ const { Strophe } = converse.env;
describe("Converse", function() {
describe("Authentication", function () {
it("needs either a bosh_service_url a websocket_url or both", mock.initConverse(async (_converse) => {
const { api } = _converse;
const url = api.settings.get('bosh_service_url');
const connection = _converse.connection;
_converse.api.settings.set('bosh_service_url', undefined);
delete _converse.connection;
try {
await _converse.initConnection();
} catch (e) {
_converse.api.settings.set('bosh_service_url', url);
_converse.connection = connection;
expect(e.message).toBe("initConnection: you must supply a value for either the bosh_service_url or websocket_url or both.");
}
}));
});
describe("A chat state indication", function () {
it("are sent out when the client becomes or stops being idle",
@ -53,7 +35,7 @@ describe("Converse", function() {
describe("Automatic status change", function () {
it("happens when the client is idle for long enough",
mock.initConverse(['initialized'], {}, async (_converse) => {
mock.initConverse(['chatBoxesFetched'], {}, async (_converse) => {
const { api } = _converse;
let i = 0;

View File

@ -1,6 +1,7 @@
import bootstrap from 'bootstrap.native';
import tpl_login_panel from './templates/loginform.js';
import { CustomElement } from 'shared/components/element.js';
import { Model } from '@converse/skeletor/src/model.js';
import { __ } from 'i18n';
import { _converse, api, converse } from '@converse/headless/core';
@ -10,7 +11,9 @@ const { Strophe, u } = converse.env;
class LoginForm extends CustomElement {
initialize () {
this.listenTo(_converse.connfeedback, 'change', this.requesUpdate);
this.model = new Model();
this.listenTo(_converse.connfeedback, 'change', () => this.requestUpdate());
this.listenTo(this.model, 'change', () => this.requestUpdate());
}
render () {
@ -21,6 +24,35 @@ class LoginForm extends CustomElement {
this.initPopovers();
}
async onLoginFormSubmitted (ev) {
ev?.preventDefault();
if (api.settings.get('bosh_service_url') ||
api.settings.get('websocket_url') ||
this.model.get('show_connection_url_input')) {
this.authenticate(ev);
}
await this.discoverConnectionMethods(ev);
if (api.settings.get('bosh_service_url') || api.settings.get('websocket_url')) {
this.authenticate(ev);
} else {
this.model.set('show_connection_url_input', true);
}
}
// eslint-disable-next-line class-methods-use-this
async discoverConnectionMethods (ev) {
if (!api.settings.get("discover_connection_methods")) {
return;
}
const form_data = new FormData(ev.target);
const jid = form_data.get('jid');
const domain = Strophe.getDomainFromJid(jid);
if (!_converse.connection?.jid || (jid && !u.isSameDomain(_converse.connection.jid, jid))) {
await _converse.initConnection();
}
return _converse.connection.discoverConnectionMethods(domain);
}
initPopovers () {
Array.from(this.querySelectorAll('[data-title]')).forEach(el => {
new bootstrap.Popover(el, {
@ -52,7 +84,6 @@ class LoginForm extends CustomElement {
* @param { Event } ev
*/
authenticate (ev) {
ev?.preventDefault();
if (api.settings.get('authentication') === _converse.ANONYMOUS) {
return this.connect(_converse.jid, null);
}
@ -61,6 +92,13 @@ class LoginForm extends CustomElement {
}
const form_data = new FormData(ev.target);
const connection_url = form_data.get('connection-url');
if (connection_url?.startsWith('ws')) {
api.settings.set('websocket_url', connection_url);
} else if (connection_url?.startsWith('http')) {
api.settings.set('bosh_service_url', connection_url);
}
_converse.config.save({ 'trusted': (form_data.get('trusted') && true) || false });
let jid = form_data.get('jid');

View File

@ -24,6 +24,24 @@ const trust_checkbox = (checked) => {
`;
}
const connection_url_input = () => {
const i18n_connection_url = __('Connection URL');
const i18n_form_help = __('HTTP or websocket URL that is used to connect to your XMPP server');
const i18n_placeholder = __('e.g. wss://example.org/xmpp-websocket');
return html`
<div class="form-group fade-in">
<label for="converse-conn-url">${i18n_connection_url}</label>
<p class="form-help instructions">${i18n_form_help}</p>
<input id="converse-conn-url"
class="form-control"
required="required"
type="url"
name="connection-url"
placeholder="${i18n_placeholder}"/>
</div>
`;
}
const password_input = () => {
const i18n_password = __('Password');
return html`
@ -73,6 +91,7 @@ const auth_fields = (el) => {
placeholder="${placeholder_username}"/>
</div>
${ (authentication !== _converse.EXTERNAL) ? password_input() : '' }
${ el.model.get('show_connection_url_input') ? connection_url_input() : '' }
${ show_trust_checkbox ? trust_checkbox(show_trust_checkbox === 'off' ? false : true) : '' }
<fieldset class="form-group buttons">
<input class="btn btn-primary" type="submit" value="${i18n_login}"/>
@ -105,7 +124,7 @@ export default (el) => {
const conn_feedback_message = _converse.connfeedback.get('message');
return html`
<converse-brand-heading></converse-brand-heading>
<form id="converse-login" class="converse-form" method="post" @submit=${el.authenticate}>
<form id="converse-login" class="converse-form" method="post" @submit=${el.onLoginFormSubmitted}>
<div class="conn-feedback fade-in ${ !pretty_status ? 'hidden' : feedback_class}">
<p class="feedback-subject">${ pretty_status }</p>
<p class="feedback-message ${ !conn_feedback_message ? 'hidden' : '' }">${conn_feedback_message}</p>

View File

@ -38,8 +38,9 @@
muc_domain: 'conference.chat.example.org',
muc_respect_autojoin: true,
view_mode: 'fullscreen',
websocket_url: 'ws://chat.example.org:5380/xmpp-websocket',
// websocket_url: 'ws://chat.example.org:5380/xmpp-websocket',
// bosh_service_url: 'http://chat.example.org:5280/http-bind',
allow_user_defined_connection_url: true,
muc_show_logs_before_join: true,
whitelisted_plugins: ['converse-debug', 'converse-batched-probe'],
blacklisted_plugins: [],