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
...
@@ -2,7 +2,9 @@ package keyshareserver
import
(
import
(
"encoding/binary"
"encoding/binary"
"html/template"
"io/ioutil"
"io/ioutil"
"net/smtp"
"os"
"os"
"strings"
"strings"
...
@@ -68,6 +70,16 @@ type Configuration struct {
...
@@ -68,6 +70,16 @@ type Configuration struct {
KeyshareCredential
string
KeyshareCredential
string
KeyshareAttribute
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
// Logging verbosity level: 0 is normal, 1 includes DEBUG level, 2 includes TRACE level
Verbose
int
`json:"verbose" mapstructure:"verbose"`
Verbose
int
`json:"verbose" mapstructure:"verbose"`
// Don't log anything at all
// Don't log anything at all
...
@@ -128,6 +140,31 @@ func processConfiguration(conf *Configuration) (*keysharecore.KeyshareCore, erro
...
@@ -128,6 +140,31 @@ func processConfiguration(conf *Configuration) (*keysharecore.KeyshareCore, erro
// Force production status to match
// Force production status to match
conf
.
ServerConfiguration
.
Production
=
conf
.
Production
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)
// Load configuration (because server setup needs this to be in place)
if
conf
.
ServerConfiguration
.
IrmaConfiguration
==
nil
{
if
conf
.
ServerConfiguration
.
IrmaConfiguration
==
nil
{
var
(
var
(
...
...
server/keyshareserver/db.go
View file @
eb225204
...
@@ -30,7 +30,7 @@ const (
...
@@ -30,7 +30,7 @@ const (
)
)
type
KeyshareDB
interface
{
type
KeyshareDB
interface
{
NewUser
(
user
KeyshareUserData
)
error
NewUser
(
user
KeyshareUserData
)
(
KeyshareUser
,
error
)
User
(
username
string
)
(
KeyshareUser
,
error
)
User
(
username
string
)
(
KeyshareUser
,
error
)
UpdateUser
(
user
KeyshareUser
)
error
UpdateUser
(
user
KeyshareUser
)
error
...
@@ -40,6 +40,8 @@ type KeyshareDB interface {
...
@@ -40,6 +40,8 @@ type KeyshareDB interface {
SetSeen
(
user
KeyshareUser
)
error
SetSeen
(
user
KeyshareUser
)
error
AddLog
(
user
KeyshareUser
,
eventType
LogEntryType
,
param
interface
{})
error
AddLog
(
user
KeyshareUser
,
eventType
LogEntryType
,
param
interface
{})
error
AddEmailVerification
(
user
KeyshareUser
,
emailAddress
,
token
string
)
error
}
}
type
KeyshareUser
interface
{
type
KeyshareUser
interface
{
...
@@ -81,7 +83,7 @@ func (db *keyshareMemoryDB) User(username string) (KeyshareUser, error) {
...
@@ -81,7 +83,7 @@ func (db *keyshareMemoryDB) User(username string) (KeyshareUser, error) {
return
&
keyshareMemoryUser
{
KeyshareUserData
{
Username
:
username
,
Coredata
:
data
}},
nil
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
// Ensure access to database is single-threaded
db
.
lock
.
Lock
()
db
.
lock
.
Lock
()
defer
db
.
lock
.
Unlock
()
defer
db
.
lock
.
Unlock
()
...
@@ -89,10 +91,10 @@ func (db *keyshareMemoryDB) NewUser(user KeyshareUserData) error {
...
@@ -89,10 +91,10 @@ func (db *keyshareMemoryDB) NewUser(user KeyshareUserData) error {
// Check and insert user
// Check and insert user
_
,
exists
:=
db
.
users
[
user
.
Username
]
_
,
exists
:=
db
.
users
[
user
.
Username
]
if
exists
{
if
exists
{
return
ErrUserAlreadyExists
return
nil
,
ErrUserAlreadyExists
}
}
db
.
users
[
user
.
Username
]
=
user
.
Coredata
db
.
users
[
user
.
Username
]
=
user
.
Coredata
return
nil
return
&
keyshareMemoryUser
{
KeyshareUserData
:
user
},
nil
}
}
func
(
db
*
keyshareMemoryDB
)
UpdateUser
(
user
KeyshareUser
)
error
{
func
(
db
*
keyshareMemoryDB
)
UpdateUser
(
user
KeyshareUser
)
error
{
...
@@ -132,13 +134,17 @@ func (db *keyshareMemoryDB) AddLog(user KeyshareUser, eventType LogEntryType, pa
...
@@ -132,13 +134,17 @@ func (db *keyshareMemoryDB) AddLog(user KeyshareUser, eventType LogEntryType, pa
return
nil
return
nil
}
}
func
(
db
*
keyshareMemoryDB
)
AddEmailVerification
(
user
KeyshareUser
,
emailAddress
,
token
string
)
error
{
return
nil
}
type
keysharePostgresDatabase
struct
{
type
keysharePostgresDatabase
struct
{
db
*
sql
.
DB
db
*
sql
.
DB
}
}
type
keysharePostgresUser
struct
{
type
keysharePostgresUser
struct
{
KeyshareUserData
KeyshareUserData
id
int
id
int
64
}
}
func
(
m
*
keysharePostgresUser
)
Data
()
*
KeyshareUserData
{
func
(
m
*
keysharePostgresUser
)
Data
()
*
KeyshareUserData
{
...
@@ -158,19 +164,24 @@ func NewPostgresDatabase(connstring string) (KeyshareDB, error) {
...
@@ -158,19 +164,24 @@ func NewPostgresDatabase(connstring string) (KeyshareDB, error) {
},
nil
},
nil
}
}
func
(
db
*
keysharePostgresDatabase
)
NewUser
(
user
KeyshareUserData
)
error
{
func
(
db
*
keysharePostgresDatabase
)
NewUser
(
user
KeyshareUserData
)
(
KeyshareUser
,
error
)
{
res
,
err
:=
db
.
db
.
Exec
(
"INSERT INTO irma.users (username, coredata, pinCounter, pinBlockDate) VALUES ($1, $2, 0, 0);"
,
user
.
Username
,
user
.
Coredata
[
:
])
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
{
if
err
!=
nil
{
return
err
return
nil
,
err
}
}
c
,
err
:=
res
.
RowsAffected
()
defer
res
.
Close
()
if
err
!=
nil
{
if
!
res
.
Next
()
{
return
err
return
nil
,
ErrUserAlreadyExists
}
}
if
c
==
0
{
var
id
int64
return
ErrUserAlreadyExists
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
)
{
func
(
db
*
keysharePostgresDatabase
)
User
(
username
string
)
(
KeyshareUser
,
error
)
{
...
@@ -335,3 +346,16 @@ func (db *keysharePostgresDatabase) AddLog(user KeyshareUser, eventType LogEntry
...
@@ -335,3 +346,16 @@ func (db *keysharePostgresDatabase) AddLog(user KeyshareUser, eventType LogEntry
userdata
.
id
)
userdata
.
id
)
return
err
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
CREATE
TABLE
IF
NOT
EXISTS
irma
.
users
(
(
id
serial
PRIMARY
KEY
,
id
serial
PRIMARY
KEY
,
username
varchar
(
128
)
,
username
text
NOT
NULL
,
coredata
bytea
,
coredata
bytea
NOT
NULL
,
lastSeen
bigint
,
lastSeen
bigint
NOT
NULL
,
pinCounter
int
,
pinCounter
int
NOT
NULL
,
pinBlockDate
bigint
pinBlockDate
bigint
NOT
NULL
);
);
CREATE
UNIQUE
INDEX
username_index
ON
irma
.
users
(
username
);
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
CREATE
TABLE
IF
NOT
EXISTS
irma
.
log_entry_records
(
(
id
serial
PRIMARY
KEY
,
id
serial
PRIMARY
KEY
,
time
bigint
,
time
bigint
NOT
NULL
,
event
varchar
(
256
)
,
event
text
NOT
NULL
,
param
text
,
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
);
CREATE
INDEX
emailAddress_index
ON
irma
.
email_addresses
(
emailAddress
);
GRANT
ALL
PRIVILEGES
ON
TABLE
irma
.
log_entry_records
TO
irma
;
CREATE
INDEX
emailAddress_userid_index
ON
irma
.
email_addresses
(
user_id
);
\ No newline at end of file
server/keyshareserver/server.go
View file @
eb225204
package
keyshareserver
package
keyshareserver
import
(
import
(
"bytes"
"crypto/rand"
"crypto/rand"
"encoding/base32"
"encoding/base64"
"encoding/base64"
"encoding/json"
"encoding/json"
"fmt"
"fmt"
...
@@ -448,13 +450,66 @@ func (s *Server) handleRegister(w http.ResponseWriter, r *http.Request) {
...
@@ -448,13 +450,66 @@ func (s *Server) handleRegister(w http.ResponseWriter, r *http.Request) {
server
.
WriteError
(
w
,
server
.
ErrorInvalidRequest
,
err
.
Error
())
server
.
WriteError
(
w
,
server
.
ErrorInvalidRequest
,
err
.
Error
())
return
return
}
}
err
=
s
.
db
.
NewUser
(
KeyshareUserData
{
Username
:
username
,
Coredata
:
coredata
})
user
,
err
:
=
s
.
db
.
NewUser
(
KeyshareUserData
{
Username
:
username
,
Coredata
:
coredata
})
if
err
!=
nil
{
if
err
!=
nil
{
s
.
conf
.
Logger
.
WithField
(
"error"
,
err
)
.
Error
(
"Could not store new user in database"
)
s
.
conf
.
Logger
.
WithField
(
"error"
,
err
)
.
Error
(
"Could not store new user in database"
)
server
.
WriteError
(
w
,
server
.
ErrorInternal
,
err
.
Error
())
server
.
WriteError
(
w
,
server
.
ErrorInternal
,
err
.
Error
())
return
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.
// Setup and return issuance session for keyshare credential.
request
:=
irma
.
NewIssuanceRequest
([]
*
irma
.
CredentialRequest
{
request
:=
irma
.
NewIssuanceRequest
([]
*
irma
.
CredentialRequest
{
{
{
...
...
server/keyshareserver/tmpmain/main.go
View file @
eb225204
...
@@ -17,6 +17,18 @@ func main() {
...
@@ -17,6 +17,18 @@ func main() {
StoragePrimaryKeyFile
:
"storagekey"
,
StoragePrimaryKeyFile
:
"storagekey"
,
KeyshareCredential
:
"test.test.mijnirma"
,
KeyshareCredential
:
"test.test.mijnirma"
,
KeyshareAttribute
:
"email"
,
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
{
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