3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-25 13:29:27 +01:00

accounts: Add very initial, extremely broken account work (not including config changes)

This commit is contained in:
Daniel Oaks 2016-09-04 19:25:33 +10:00
parent 1746be2bb8
commit e4b6c1852b
7 changed files with 339 additions and 177 deletions

16
irc/accounts.go Normal file
View File

@ -0,0 +1,16 @@
// Copyright (c) 2016- Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license
package irc
import "time"
// Account represents a user account.
type Account struct {
// Name of the account.
Name string
// RegisteredAt represents the time that the account was registered.
RegisteredAt time.Time
// Clients that are currently logged into this account (useful for notifications).
Clients []Client
}

View File

@ -152,6 +152,10 @@ var Commands = map[string]Command{
usablePreReg: true, usablePreReg: true,
minParams: 0, minParams: 0,
}, },
"REG": {
handler: regHandler,
minParams: 3,
},
/*TODO(dan): Add this back in /*TODO(dan): Add this back in
"THEATRE": Command{ "THEATRE": Command{
handler: theatreHandler, handler: theatreHandler,

View File

@ -44,16 +44,32 @@ func (conf *PassConfig) PasswordBytes() []byte {
return bytes return bytes
} }
type AccountRegistrationConfig struct {
Enabled bool
EnabledCallbacks []string `yaml:"enabled-callbacks"`
Callbacks struct {
Mailto struct {
Server string
Port int
TLS struct {
Enabled bool
InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
ServerName string `yaml:"servername"`
}
Username string
Password string
Sender string
VerifyMessageSubject string `yaml:"verify-message-subject"`
VerifyMessage string `yaml:"verify-message"`
}
}
}
type Config struct { type Config struct {
Network struct { Network struct {
Name string Name string
} }
Datastore struct {
Path string
SQLitePath string `yaml:"sqlite-path"`
}
Server struct { Server struct {
PassConfig PassConfig
Password string Password string
@ -67,6 +83,15 @@ type Config struct {
ProxyAllowedFrom []string `yaml:"proxy-allowed-from"` ProxyAllowedFrom []string `yaml:"proxy-allowed-from"`
} }
Datastore struct {
Path string
SQLitePath string `yaml:"sqlite-path"`
}
Registration struct {
Accounts AccountRegistrationConfig
}
Operator map[string]*PassConfig Operator map[string]*PassConfig
Theater map[string]*PassConfig Theater map[string]*PassConfig

View File

@ -117,6 +117,7 @@ const (
ERR_NICKNAMEINUSE = "433" ERR_NICKNAMEINUSE = "433"
ERR_NICKCOLLISION = "436" ERR_NICKCOLLISION = "436"
ERR_UNAVAILRESOURCE = "437" ERR_UNAVAILRESOURCE = "437"
ERR_REG_UNAVAILABLE = "440"
ERR_USERNOTINCHANNEL = "441" ERR_USERNOTINCHANNEL = "441"
ERR_NOTONCHANNEL = "442" ERR_NOTONCHANNEL = "442"
ERR_USERONCHANNEL = "443" ERR_USERONCHANNEL = "443"
@ -147,4 +148,13 @@ const (
ERR_NOOPERHOST = "491" ERR_NOOPERHOST = "491"
ERR_UMODEUNKNOWNFLAG = "501" ERR_UMODEUNKNOWNFLAG = "501"
ERR_USERSDONTMATCH = "502" ERR_USERSDONTMATCH = "502"
RPL_REGISTRATION_SUCCESS = "920"
ERR_ACCOUNT_ALREADY_EXISTS = "921"
ERR_REG_UNSPECIFIED_ERROR = "922"
RPL_VERIFYSUCCESS = "923"
ERR_ACCOUNT_ALREADY_VERIFIED = "924"
ERR_ACCOUNT_INVALID_VERIFY_CODE = "925"
RPL_REG_VERIFICATION_REQUIRED = "927"
ERR_REG_INVALID_CALLBACK = "929"
ERR_REG_INVALID_CRED_TYPE = "982"
) )

84
irc/registration.go Normal file
View File

@ -0,0 +1,84 @@
// Copyright (c) 2016- Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license
package irc
import (
"errors"
"fmt"
"strconv"
"strings"
"time"
"github.com/DanielOaks/girc-go/ircmsg"
"github.com/tidwall/buntdb"
)
var (
errAccountCreation = errors.New("Account could not be created")
)
// AccountRegistration manages the registration of accounts.
type AccountRegistration struct {
Enabled bool
EnabledRegistrationCallbackTypes []string
}
// NewAccountRegistration returns a new AccountRegistration, configured correctly.
func NewAccountRegistration(config AccountRegistrationConfig) (accountReg AccountRegistration) {
if config.Enabled {
accountReg.Enabled = true
accountReg.EnabledRegistrationCallbackTypes = config.EnabledCallbacks
}
return accountReg
}
// regHandler parses the REG command.
func regHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
subcommand := strings.ToLower(msg.Params[0])
if subcommand == "create" {
client.Notice("Parsing CREATE")
// get and sanitise account name
account := NewName(msg.Params[1])
if !account.IsNickname() || msg.Params[1] == "*" {
client.Send(nil, server.nameString, ERR_REG_UNSPECIFIED_ERROR, client.nickString, msg.Params[1], "Account name is not valid")
return false
}
accountString := account.String()
// check whether account exists
// do it all in one write tx to prevent races
err := server.store.Update(func(tx *buntdb.Tx) error {
accountKey := fmt.Sprintf("account %s exists", accountString)
_, err := tx.Get(accountKey)
if err != buntdb.ErrNotFound {
//TODO(dan): if account verified key doesn't exist account is not verified, calc the maximum time without verification and expire and continue if need be
client.Send(nil, server.nameString, ERR_ACCOUNT_ALREADY_EXISTS, client.nickString, msg.Params[1], "Account already exists")
return errAccountCreation
}
registeredTimeKey := fmt.Sprintf("account %s registered.time", accountString)
tx.Set(accountKey, "1", nil)
tx.Set(registeredTimeKey, strconv.FormatInt(time.Now().Unix(), 10), nil)
return nil
})
// account could not be created and relevant numerics have been dispatched, abort
if err != nil {
return false
}
// account didn't already exist, continue with account creation and dispatching verification (if required)
} else if subcommand == "verify" {
client.Notice("Parsing VERIFY")
} else {
client.Send(nil, server.nameString, ERR_UNKNOWNERROR, client.nickString, "REG", msg.Params[0], "Unknown subcommand")
}
return false
}

View File

@ -26,6 +26,7 @@ import (
) )
type Server struct { type Server struct {
accounts map[string]Account
channels ChannelNameMap channels ChannelNameMap
clients *ClientLookupSet clients *ClientLookupSet
commands chan Command commands chan Command
@ -39,6 +40,7 @@ type Server struct {
newConns chan clientConn newConns chan clientConn
operators map[Name][]byte operators map[Name][]byte
password []byte password []byte
accountRegistration *AccountRegistration
signals chan os.Signal signals chan os.Signal
proxyAllowedFrom []string proxyAllowedFrom []string
whoWas *WhoWasList whoWas *WhoWasList
@ -63,6 +65,7 @@ type clientConn struct {
func NewServer(config *Config) *Server { func NewServer(config *Config) *Server {
server := &Server{ server := &Server{
accounts: make(map[string]Account),
channels: make(ChannelNameMap), channels: make(ChannelNameMap),
clients: NewClientLookupSet(), clients: NewClientLookupSet(),
commands: make(chan Command), commands: make(chan Command),
@ -127,6 +130,10 @@ func NewServer(config *Config) *Server {
server.wslisten(config.Server.Wslisten, config.Server.TLSListeners) server.wslisten(config.Server.Wslisten, config.Server.TLSListeners)
} }
// registration
accountReg := NewAccountRegistration(config.Registration.Accounts)
server.accountRegistration = &accountReg
// Attempt to clean up when receiving these signals. // Attempt to clean up when receiving these signals.
signal.Notify(server.signals, SERVER_SIGNALS...) signal.Notify(server.signals, SERVER_SIGNALS...)
@ -144,9 +151,25 @@ func NewServer(config *Config) *Server {
server.isupport.Add("NETWORK", config.Network.Name) server.isupport.Add("NETWORK", config.Network.Name)
server.isupport.Add("NICKLEN", strconv.Itoa(config.Limits.NickLen)) server.isupport.Add("NICKLEN", strconv.Itoa(config.Limits.NickLen))
server.isupport.Add("PREFIX", "(qaohv)~&@%+") server.isupport.Add("PREFIX", "(qaohv)~&@%+")
// server.isupport.Add("STATUSMSG", "@+") //TODO(dan): Autogenerate based on PREFIXes, support STATUSMSG // server.isupport.Add("STATUSMSG", "@+") //TODO(dan): Support STATUSMSG
// server.isupport.Add("TARGMAX", "") //TODO(dan): Support this // server.isupport.Add("TARGMAX", "") //TODO(dan): Support this
// server.isupport.Add("TOPICLEN", "") //TODO(dan): Support topic length // server.isupport.Add("TOPICLEN", "") //TODO(dan): Support topic length
// account registration
if server.accountRegistration.Enabled {
// 'none' isn't shown in the REGCALLBACKS vars
var enabledCallbackTypes []string
for _, name := range server.accountRegistration.EnabledRegistrationCallbackTypes {
if name != "none" {
enabledCallbackTypes = append(enabledCallbackTypes, name)
}
}
server.isupport.Add("REGCOMMANDS", "CREATE,VERIFY")
server.isupport.Add("REGCALLBACKS", strings.Join(enabledCallbackTypes, ","))
server.isupport.Add("REGCREDTYPES", "passphrase,certfp")
}
server.isupport.RegenerateCachedReply() server.isupport.RegenerateCachedReply()
return server return server

View File

@ -5,17 +5,6 @@ network:
# name of the network # name of the network
name: OragonoTest name: OragonoTest
# datastore configuration
datastore:
# path to the datastore
# this can also be ":memory:" for an in-memory-only db
path: ircd.db
# path to our sqlite db
# currently used to lookup masks and store persistent chan data
# but planned to be deprecated in a future release
sqlite-path: ircd-sqlite.db
# server configuration # server configuration
server: server:
# server name # server name
@ -65,6 +54,17 @@ operator:
# generated using "oragono genpasswd" # generated using "oragono genpasswd"
password: JDJhJDA0JE1vZmwxZC9YTXBhZ3RWT2xBbkNwZnV3R2N6VFUwQUI0RUJRVXRBRHliZVVoa0VYMnlIaGsu password: JDJhJDA0JE1vZmwxZC9YTXBhZ3RWT2xBbkNwZnV3R2N6VFUwQUI0RUJRVXRBRHliZVVoa0VYMnlIaGsu
# datastore configuration
datastore:
# path to the datastore
# this can also be ":memory:" for an in-memory-only db
path: ircd.db
# path to our sqlite db
# currently used to lookup masks and store persistent chan data
# but planned to be deprecated in a future release
sqlite-path: ircd-sqlite.db
# limits - these need to be the same across the network # limits - these need to be the same across the network
limits: limits:
# nicklen is the max nick length allowed # nicklen is the max nick length allowed