Compare commits
5 changed files with 28 additions and 158 deletions
18
README.md
18
README.md
|
|
@ -20,28 +20,10 @@ Compared to lldap, llldap:
|
||||||
- supports mailalias with multiple values in search queries
|
- supports mailalias with multiple values in search queries
|
||||||
- support listening on unix domain sockets (UDS) out-of-the-box
|
- support listening on unix domain sockets (UDS) out-of-the-box
|
||||||
|
|
||||||
## Design
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> Most of these design decisions have not been implemented yet.
|
|
||||||
|
|
||||||
- different users can have the same username across different domains, so `foo@bar.com` is not the same
|
|
||||||
user as `foo@baz.com`
|
|
||||||
- service accounts are separated from normal user accounts, and do not have an associated domain;
|
|
||||||
they allow 3rd-party programs to connect via LDAP with special sets of permissions
|
|
||||||
- service groups are separated from normal user groups, and do not have an associated domain;
|
|
||||||
they are meant to store permissions (eg. `admins`, or `mail`)
|
|
||||||
- aliases are supported, but remain associated with a user account forever ([more details](docs/aliases.md))
|
|
||||||
- TODO: should users be able to create custom groups/aliases? that sounds useful, but may need monitoring
|
|
||||||
to prevent accidental/malicious abuse (such as registering `comptabilité@` or `legalteam@`)
|
|
||||||
|
|
||||||
## Potential future features
|
## Potential future features
|
||||||
|
|
||||||
We may explore in the future:
|
We may explore in the future:
|
||||||
|
|
||||||
- i18n domains (punycode)
|
|
||||||
- i18n user identifiers (TODO: check compatibility with other tools)
|
|
||||||
- RTL languages
|
|
||||||
- hierarchical groups
|
- hierarchical groups
|
||||||
- fine-grained permissions (RBAC)
|
- fine-grained permissions (RBAC)
|
||||||
- running with different async runtimes
|
- running with different async runtimes
|
||||||
|
|
|
||||||
|
|
@ -2,109 +2,54 @@ body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#login-screen {
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
/* width: 100%; */
|
|
||||||
/* height: 100%; */
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: space-around;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#logo {
|
#logo {
|
||||||
display: block;
|
display: block;
|
||||||
|
max-height: 5rem;
|
||||||
|
max-width: 5rem;
|
||||||
|
height: auto;
|
||||||
width: auto;
|
width: auto;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-line {
|
#center {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: space-around;
|
||||||
|
flex-direction: column;
|
||||||
|
max-width: 250px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#login-form {
|
||||||
|
max-width: 200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#login-line {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
font-size: 2rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
align-items: center;
|
height: 2.5rem;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-line > input {
|
#login-line > img {
|
||||||
border-color: rgba(31, 41, 55, 0.2);
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 1407px) {
|
|
||||||
#login-screen {
|
|
||||||
font-size: 24pt;
|
|
||||||
gap: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#logo {
|
|
||||||
max-width: 25%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-line {
|
|
||||||
height: 5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-line > img {
|
|
||||||
height: 3rem;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-line > input {
|
|
||||||
width: 40%;
|
|
||||||
height: 4rem;
|
|
||||||
border-radius: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-line > input::placeholder {
|
|
||||||
font-size: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-line-input {
|
|
||||||
/* Center the middle of the input relative to the logo. Is that a good idea? */
|
|
||||||
margin-right: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#submit-login {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1408px) {
|
|
||||||
#login-screen {
|
|
||||||
max-width: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#logo {
|
|
||||||
max-height: 5rem;
|
|
||||||
max-width: 5rem;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-line {
|
|
||||||
height: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-line > img {
|
|
||||||
width: 1.75rem;
|
width: 1.75rem;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-line > input {
|
#login-line > input {
|
||||||
|
border-color: rgba(31, 41, 55, 0.2);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
padding: 0.5rem 1rem;
|
||||||
|
|
||||||
.login-line-input {
|
|
||||||
/* Center the middle of the input relative to the logo. Is that a good idea? */
|
|
||||||
margin-right: 1.75rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#submit-login {
|
#submit-login {
|
||||||
|
width: 100%;
|
||||||
background-color: rgb(87, 13, 248);
|
background-color: rgb(87, 13, 248);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
# Aliases
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> This is a design document and does not necessarily reflect the current state
|
|
||||||
> of the implementation.
|
|
||||||
|
|
||||||
[Mail aliases](https://en.wikipedia.org/wiki/Email_alias) are supported in llldap, in three forms:
|
|
||||||
|
|
||||||
- *user aliases* are associated with an account, in order to provide more pseudonymity and limit
|
|
||||||
damage in case of data breach (eg. signup to Google with `mygooglealias@example.com`)
|
|
||||||
- *group aliases* are a simple group with special permission to receive emails; all emails
|
|
||||||
are forwarded to all members, and all members can send mail from the alias
|
|
||||||
- *service aliases* are reserved keywords (eg. `abuse`) on every domain and forward emails they receive
|
|
||||||
both to domain admins and to global admins
|
|
||||||
|
|
||||||
## User aliases
|
|
||||||
|
|
||||||
Depending on a domain's configuration, a user may or may not create aliases. When a user acquires an
|
|
||||||
alias, it is associated to their account forever.
|
|
||||||
|
|
||||||
When deleted, your alias remains as a *tombstone* preventing another user from acquiring it and receiving your mail.
|
|
||||||
An alias can be recovered (re-enabled) by its owner at any time. Email to a deleted alias just doesn't go anywhere.
|
|
||||||
|
|
||||||
**TODO:** Maybe we'd like domain admins to be able to reclaim tombstones?
|
|
||||||
|
|
||||||
## Group aliases
|
|
||||||
|
|
||||||
A group on a domain may or may not be configured as a mail alias. When it is, all group members can send and
|
|
||||||
receive mail with the group's address, such as `mygroup@example.com`.
|
|
||||||
|
|
||||||
As a consequence, `uid` on a domain are unique across users and groups. If there is already a user account
|
|
||||||
`example@example.com`, then we cannot create a group `example@example.com`.
|
|
||||||
|
|
||||||
A deleted group remains as a *tombstone* to prevent leaking future group emails by a new user registering
|
|
||||||
as the group name. Email to a deleted group, or to a group that has disabled email, just doesn't go anywhere.
|
|
||||||
|
|
||||||
**TODO:** Maybe we'd like domain admins to be able to reclaim tombstones?
|
|
||||||
|
|
||||||
## Service aliases
|
|
||||||
|
|
||||||
Some special keywords are reserved for important mail communication: *admin*, *admins*, *root*, *hostmaster*, *postmaster*, *abuse*.
|
|
||||||
**TODO:** should `contact@` be in this list as well?
|
|
||||||
|
|
||||||
The `admins` group on each domain can be edited by domain admins to add/remove admins for the domain.
|
|
||||||
All global admins are part of every domain's `admins` group, and a domain admin cannot remove them.
|
|
||||||
Others are meta-groups that cannot ever be edited, and refer to the domain's `admins` group for membership information.
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
|
|
||||||
If we support a group as mailalias, how do we prevent collisions between users and groups?
|
|
||||||
|
|
||||||
- [ ] User aliases in DB
|
|
||||||
- [ ] Aliases in LDAP search responses
|
|
||||||
- [ ] Domain config to allow/disallow user aliases on domain
|
|
||||||
- [ ] Group setting to enable/disable receiving email to this group
|
|
||||||
|
|
@ -20,8 +20,6 @@ pub async fn logout(
|
||||||
login_page(
|
login_page(
|
||||||
State(state),
|
State(state),
|
||||||
Some(LoginError::SessionInvalidated),
|
Some(LoginError::SessionInvalidated),
|
||||||
// TODO: this is wrong because we produce an empty redirect query which is
|
|
||||||
// then converted to b64URL (and panics).
|
|
||||||
InternalRedirect::new(),
|
InternalRedirect::new(),
|
||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<main id="login-screen">
|
<main id="center">
|
||||||
<img src="/assets/img/logo.png" id="logo">
|
<img src="/assets/img/logo.png" id="logo">
|
||||||
{% if login_error == "InvalidCredentials" %}
|
{% if login_error == "InvalidCredentials" %}
|
||||||
<div class="banner is-error">
|
<div class="banner is-error">
|
||||||
|
|
@ -12,15 +12,15 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form method="POST" action="{{ login_url }}">
|
<form method="POST" action="{{ login_url }}">
|
||||||
<div class="login-line login-line-input">
|
<div id="login-line">
|
||||||
<img src="/assets/img/user.svg">
|
<img src="/assets/img/user.svg">
|
||||||
<input type="text" name="username" id="username" placeholder="Username">
|
<input type="text" name="username" id="username" placeholder="Username">
|
||||||
</div>
|
</div>
|
||||||
<div class="login-line login-line-input">
|
<div id="login-line">
|
||||||
<img src="/assets/img/password.svg">
|
<img src="/assets/img/password.svg">
|
||||||
<input type="password" name="password" id="password" placeholder="Password">
|
<input type="password" name="password" id="password" placeholder="Password">
|
||||||
</div>
|
</div>
|
||||||
<div class="login-line">
|
<div id="login-line">
|
||||||
<input type="submit" id="submit-login" value="LOG IN">
|
<input type="submit" id="submit-login" value="LOG IN">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue