Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
IRMA
Github mirrors
irmago
Commits
eb225204
Commit
eb225204
authored
Feb 14, 2020
by
David Venhoek
Committed by
Sietse Ringers
Dec 11, 2020
Browse files
Added email verification generation to the registration process.
parent
c7f07e06
Changes
8
Hide whitespace changes
Inline
Side-by-side
server/email.go
0 → 100644
View file @
eb225204
package
server
import
"net/smtp"
func
SendHTMLMail
(
addr
string
,
a
smtp
.
Auth
,
from
,
to
,
subject
string
,
msg
[]
byte
)
error
{
headers
:=
[]
byte
(
"To: "
+
to
+
"
\r\n
"
+
"From: "
+
from
+
"
\r\n
"
+
"Subject: "
+
subject
+
"
\r\n
"
+
"Content-Type: text/html; charset=UTF-8
\r\n
"
+
"Content-Transfer-Encoding: binary
\r\n
"
+
"
\r\n
"
)
return
smtp
.
SendMail
(
addr
,
a
,
from
,
[]
string
{
to
},
append
(
headers
,
msg
...
))
}
server/keyshareserver/cleanup.sql
0 → 100644
View file @
eb225204
DROP
SCHEMA
irma
CASCADE
;
\ No newline at end of file
server/keyshareserver/conf.go
View file @
eb225204
...
...
@@ -2,7 +2,9 @@ package keyshareserver
import
(
"encoding/binary"
"html/template"
"io/ioutil"
"net/smtp"
"os"
"strings"
...
...
@@ -68,6 +70,16 @@ type Configuration struct {
KeyshareCredential
string
KeyshareAttribute
string
// Configuration for email sending during registration (email address use will be disabled if not present)
EmailServer
string
EmailAuth
smtp
.
Auth
EmailFrom
string
RegistrationEmailFiles
map
[
string
]
string
RegistrationEmailTemplates
map
[
string
]
*
template
.
Template
RegistrationEmailSubject
map
[
string
]
string
VerificationURL
map
[
string
]
string
DefaultLanguage
string
// Logging verbosity level: 0 is normal, 1 includes DEBUG level, 2 includes TRACE level
Verbose
int
`json:"verbose" mapstructure:"verbose"`
// Don't log anything at all
...
...
@@ -128,6 +140,31 @@ func processConfiguration(conf *Configuration) (*keysharecore.KeyshareCore, erro
// Force production status to match
conf
.
ServerConfiguration
.
Production
=
conf
.
Production
// Setup email templates
if
conf
.
EmailServer
!=
""
&&
conf
.
RegistrationEmailTemplates
==
nil
{
conf
.
RegistrationEmailTemplates
=
map
[
string
]
*
template
.
Template
{}
for
lang
,
templateFile
:=
range
conf
.
RegistrationEmailFiles
{
var
err
error
conf
.
RegistrationEmailTemplates
[
lang
],
err
=
template
.
ParseFiles
(
templateFile
)
if
err
!=
nil
{
return
nil
,
server
.
LogError
(
err
)
}
}
}
// Verify email configuration
if
conf
.
EmailServer
!=
""
{
if
_
,
ok
:=
conf
.
RegistrationEmailTemplates
[
conf
.
DefaultLanguage
];
!
ok
{
return
nil
,
server
.
LogError
(
errors
.
Errorf
(
"Missing registration email template for default language"
))
}
if
_
,
ok
:=
conf
.
RegistrationEmailSubject
[
conf
.
DefaultLanguage
];
!
ok
{
return
nil
,
server
.
LogError
(
errors
.
Errorf
(
"Missing registration email subject for default language"
))
}
if
_
,
ok
:=
conf
.
VerificationURL
[
conf
.
DefaultLanguage
];
!
ok
{
return
nil
,
server
.
LogError
(
errors
.
Errorf
(
"Missing verification base url for default lanaguage"
))
}
}
// Load configuration (because server setup needs this to be in place)
if
conf
.
ServerConfiguration
.
IrmaConfiguration
==
nil
{
var
(
...
...
server/keyshareserver/db.go
View file @
eb225204
...
...
@@ -30,7 +30,7 @@ const (
)
type
KeyshareDB
interface
{
NewUser
(
user
KeyshareUserData
)
error
NewUser
(
user
KeyshareUserData
)
(
KeyshareUser
,
error
)
User
(
username
string
)
(
KeyshareUser
,
error
)
UpdateUser
(
user
KeyshareUser
)
error
...
...
@@ -40,6 +40,8 @@ type KeyshareDB interface {
SetSeen
(
user
KeyshareUser
)
error
AddLog
(
user
KeyshareUser
,
eventType
LogEntryType
,
param
interface
{})
error
AddEmailVerification
(
user
KeyshareUser
,
emailAddress
,
token
string
)
error
}
type
KeyshareUser
interface
{
...
...
@@ -81,7 +83,7 @@ func (db *keyshareMemoryDB) User(username string) (KeyshareUser, error) {
return
&
keyshareMemoryUser
{
KeyshareUserData
{
Username
:
username
,
Coredata
:
data
}},
nil
}
func
(
db
*
keyshareMemoryDB
)
NewUser
(
user
KeyshareUserData
)
error
{
func
(
db
*
keyshareMemoryDB
)
NewUser
(
user
KeyshareUserData
)
(
KeyshareUser
,
error
)
{
// Ensure access to database is single-threaded
db
.
lock
.
Lock
()
defer
db
.
lock
.
Unlock
()
...
...
@@ -89,10 +91,10 @@ func (db *keyshareMemoryDB) NewUser(user KeyshareUserData) error {
// Check and insert user
_
,
exists
:=
db
.
users
[
user
.
Username
]
if
exists
{
return
ErrUserAlreadyExists
return
nil
,
ErrUserAlreadyExists
}
db
.
users
[
user
.
Username
]
=
user
.
Coredata
return
nil
return
&
keyshareMemoryUser
{
KeyshareUserData
:
user
},
nil
}
func
(
db
*
keyshareMemoryDB
)
UpdateUser
(
user
KeyshareUser
)
error
{
...
...
@@ -132,13 +134,17 @@ func (db *keyshareMemoryDB) AddLog(user KeyshareUser, eventType LogEntryType, pa
return
nil
}
func
(
db
*
keyshareMemoryDB
)
AddEmailVerification
(
user
KeyshareUser
,
emailAddress
,
token
string
)
error
{
return
nil
}
type
keysharePostgresDatabase
struct
{
db
*
sql
.
DB
}
type
keysharePostgresUser
struct
{
KeyshareUserData
id
int
id
int
64
}
func
(
m
*
keysharePostgresUser
)
Data
()
*
KeyshareUserData
{
...
...
@@ -158,19 +164,24 @@ func NewPostgresDatabase(connstring string) (KeyshareDB, error) {
},
nil
}
func
(
db
*
keysharePostgresDatabase
)
NewUser
(
user
KeyshareUserData
)
error
{
res
,
err
:=
db
.
db
.
Exec
(
"INSERT INTO irma.users (username, coredata, pinCounter, pinBlockDate) VALUES ($1, $2, 0, 0);"
,
user
.
Username
,
user
.
Coredata
[
:
])
func
(
db
*
keysharePostgresDatabase
)
NewUser
(
user
KeyshareUserData
)
(
KeyshareUser
,
error
)
{
res
,
err
:=
db
.
db
.
Query
(
"INSERT INTO irma.users (username, coredata, lastSeen, pinCounter, pinBlockDate) VALUES ($1, $2, $3, 0, 0) RETURNING id"
,
user
.
Username
,
user
.
Coredata
[
:
],
time
.
Now
()
.
Unix
())
if
err
!=
nil
{
return
err
return
nil
,
err
}
c
,
err
:=
res
.
RowsAffected
()
if
err
!=
nil
{
return
err
defer
res
.
Close
()
if
!
res
.
Next
()
{
return
nil
,
ErrUserAlreadyExists
}
if
c
==
0
{
return
ErrUserAlreadyExists
var
id
int64
err
=
res
.
Scan
(
&
id
)
if
err
!=
nil
{
return
nil
,
err
}
return
nil
return
&
keysharePostgresUser
{
KeyshareUserData
:
user
,
id
:
id
},
nil
}
func
(
db
*
keysharePostgresDatabase
)
User
(
username
string
)
(
KeyshareUser
,
error
)
{
...
...
@@ -335,3 +346,16 @@ func (db *keysharePostgresDatabase) AddLog(user KeyshareUser, eventType LogEntry
userdata
.
id
)
return
err
}
func
(
db
*
keysharePostgresDatabase
)
AddEmailVerification
(
user
KeyshareUser
,
emailAddress
,
token
string
)
error
{
userdata
,
ok
:=
user
.
(
*
keysharePostgresUser
)
if
!
ok
{
return
ErrInvalidData
}
_
,
err
:=
db
.
db
.
Exec
(
"INSERT INTO irma.email_verification_tokens (token, email, user_id) VALUES ($1, $2, $3)"
,
token
,
emailAddress
,
userdata
.
id
)
return
err
}
server/keyshareserver/schema.sql
View file @
eb225204
CREATE
SCHEMA
irma
;
CREATE
TABLE
IF
NOT
EXISTS
irma
.
users
(
id
serial
PRIMARY
KEY
,
username
varchar
(
128
)
,
coredata
bytea
,
lastSeen
bigint
,
pinCounter
int
,
pinBlockDate
bigint
username
text
NOT
NULL
,
coredata
bytea
NOT
NULL
,
lastSeen
bigint
NOT
NULL
,
pinCounter
int
NOT
NULL
,
pinBlockDate
bigint
NOT
NULL
);
CREATE
UNIQUE
INDEX
username_index
ON
irma
.
users
(
username
);
GRANT
ALL
PRIVILEGES
ON
TABLE
irma
.
users
TO
irma
;
CREATE
TABLE
IF
NOT
EXISTS
irma
.
log_entry_records
(
id
serial
PRIMARY
KEY
,
time
bigint
,
event
varchar
(
256
)
,
time
bigint
NOT
NULL
,
event
text
NOT
NULL
,
param
text
,
user_id
int
user_id
int
NOT
NULL
);
CREATE
INDEX
log_entry_records_user_id_index
ON
irma
.
log_entry_records
(
user_id
,
time
);
CREATE
TABLE
IF
NOT
EXISTS
irma
.
email_verification_tokens
(
id
serial
PRIMARY
KEY
,
token
text
NOT
NULL
,
email
text
NOT
NULL
,
user_id
int
NOT
NULL
);
CREATE
UNIQUE
INDEX
email_verification_token_index
ON
irma
.
email_verification_tokens
(
token
);
CREATE
TABLE
IF
NOT
EXISTS
irma
.
email_addresses
(
id
serial
PRIMARY
KEY
,
user_id
int
NOT
NULL
,
emailAddress
text
NOT
NULL
);
CREATE
INDEX
log_entry_records_user_id_index
ON
irma
.
log_entry_records
(
user_id
);
GRANT
ALL
PRIVILEGES
ON
TABLE
irma
.
log_entry_records
TO
irma
;
\ No newline at end of file
CREATE
INDEX
emailAddress_index
ON
irma
.
email_addresses
(
emailAddress
);
CREATE
INDEX
emailAddress_userid_index
ON
irma
.
email_addresses
(
user_id
);
server/keyshareserver/server.go
View file @
eb225204
package
keyshareserver
import
(
"bytes"
"crypto/rand"
"encoding/base32"
"encoding/base64"
"encoding/json"
"fmt"
...
...
@@ -448,13 +450,66 @@ func (s *Server) handleRegister(w http.ResponseWriter, r *http.Request) {
server
.
WriteError
(
w
,
server
.
ErrorInvalidRequest
,
err
.
Error
())
return
}
err
=
s
.
db
.
NewUser
(
KeyshareUserData
{
Username
:
username
,
Coredata
:
coredata
})
user
,
err
:
=
s
.
db
.
NewUser
(
KeyshareUserData
{
Username
:
username
,
Coredata
:
coredata
})
if
err
!=
nil
{
s
.
conf
.
Logger
.
WithField
(
"error"
,
err
)
.
Error
(
"Could not store new user in database"
)
server
.
WriteError
(
w
,
server
.
ErrorInternal
,
err
.
Error
())
return
}
// Send email if user specified email address
if
msg
.
Email
!=
nil
&&
s
.
conf
.
EmailServer
!=
""
{
// Fetch template and configuration data for users language, falling back if needed
template
,
ok
:=
s
.
conf
.
RegistrationEmailTemplates
[
msg
.
Language
]
if
!
ok
{
template
=
s
.
conf
.
RegistrationEmailTemplates
[
s
.
conf
.
DefaultLanguage
]
}
verificationBaseURL
,
ok
:=
s
.
conf
.
VerificationURL
[
msg
.
Language
]
if
!
ok
{
verificationBaseURL
=
s
.
conf
.
VerificationURL
[
s
.
conf
.
DefaultLanguage
]
}
subject
,
ok
:=
s
.
conf
.
RegistrationEmailSubject
[
msg
.
Language
]
if
!
ok
{
subject
=
s
.
conf
.
RegistrationEmailSubject
[
s
.
conf
.
DefaultLanguage
]
}
// Generate token
tokenData
:=
make
([]
byte
,
35
)
_
,
err
=
rand
.
Read
(
tokenData
)
if
err
!=
nil
{
s
.
conf
.
Logger
.
WithField
(
"error"
,
err
)
.
Error
(
"Could not generate email verification token"
)
server
.
WriteError
(
w
,
server
.
ErrorInternal
,
err
.
Error
())
return
}
token
:=
base32
.
StdEncoding
.
EncodeToString
(
tokenData
)
// Add it to the database
err
=
s
.
db
.
AddEmailVerification
(
user
,
*
msg
.
Email
,
token
)
if
err
!=
nil
{
s
.
conf
.
Logger
.
WithField
(
"error"
,
err
)
.
Error
(
"Could not add email verification record to user"
)
server
.
WriteError
(
w
,
server
.
ErrorInternal
,
err
.
Error
())
return
}
// Build message
var
emsg
bytes
.
Buffer
err
=
template
.
Execute
(
&
emsg
,
map
[
string
]
string
{
"VerificationURL"
:
verificationBaseURL
+
token
})
if
err
!=
nil
{
s
.
conf
.
Logger
.
WithField
(
"error"
,
err
)
.
Error
(
"Could not generate email verifcation mail"
)
server
.
WriteError
(
w
,
server
.
ErrorInternal
,
err
.
Error
())
return
}
// And send it
err
=
server
.
SendHTMLMail
(
s
.
conf
.
EmailServer
,
s
.
conf
.
EmailAuth
,
s
.
conf
.
EmailFrom
,
*
msg
.
Email
,
subject
,
emsg
.
Bytes
())
}
// Setup and return issuance session for keyshare credential.
request
:=
irma
.
NewIssuanceRequest
([]
*
irma
.
CredentialRequest
{
{
...
...
server/keyshareserver/tmpmain/main.go
View file @
eb225204
...
...
@@ -17,6 +17,18 @@ func main() {
StoragePrimaryKeyFile
:
"storagekey"
,
KeyshareCredential
:
"test.test.mijnirma"
,
KeyshareAttribute
:
"email"
,
RegistrationEmailSubject
:
map
[
string
]
string
{
"en"
:
"Test"
,
},
RegistrationEmailFiles
:
map
[
string
]
string
{
"en"
:
"registration.html"
,
},
DefaultLanguage
:
"en"
,
VerificationURL
:
map
[
string
]
string
{
"en"
:
"http://example.com/verify/"
,
},
EmailServer
:
"localhost:1025"
,
EmailFrom
:
"test@example.com"
,
})
if
err
!=
nil
{
...
...
server/keyshareserver/tmpmain/registration.html
0 → 100644
View file @
eb225204
<p>
Welcome to irma
</p>
<p><a
href=
"{{.VerificationURL}}"
>
Click here to verify your account
</a>
or paste the following in your browser: {{.VerificationURL}}
</p>
\ No newline at end of file
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment