mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-22 11:59:40 +01:00
Basic EXTJWT support
This commit is contained in:
parent
6ff6225c1e
commit
0bbb5d121d
@ -161,6 +161,13 @@ server:
|
|||||||
# - "192.168.1.1"
|
# - "192.168.1.1"
|
||||||
# - "192.168.10.1/24"
|
# - "192.168.10.1/24"
|
||||||
|
|
||||||
|
# these services can integrate with the ircd using JSON Web Tokens (https://jwt.io)
|
||||||
|
# sometimes referred to with 'EXTJWT'
|
||||||
|
jwt-services:
|
||||||
|
# # service name -> secret string the service uses to verify our tokens
|
||||||
|
# call-host: call-hosting-secret-token
|
||||||
|
# image-host: image-hosting-secret-token
|
||||||
|
|
||||||
# allow use of the RESUME extension over plaintext connections:
|
# allow use of the RESUME extension over plaintext connections:
|
||||||
# do not enable this unless the ircd is only accessible over internal networks
|
# do not enable this unless the ircd is only accessible over internal networks
|
||||||
allow-plaintext-resume: false
|
allow-plaintext-resume: false
|
||||||
|
@ -187,6 +187,13 @@ server:
|
|||||||
# - "192.168.1.1"
|
# - "192.168.1.1"
|
||||||
# - "192.168.10.1/24"
|
# - "192.168.10.1/24"
|
||||||
|
|
||||||
|
# these services can integrate with the ircd using JSON Web Tokens (https://jwt.io)
|
||||||
|
# sometimes referred to with 'EXTJWT'
|
||||||
|
jwt-services:
|
||||||
|
# # service name -> secret string the service uses to verify our tokens
|
||||||
|
# call-host: call-hosting-secret-token
|
||||||
|
# image-host: image-hosting-secret-token
|
||||||
|
|
||||||
# allow use of the RESUME extension over plaintext connections:
|
# allow use of the RESUME extension over plaintext connections:
|
||||||
# do not enable this unless the ircd is only accessible over internal networks
|
# do not enable this unless the ircd is only accessible over internal networks
|
||||||
allow-plaintext-resume: false
|
allow-plaintext-resume: false
|
||||||
|
2
go.mod
2
go.mod
@ -4,11 +4,11 @@ go 1.14
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48
|
code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
||||||
github.com/go-ldap/ldap/v3 v3.1.10
|
github.com/go-ldap/ldap/v3 v3.1.10
|
||||||
github.com/go-sql-driver/mysql v1.5.0
|
github.com/go-sql-driver/mysql v1.5.0
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/goshuirc/e-nfa v0.0.0-20160917075329-7071788e3940 // indirect
|
|
||||||
github.com/goshuirc/irc-go v0.0.0-20200311142257-57fd157327ac
|
github.com/goshuirc/irc-go v0.0.0-20200311142257-57fd157327ac
|
||||||
github.com/onsi/ginkgo v1.12.0 // indirect
|
github.com/onsi/ginkgo v1.12.0 // indirect
|
||||||
github.com/onsi/gomega v1.9.0 // indirect
|
github.com/onsi/gomega v1.9.0 // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -5,6 +5,8 @@ code.cloudfoundry.org/bytefmt v0.0.0-20200131002437-cf55d5288a48/go.mod h1:wN/zk
|
|||||||
github.com/DanielOaks/go-idn v0.0.0-20160120021903-76db0e10dc65/go.mod h1:GYIaL2hleNQvfMUBTes1Zd/lDTyI/p2hv3kYB4jssyU=
|
github.com/DanielOaks/go-idn v0.0.0-20160120021903-76db0e10dc65/go.mod h1:GYIaL2hleNQvfMUBTes1Zd/lDTyI/p2hv3kYB4jssyU=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
|
@ -539,6 +539,17 @@ func (channel *Channel) ClientPrefixes(client *Client, isMultiPrefix bool) strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (channel *Channel) ClientModeStrings(client *Client) []string {
|
||||||
|
channel.stateMutex.RLock()
|
||||||
|
defer channel.stateMutex.RUnlock()
|
||||||
|
modes, present := channel.members[client]
|
||||||
|
if !present {
|
||||||
|
return []string{}
|
||||||
|
} else {
|
||||||
|
return modes.Strings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool {
|
func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool {
|
||||||
channel.stateMutex.RLock()
|
channel.stateMutex.RLock()
|
||||||
founder := channel.registeredFounder
|
founder := channel.registeredFounder
|
||||||
|
@ -130,6 +130,10 @@ func init() {
|
|||||||
minParams: 1,
|
minParams: 1,
|
||||||
oper: true,
|
oper: true,
|
||||||
},
|
},
|
||||||
|
"EXTJWT": {
|
||||||
|
handler: extjwtHandler,
|
||||||
|
minParams: 1,
|
||||||
|
},
|
||||||
"HELP": {
|
"HELP": {
|
||||||
handler: helpHandler,
|
handler: helpHandler,
|
||||||
minParams: 0,
|
minParams: 0,
|
||||||
|
@ -502,8 +502,9 @@ type Config struct {
|
|||||||
MOTDFormatting bool `yaml:"motd-formatting"`
|
MOTDFormatting bool `yaml:"motd-formatting"`
|
||||||
ProxyAllowedFrom []string `yaml:"proxy-allowed-from"`
|
ProxyAllowedFrom []string `yaml:"proxy-allowed-from"`
|
||||||
proxyAllowedFromNets []net.IPNet
|
proxyAllowedFromNets []net.IPNet
|
||||||
WebIRC []webircConfig `yaml:"webirc"`
|
WebIRC []webircConfig `yaml:"webirc"`
|
||||||
MaxSendQString string `yaml:"max-sendq"`
|
JwtServices map[string]string `yaml:"jwt-services"`
|
||||||
|
MaxSendQString string `yaml:"max-sendq"`
|
||||||
MaxSendQBytes int
|
MaxSendQBytes int
|
||||||
AllowPlaintextResume bool `yaml:"allow-plaintext-resume"`
|
AllowPlaintextResume bool `yaml:"allow-plaintext-resume"`
|
||||||
Compatibility struct {
|
Compatibility struct {
|
||||||
@ -1177,6 +1178,7 @@ func (config *Config) generateISupport() (err error) {
|
|||||||
isupport.Add("CHANTYPES", chanTypes)
|
isupport.Add("CHANTYPES", chanTypes)
|
||||||
isupport.Add("ELIST", "U")
|
isupport.Add("ELIST", "U")
|
||||||
isupport.Add("EXCEPTS", "")
|
isupport.Add("EXCEPTS", "")
|
||||||
|
isupport.Add("EXTJWT", "1")
|
||||||
isupport.Add("INVEX", "")
|
isupport.Add("INVEX", "")
|
||||||
isupport.Add("KICKLEN", strconv.Itoa(config.Limits.KickLen))
|
isupport.Add("KICKLEN", strconv.Itoa(config.Limits.KickLen))
|
||||||
isupport.Add("MAXLIST", fmt.Sprintf("beI:%s", strconv.Itoa(config.Limits.ChanListModes)))
|
isupport.Add("MAXLIST", fmt.Sprintf("beI:%s", strconv.Itoa(config.Limits.ChanListModes)))
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/goshuirc/irc-go/ircfmt"
|
"github.com/goshuirc/irc-go/ircfmt"
|
||||||
"github.com/goshuirc/irc-go/ircmsg"
|
"github.com/goshuirc/irc-go/ircmsg"
|
||||||
"github.com/oragono/oragono/irc/caps"
|
"github.com/oragono/oragono/irc/caps"
|
||||||
@ -911,6 +912,73 @@ func dlineHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
|
|||||||
return killClient
|
return killClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EXTJWT <target> [service_name]
|
||||||
|
func extjwtHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
||||||
|
expireInSeconds := int64(30)
|
||||||
|
|
||||||
|
accountName := client.AccountName()
|
||||||
|
if accountName == "*" {
|
||||||
|
accountName = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
claims := jwt.MapClaims{
|
||||||
|
"exp": time.Now().Unix() + expireInSeconds,
|
||||||
|
"iss": server.name,
|
||||||
|
"sub": client.Nick(),
|
||||||
|
"account": accountName,
|
||||||
|
"umodes": []string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Params[0] != "*" {
|
||||||
|
channel := server.channels.Get(msg.Params[0])
|
||||||
|
if channel == nil {
|
||||||
|
rb.Add(nil, server.name, "FAIL", "EXTJWT", "NO_SUCH_CHANNEL", client.t("No such channel"))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
claims["channel"] = channel.Name()
|
||||||
|
claims["joined"] = 0
|
||||||
|
claims["cmodes"] = []string{}
|
||||||
|
if channel.hasClient(client) {
|
||||||
|
claims["joined"] = time.Now().Unix() - 100 //TODO(dan): um we need to store when clients joined for reals
|
||||||
|
claims["cmodes"] = channel.ClientModeStrings(client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
|
||||||
|
// we default to a secret of `*`. if you want a real secret setup a service in the config~
|
||||||
|
service := "*"
|
||||||
|
secret := "*"
|
||||||
|
if 1 < len(msg.Params) {
|
||||||
|
service = strings.ToLower(msg.Params[1])
|
||||||
|
|
||||||
|
c := server.Config()
|
||||||
|
var exists bool
|
||||||
|
secret, exists = c.Server.JwtServices[service]
|
||||||
|
if !exists {
|
||||||
|
rb.Add(nil, server.name, "FAIL", "EXTJWT", "NO_SUCH_SERVICE", client.t("No such service"))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenString, err := token.SignedString([]byte(secret))
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
maxTokenLength := 400
|
||||||
|
|
||||||
|
for maxTokenLength < len(tokenString) {
|
||||||
|
rb.Add(nil, server.name, "EXTJWT", msg.Params[0], service, "*", tokenString[:maxTokenLength])
|
||||||
|
tokenString = tokenString[maxTokenLength:]
|
||||||
|
}
|
||||||
|
rb.Add(nil, server.name, "EXTJWT", msg.Params[0], service, tokenString)
|
||||||
|
} else {
|
||||||
|
rb.Add(nil, server.name, "FAIL", "EXTJWT", "UNKNOWN_ERROR", client.t("Could not generate EXTJWT token"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// HELP [<query>]
|
// HELP [<query>]
|
||||||
func helpHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
func helpHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
||||||
argument := strings.ToLower(strings.TrimSpace(strings.Join(msg.Params, " ")))
|
argument := strings.ToLower(strings.TrimSpace(strings.Join(msg.Params, " ")))
|
||||||
|
@ -198,6 +198,11 @@ ON <server> specifies that the ban is to be set on that specific server.
|
|||||||
[reason] and [oper reason], if they exist, are separated by a vertical bar (|).
|
[reason] and [oper reason], if they exist, are separated by a vertical bar (|).
|
||||||
|
|
||||||
If "DLINE LIST" is sent, the server sends back a list of our current DLINEs.`,
|
If "DLINE LIST" is sent, the server sends back a list of our current DLINEs.`,
|
||||||
|
},
|
||||||
|
"extjwt": {
|
||||||
|
text: `EXTJWT <target> [service_name]
|
||||||
|
|
||||||
|
Get a JSON Web Token for target (either * or a channel name).`,
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
text: `HELP <argument>
|
text: `HELP <argument>
|
||||||
|
@ -388,6 +388,18 @@ func (set *ModeSet) String() (result string) {
|
|||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strings returns the modes in this set.
|
||||||
|
func (set *ModeSet) Strings() (result []string) {
|
||||||
|
if set == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mode := range set.AllModes() {
|
||||||
|
result = append(result, mode.String())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Prefixes returns a list of prefixes for the given set of channel modes.
|
// Prefixes returns a list of prefixes for the given set of channel modes.
|
||||||
func (set *ModeSet) Prefixes(isMultiPrefix bool) (prefixes string) {
|
func (set *ModeSet) Prefixes(isMultiPrefix bool) (prefixes string) {
|
||||||
if set == nil {
|
if set == nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user