thelounge/client/components/NetworkForm.vue
Nachtalb 08f45eabb2
Restrict what the browser should try to autocomplete
Browsers often autocomplete into wrong fields. Eg. wanting to put the password in a fields in the settings screen and then use a sudo random other fields for username etc.
This is rather annoying and can break someones configuration, thus we should only enable it on fields where it somewhat makes sense (name, server & password fields).
2021-04-12 14:10:33 +02:00

450 lines
11 KiB
Vue

<template>
<div id="connect" class="window" role="tabpanel" aria-label="Connect">
<div class="header">
<SidebarToggle />
</div>
<form class="container" method="post" action="" @submit.prevent="onSubmit">
<h1 class="title">
<template v-if="defaults.uuid">
<input v-model="defaults.uuid" type="hidden" name="uuid" />
Edit {{ defaults.name }}
</template>
<template v-else>
Connect
<template v-if="config.lockNetwork && $store.state.serverConfiguration.public">
to {{ defaults.name }}
</template>
</template>
</h1>
<template v-if="!config.lockNetwork">
<h2>Network settings</h2>
<div class="connect-row">
<label for="connect:name">Name</label>
<input
id="connect:name"
v-model="defaults.name"
class="input"
name="name"
maxlength="100"
/>
</div>
<div class="connect-row">
<label for="connect:host">Server</label>
<div class="input-wrap">
<input
id="connect:host"
v-model="defaults.host"
class="input"
name="host"
aria-label="Server address"
maxlength="255"
required
/>
<span id="connect:portseparator">:</span>
<input
id="connect:port"
v-model="defaults.port"
class="input"
type="number"
min="1"
max="65535"
name="port"
aria-label="Server port"
/>
</div>
</div>
<div class="connect-row">
<label for="connect:password">Password</label>
<RevealPassword
v-slot:default="slotProps"
class="input-wrap password-container"
>
<input
id="connect:password"
v-model="defaults.password"
class="input"
:type="slotProps.isVisible ? 'text' : 'password'"
placeholder="Server password (optional)"
name="password"
maxlength="300"
/>
</RevealPassword>
</div>
<div class="connect-row">
<label></label>
<div class="input-wrap">
<label class="tls">
<input
v-model="defaults.tls"
type="checkbox"
name="tls"
:disabled="defaults.hasSTSPolicy"
/>
Use secure connection (TLS)
<span
v-if="defaults.hasSTSPolicy"
class="tooltipped tooltipped-n tooltipped-no-delay"
aria-label="This network has a strict transport security policy, you will be unable to disable TLS"
>🔒 STS</span
>
</label>
<label class="tls">
<input
v-model="defaults.rejectUnauthorized"
type="checkbox"
name="rejectUnauthorized"
/>
Only allow trusted certificates
</label>
</div>
</div>
</template>
<template v-else-if="config.lockNetwork && !$store.state.serverConfiguration.public">
<h2>Network settings</h2>
<div class="connect-row">
<label for="connect:name">Name</label>
<input
id="connect:name"
v-model="defaults.name"
class="input"
name="name"
maxlength="100"
/>
</div>
<div class="connect-row">
<label for="connect:password">Password</label>
<RevealPassword
v-slot:default="slotProps"
class="input-wrap password-container"
>
<input
id="connect:password"
v-model="defaults.password"
class="input"
:type="slotProps.isVisible ? 'text' : 'password'"
placeholder="Server password (optional)"
name="password"
maxlength="300"
/>
</RevealPassword>
</div>
</template>
<h2>User preferences</h2>
<div class="connect-row">
<label for="connect:nick">Nick</label>
<input
id="connect:nick"
v-model="defaults.nick"
class="input nick"
name="nick"
pattern="[^\s:!@]+"
maxlength="100"
required
@input="onNickChanged"
/>
</div>
<template v-if="!config.useHexIp">
<div class="connect-row">
<label for="connect:username">Username</label>
<input
id="connect:username"
ref="usernameInput"
v-model="defaults.username"
class="input username"
name="username"
maxlength="100"
/>
</div>
</template>
<div class="connect-row">
<label for="connect:realname">Real name</label>
<input
id="connect:realname"
v-model="defaults.realname"
class="input"
name="realname"
maxlength="300"
/>
</div>
<div class="connect-row">
<label for="connect:leaveMessage">Leave message</label>
<input
id="connect:leaveMessage"
v-model="defaults.leaveMessage"
autocomplete="off"
class="input"
name="leaveMessage"
placeholder="The Lounge - https://thelounge.chat"
/>
</div>
<template v-if="defaults.uuid && !$store.state.serverConfiguration.public">
<div class="connect-row">
<label for="connect:commands">
Commands
<span
class="tooltipped tooltipped-ne tooltipped-no-delay"
aria-label="One /command per line.
Each command will be executed in
the server tab on new connection"
>
<button class="extra-help" />
</span>
</label>
<textarea
id="connect:commands"
ref="commandsInput"
autocomplete="off"
:value="defaults.commands ? defaults.commands.join('\n') : ''"
class="input"
name="commands"
@input="resizeCommandsInput"
/>
</div>
</template>
<template v-else-if="!defaults.uuid">
<div class="connect-row">
<label for="connect:channels">Channels</label>
<input
id="connect:channels"
v-model="defaults.join"
class="input"
name="join"
/>
</div>
</template>
<template v-if="$store.state.serverConfiguration.public">
<template v-if="config.lockNetwork">
<div class="connect-row">
<label></label>
<div class="input-wrap">
<label class="tls">
<input v-model="displayPasswordField" type="checkbox" />
I have a password
</label>
</div>
</div>
<div v-if="displayPasswordField" class="connect-row">
<label for="connect:password">Password</label>
<RevealPassword
v-slot:default="slotProps"
class="input-wrap password-container"
>
<input
id="connect:password"
ref="publicPassword"
v-model="defaults.password"
class="input"
:type="slotProps.isVisible ? 'text' : 'password'"
placeholder="Server password (optional)"
name="password"
maxlength="300"
/>
</RevealPassword>
</div>
</template>
</template>
<template v-else>
<h2 id="label-auth">Authentication</h2>
<div class="connect-row connect-auth" role="group" aria-labelledby="label-auth">
<label class="opt">
<input
:checked="!defaults.sasl"
type="radio"
name="sasl"
value=""
@change="setSaslAuth('')"
/>
No authentication
</label>
<label class="opt">
<input
:checked="defaults.sasl === 'plain'"
type="radio"
name="sasl"
value="plain"
@change="setSaslAuth('plain')"
/>
Username + password (SASL PLAIN)
</label>
<label
v-if="!$store.state.serverConfiguration.public && defaults.tls"
class="opt"
>
<input
:checked="defaults.sasl === 'external'"
type="radio"
name="sasl"
value="external"
@change="setSaslAuth('external')"
/>
Client certificate (SASL EXTERNAL)
</label>
</div>
<template v-if="defaults.sasl === 'plain'">
<div class="connect-row">
<label for="connect:username">Account</label>
<input
id="connect:saslAccount"
v-model="defaults.saslAccount"
class="input"
name="saslAccount"
maxlength="100"
required
/>
</div>
<div class="connect-row">
<label for="connect:password">Password</label>
<RevealPassword
v-slot:default="slotProps"
class="input-wrap password-container"
>
<input
id="connect:saslPassword"
v-model="defaults.saslPassword"
class="input"
:type="slotProps.isVisible ? 'text' : 'password'"
name="saslPassword"
maxlength="300"
required
/>
</RevealPassword>
</div>
</template>
<div v-else-if="defaults.sasl === 'external'" class="connect-sasl-external">
<p>The Lounge automatically generates and manages the client certificate.</p>
<p>
On the IRC server, you will need to tell the services to attach the
certificate fingerprint (certfp) to your account, for example:
</p>
<pre><code>/msg NickServ CERT ADD</code></pre>
</div>
</template>
<div>
<button type="submit" class="btn" :disabled="disabled ? true : false">
<template v-if="defaults.uuid">Save network</template>
<template v-else>Connect</template>
</button>
</div>
</form>
</div>
</template>
<style>
#connect .connect-auth {
display: block;
margin-bottom: 10px;
}
#connect .connect-auth .opt {
display: block;
width: 100%;
}
#connect .connect-auth input {
margin: 3px 10px 0 0;
}
#connect .connect-sasl-external {
padding: 10px;
border-radius: 2px;
background-color: #d9edf7;
color: #31708f;
}
#connect .connect-sasl-external pre {
margin: 0;
user-select: text;
}
</style>
<script>
import RevealPassword from "./RevealPassword.vue";
import SidebarToggle from "./SidebarToggle.vue";
export default {
name: "NetworkForm",
components: {
RevealPassword,
SidebarToggle,
},
props: {
handleSubmit: Function,
defaults: Object,
disabled: Boolean,
},
data() {
return {
config: this.$store.state.serverConfiguration,
previousUsername: this.defaults.username,
displayPasswordField: false,
};
},
watch: {
displayPasswordField(value) {
if (value) {
this.$nextTick(() => this.$refs.publicPassword.focus());
}
},
"defaults.commands"() {
this.$nextTick(this.resizeCommandsInput);
},
"defaults.tls"(isSecureChecked) {
const ports = [6667, 6697];
const newPort = isSecureChecked ? 0 : 1;
// If you disable TLS and current port is 6697,
// set it to 6667, and vice versa
if (this.defaults.port === ports[newPort]) {
this.defaults.port = ports[1 - newPort];
}
},
},
methods: {
setSaslAuth(type) {
this.defaults.sasl = type;
},
onNickChanged(event) {
// Username input is not available when useHexIp is set
if (!this.$refs.usernameInput) {
return;
}
if (
!this.$refs.usernameInput.value ||
this.$refs.usernameInput.value === this.previousUsername
) {
this.$refs.usernameInput.value = event.target.value;
}
this.previousUsername = event.target.value;
},
onSubmit(event) {
const formData = new FormData(event.target);
const data = {};
for (const item of formData.entries()) {
data[item[0]] = item[1];
}
this.handleSubmit(data);
},
resizeCommandsInput() {
if (!this.$refs.commandsInput) {
return;
}
// Reset height first so it can down size
this.$refs.commandsInput.style.height = "";
// 2 pixels to account for the border
this.$refs.commandsInput.style.height =
Math.ceil(this.$refs.commandsInput.scrollHeight + 2) + "px";
},
},
};
</script>