Contents:
%[profile: ]
macro%[sess: ]
macroThalassa CMS uses single-use passwords to authentificate its users. The passwords are sent to users' emails on their requests. As of present, this is the only possibility.
Single use passwords are chosen because Thalassa is intended to be used over unencrypted channels, that is, using HTTP (as opposite to HTTPS). This of course doesn't mean you can't use HTTPS — definitely you can. If you want. From the other hand, you can use plain HTTP as well. Practice shows this doesn't make any problems for relatively small sites; despite it is possible to sniff all the traffic between the client and the server in any network between them, it is still hard for a potential attacker to take over the control on any of these networks, and the networks' personnel is in most cases just not interested in such an attack. If, however, the HTTP traffic actually gets sniffed, single-use passwords, being sent by email, will only be seen by the attacker when they get actually used (within the HTTP POST request that logs the user in) and become useless. To intercept the passwords when they are still of value, the attacker will need to sniff the SMTP channels as well, or break into the user's mailbox, or act as a man-in-the-middle rather than just sniffing the traffic, which is notably harder to perform.
WARNING: It is all still perfectly possible! In most of existing real life cases, breaking a user account at a small site is not worth bothering with all these espionage techniques, but your particular case may be different. If, for example, you expect some VIPs like the president or the prime minister of your country (or, well, leaders of political opposition) among your users, perhaps you should take some additional care, such as enabling HTTPS and making sure your outgoing SMTP traffic goes using TLS.
It is important to note that user (login) names in Thalassa CMS are used as file (well, directory) names within the database so there are strict limits on what can and what can not be used as a user name. Furthermore, there are actually two different limitations: first, what can users specify as their login names, and, second, what the site's administrator can use as login names for accounts created manually.
For both cases, only low-case latin chars, digits and the underscore are acceptable. Any char not within this set, in any position of the name, will make the name non-working even if the account is created by the administrator, because Thalassa CGI rejects it long before any password checks or other actions.
For user names choosen by users on their own, additional limitations are in effect. The name can't be shorter than 2 nor longer than 16 chars (both limits are hardcoded), and it must start with a latin letter, neither a digit nor the underscore are accepted as the first char of such a name.
For example, names such as joe
, bond007
,
mister_x
, wolf__
are allowed for users to choose
on sign up; names like x
, 007
,
7seas
, _alice
are not allowed for users to choose
on their own, but the administrator still can make such accounts; and the
following names will not work at all:
John
, JOHN
,
john.doe
, john+doe
, john-doe
and so on.
Besides the login name, a user account has the following properties:
active
,
pending
(email is to be confirmed to finish the signup
process) and blocked
;pending
status and
for accounts in process of changing their emails);all
role is always there, even if
there's no user, the anon
role is there for sessions that
aren't logged in, and the auth
role belongs to every logged in
user);pending
).Within the user database
directory, a subdirectory named _users
contains the user
accounts' information. For every account, a subdirectory in the
_users
directory is created, using the account name (login
name) as the name for that directory. For example, if your database is
located ad /var/sites/example.com
, and a new user registers
with the name lizzie
, her account's directory will be
/var/sites/example.com/_users/lizzie
.
In each user account directory there's a text file named _data
,
which contains NAME = VALUE
pairs,
just like for session
files. The following NAMEs are used to store the properties
enumerated above:
status
,
realname
,
site
,
created
,
last_login
,
last_seen
,
last_pwdsent
,
last_mailchange
,
new_email
,
confirmation_code
,
roles
.
As you may have noticed, login name and passwords are not on this list.
The login name is always the same as the name of the user account's
directory; as of passwords, they are stored as hardlinks to the
_data
file, created within the same directory. The
passwords generated by Thalassa never contain the underscore, so the name
_data
itself can not be used as a password. Once a password
is used, Thalassa CGI unlink
s it, so it effectively stops to
exist.
This is one of the simplest possible implementations
that allows not to bother locking the directory, as both link
and unlink
operations are expected to be atomic. Using hard
links is more efficient than, e.g., using empty files, as it doesn't
involve making and deleting i-nodes, only the directory and the i-node of
the _data
file (you know, that link counter field) get
changed.
%[profile: ]
macroThe profile
macro allows to access the user account properties
for an arbitrary (not necessary the current, which is sometimes important)
user. The macro accepts at least two arguments, the first for the user
login name, and the second is for the function name. It is necessary to
note that the first argument gets lowercased, and all leading and trailing
spaces get stripped from it before proceeding.
Two special functions are provided. The id
function
returns the accounts ID, which equals to the first argument after
it is stripped and lowercased. The ifexists
function accepts
two additional arguments: the then value and the else
value, and returns the former in case the requested account exists,
otherwise returns the latter.
If the second argument of the macro call is neither id
nor
ifexists
, it must be a NAME of the account's
property, as user in the account file within the database (see the previous
section for the list of these NAMEs). In this case the macro call
returns the value of the respective account's property, or an empty string
in case the property isn't found.
It might be useful to note that the macro doesn't check if the property
name really belongs to any list, so actually you can (manually or with some
third party software) add your own properties to your users' accounts
simply by adding the desired
NAME = VALUE
pair to the
_data
file of the user account in question and access these
properties with the profile
macro.
The Thalassa's notion of a valid email address is significantly different from what is specified by so-called standards. Thalassa CGI's internal email validator doesn't allow many things that are considered acceptable by the “standards”, but are never actually used.
Only the email address as such must be entered by the user. Things like
John Doe <johndoe@example.com>
or just
<john@example.com>
will be rejected. Both '<' and
'>' are considered inacceptable chars.
Quoted local-parts are not allowed, so, e.g., "this is
crap"@example.com
, "double..dot"@example.com
,
"foo"."bar"@example.com
,
"john@example.net"@example.com
and the like are rejected
despite some email software might (surprisingly, heh) consider these valid.
Did you know about that? Well, this is even not the most ugly thing the
“standards” suggest us to allow in email addresses. For example, they
allow so called “comments” within the local-part in parentheses.
Thalassa CGI doesn't allow that, so
(comment)johnny@example.com
or
johnny(comment)@example.com
will be rejected.
“Standards” allow all of the !#$%&'*+-/=?^_`{|}~
to
appear in the local-part, unquoted, with no limitations. Thalassa CMS,
however, will reject !#$&'*?/^{|}~
in any position of the
local-part, leaving the possibility to use %-+_
and,
furthermore, will reject %-+
in the first position.
Just like the “standards” specify, the dot “.
” in
local-part can appear, but not as the first nor last char, and no more than
one dot in a row.
As for domain-part, only FQDNs are accepted. IP addresses in square
brackets, like [192.168.251.1]
, are rejected.
The last thing to mention is that FQDN must consist of at least two
tokens, so, e.g., john@doe
is considered invalid.
FQDNs are also checked to be correct, which means tokens must only consist
of latin letters, digits and the dash “-
”, and must not
start nor end with a dash.
The Thalassa CGI program remembers all email addresses it has ever seen as
users' emails, unless you remove the respective file manually. The data is
stored in the _email
subdirectory within the
user database directory; a
file is created for each email address.
Email address in question is converted into the file name as follows.
First, the domain part of the address is taken, as is, and two underscores are
appended to it; then goes the local part of the address. For example,
john.doe@example.com
will become
example.com__john.doe
.
As usual in the user database, each file contains
NAME = VALUE
pairs. Usually there
are exaclty three pairs, with NAMEs status
,
user
and date
. The value for the
user
parameter is a user's login name, and the
date
is the date/time of the last status change.
The status
' value can be one of the following:
banned
or blocked
means that, for any reason,
this particular email is not allowed on the site;used
or active
means that the address
is currently used by the user account (identified with the
user
parameter);replaced
stands that the address was used by the
user account, but one day the user requested to change it and successfully
confirmed another address;pending
marks addresses that were specified in requests
for signup or email change, but weren't (yet) confirmed;pending_replaced
means that the address belonged to the
same user some time ago (and the ownership of the address was confirmed),
was replaced by another address, and recently the user requested to change
his/her address back to this one, but the change is still to be
confirmed.Technically there's only one situation when Thalassa CGI actually forgets
an email address: it is when a registered user requests to change
the address to another, previously unknown one, but later cancels the
request. However, in case an address is in the pending
(that
is, unconfirmed) state and more than 24 hours passed, the
program acts as if it never saw that address.
If an email address was entered (so a confirmation code was sent to it), but not confirmed, Thalassa CGI will refuse to send more confirmation requests to this address for a month (strictly speaking, 31x24 hours).
One more “cooldown” limit is that users are not allowed to request email change more often than once in 24 hours. All three limits are presently hardcoded.
Thalassa CGI program supports four
actions related to user
accounts: login
, signup
, profile
and
changemail
. All the four don't accept any
“commandline-like” arguments; what they do is determined solely by
parameters sent in the POST
request. We'll explain each of
them in a dedicated subsection.
The login form is intended to be shown to users to let them sign in, that is, enter the username, one of the single-use password and let the system check if the password is valid. There's an additional function for the same form (and the same action): in case the user has no more single-use passwords or the remaining passwords are lost, the user should have an option to request new passwords. Hence, the form is expected to have two submit buttons.
For a POST
request to a page for which the action parameter's
value is “login
”, the following input values are expected by
Thalassa CGI:
sendmorepass
— shows whether it is a “send more
passwords” request; in case the value is “yes
”, the request
is assumed to be for more passwords, otherwise (no matter whether there's
no such parameter or it's value differs from “yes
”) a login
(sign in) request is assumed;login
— the user's login name; used for both request
types;passtoken
— the single-use password; only expected
for a sign-in request.One important thing to note here is that for both types of the request, and
not depending on whether the request succeeded or failed, the work session
gets bound to the login name and can not be unbound; in case the user makes
a mistake in the login name, the only way to fix it is to close the session
(with the rmsession
request) and create a new one. However, to have the session bound
to user name is not the same as to be logged
in; being bound to the name does not grant any permissions, access or
whatever, it only limits into which particular user
account this particular session still may log in.
New passwords can only be sent to a registered user (that is, the account
status must be active
), and only if one of the following is
true: either there are no more unused passwords for this user, or no less
that 24 hours passed. In case everything's okay, an email containing new
passwords (20 of them, the number is presently hardcoded) is composed
and sent to the user's email address. See the
service email section for details on service
emails (well, emails like this) composition and sending.
For a sign-in request, the CGI program first checks whether the supplied password can (as a string) be a password generated by Thalassa, and then checks whether the given password exists for the particular user. If it is, the password is removed from the database, and the session changes its status to “logged in”. Otherwise, the only change to the session will be to bind it with the user name, without becoming logged in.
The signup
action expects the following field values:
userid
— the login name;username
— the visible (“real”) name for the
user;useremail
— the email address;usersite
— the optional URL for a site, presumably
the user's home page.First, the userid
is checked for acceptability; after that, it
is checked whether there's no such user yet. In case the record with such
name is found in the database, then it is checked if it has the
pending
status, and how long it exists. For a record in the
pending
status older than 24 hours, the work continues as if
there was no record at all; otherwise, registration is refused.
Next, the useremail
is checked for validity, and in case the
address is (lexically) valid, records are checked for the address. If the
address is already known to the system, the only possibility to continue is
if it is in the pending
state and the state is more that 24
hours old; the registration is refused otherwise.
The username
is only checked to be non-empty, and the
usersite
is not checked at all.
If no checks failed, a new user record (in the pending
state)
is created in the database, and an email containing a confirmation code is
sent to the given email. After that, it is possible for the user
to log into the system using the confirmation code as a
password — and this is exactly what must be done to confirm
the email address ownership. Logging in for the second time, the user
should request new passwords.
It is strongly recommended to check from within the registration page
template whether it is being displayed as a result of a successful signup
request, and display a short version of the login form, with only the
passcode
input field an the submit button, having the
login
field as a hidden type input, so that the user can enter
the confirmation code right there, not jumping around from page to page.
The profile
action lets the user to change values for the
visible (“real”) name and the site URL. This action expects two input
field values, username
and usersite
. The only
performed checks are whether the session is in the logged in state, and
whether the username
is not empty; if any of them fails,
the action fails as well. If both checks are successful, new values are
saved in the current user's record within the database.
The changemail
action allows users to replace their email
addresses. The user must first sign in.
What data is expected depends on whether the user is, according to the
record in the database, already in progress of changing the email. If not,
the CGI expects two input field values, named
“newemail
” (for the new address) and
“passtoken
” (for a single-use password).
The password is required for this operation since the
version 0.3.00. Earlier versions would only expect the
newemail
value.
First of all, the CGI program checks the password and removes it from the list of passwords available for the user. This happens even if the user specifies an invalid or non-available email and receives the respective error message; that is, if a valid password was specified, it gets used up even if no action is taken. This is because Thalassa CMS is supposed to work on unencrypted sites; hence, a valid password, once transferred through the network, must become invalid to prevent its usage by those who manage to sniff the connection.
After that, the email address is checked for validity; for a valid address, the program checks if it already knows the address. The user is allowed to try climing the address if any of the following is true:
replaced
state and earlier
belonged to the same user;pending
state and the
associated date is more than a month (well, 31x24 hours) in the past.If there are no obstacles to go on, both the user account state and the email record state are changed accordingly, and a message containing a confirmation code is composed and sent to the email address being claimed.
In case the user is already in progress of email changing, the
POST
request to a page with the changemail
action
is handled differently. It is assumed there's a web form that lets the
user either enter the confirmation code, or cancel the email change
request; hence, the form should have two submit buttons, or there may be
just two forms. Anyway, the program first checks if there's a value named
cancel_change
set to yes
; if so, it also checks
for the really
field's value to be the string
“really
” (yes, both the name and the expected
value are “really
”), and if the check passes, both
the user account and the record for the claimed email are changed
accordingly.
Otherwise (that is, if there's no cancel_change
value or if it
is not yes
), the program gets the value of the
confirmcode
parameter and checks whether it is the same as the confirmation code on the
user's account file. If the check is successful, the program updates the
user's account file and records for both the old and the new email
addresses.
Presently, there's no limit for failed attempts. This might be one of the subjects for future work.
%[sess: ]
macroThe %[sess: ]
macro was introduced along with
sessions, but only two of its functions,
cookie
and ifvalid
, were described.
The ifhasuser
, ifloggedin
,
ifcanchangemail
and
ifchangemail
functions are conditional checkers; they all take
two additional arguments: the then value and the else
value, and return the former in case the condition is true and the latter
if it is false. The conditions actually checked are as follows:
ifhasuser
, it is if there's a user account associated
with the current session, no matter whether the user has logged in or
not;ifloggedin
, it is if there's an associated user
and the user has logged in;ifcanchangemail
, it is if the user has logged in
and can request email address change; the condition
will be false if the user is either currently changing email, or
placed a request less than 24 hours ago and then cancelled it;ifchangemail
, it is if the user has logged in
and the user's account is in process of changing the email
address.The following functions are used to access the data related to the user account; they don't need parameters:
%[sess:user]
returns the associated user's login name (or
an empty string if there's no associated user);%[sess:loggeduser]
returns the user's login name in case
the user is logged in, otherwise returns an empty string;%[sess:username]
returns the associated user's visible
(“real”) name; in case the session is not logged in,
this function still may return a useful value: when an anonymous
user posts a comment, the visible name used for it is stored with the
session data and the %[sess:username]
function will return
that name, unless the session becomes a logged-in one;%[sess:useremail]
returns the associated user's email
address, even if the user account is in the pending
state and
the email address is not yet confirmed (use the fact such user can not be
logged in to check if the email can be actually used);%[sess:usersite]
returns the associated user's site URL,
or an empty string if the account doesn't have one;%[sess:usernewemail]
returns the requested new email in
case the user has an unfinished change email request, otherwise returns an
empty string;%[sess:roles]
returns a space-separated list of
roles the associated user has.The rest of the sess
macro functions,
related to the moderation
queue (namely premodq
, pqprev
and
pqnext
), are described along with
comments.
In the text above (on this page) we've seen two situations when Thalassa
has to send an email: a message with a confirmation code to let the user
prove his/her ownership over the email address (both as a part of signup
and email change — these cases use the same email composition
procedure) and a letter containing new single-use passwords. These two
cases are identified as confirm
and passwords
.
As of present, there are no more cases when Thalassa sends “service”
(that is, composed by Thalassa itself) email messages.
Thalassa supports the contact form which is also able to send emails, but in that case email text and subject are supplied by the user and there's a fixed pre-configured list of addresses such a message can be sent to.
Thalassa CGI program sends emails by launching an external command
(presumably /usr/sbin/sendmail
, but both the name of the
command and its arguments are fully configurable) and supplying the message
(header and body) to its standard input stream.
The [servicemail]
ini file section is used to configure what
exactly is to be sent and how. There are four parameters in the
section:
send_command
is a command line (name and arguments) for
the external program to launch to send the message; arguments are split down
to words, using the apostrophe “'
” and the doublequote
“"
” as grouping symbols (both an apostrophe within
doublequotes and a doublequote within apostrophes are considered as plain
chars);header
is a template to build the email header;body
is a template to build the message body;subject
is a template for message subject (the value for
the %subject%
macro).For all four parameters, either confirm
or
passwords
is always applied as the
specifier, so you can
configure different values for different cases.
The following context-specific macros are expanded within the values of
the send_command
, header
and body
parameters:
%receiver%
is the email address to which the message is to
be sent;%subject%
is the subject string, that is, the
subject
parameter's value;%passwords%
is the list of fresh passwords
(passwords
case only);%confirmcode%
is the random confirmation code
(confirm
case only);%event%
is either signup
,
changemail
or passwords
.The following can serve as an example of the [servicemail]
section:
[servicemail] send_command = /usr/sbin/sendmail -bm -i + -f thalassa_master@example.com '%[receiver]' header = From: thalassa_master@example.com +To: %[receiver] +Subject: %[subject] +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: 8Bit +X-Sender-Software: Thalassa CGI script +X-Client: %[getenv:REMOTE_ADDR]:%[getenv:REMOTE_PORT] + subject:passwords = access to www.example.com body:passwords = Your new single-use passwords: +%[passwords] +See you on the site! + subject:confirm = www.example.com: email address confirmation body:confirm = +Someone (most probably you) entered %[receiver] at the site +www.example.com as his/her email address. If it weren't you, +please simply ignore this message. No more messages will be +sent to your address. To confirm you really own this address, +please enter the following code on the site: + +%[confirmcode] +