mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-25 21:39:25 +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.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:
|
||||
# do not enable this unless the ircd is only accessible over internal networks
|
||||
allow-plaintext-resume: false
|
||||
|
@ -187,6 +187,13 @@ server:
|
||||
# - "192.168.1.1"
|
||||
# - "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:
|
||||
# do not enable this unless the ircd is only accessible over internal networks
|
||||
allow-plaintext-resume: false
|
||||
|
2
go.mod
2
go.mod
@ -4,11 +4,11 @@ go 1.14
|
||||
|
||||
require (
|
||||
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/go-ldap/ldap/v3 v3.1.10
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
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/onsi/ginkgo v1.12.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/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/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/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
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 {
|
||||
channel.stateMutex.RLock()
|
||||
founder := channel.registeredFounder
|
||||
|
@ -130,6 +130,10 @@ func init() {
|
||||
minParams: 1,
|
||||
oper: true,
|
||||
},
|
||||
"EXTJWT": {
|
||||
handler: extjwtHandler,
|
||||
minParams: 1,
|
||||
},
|
||||
"HELP": {
|
||||
handler: helpHandler,
|
||||
minParams: 0,
|
||||
|
@ -503,6 +503,7 @@ type Config struct {
|
||||
ProxyAllowedFrom []string `yaml:"proxy-allowed-from"`
|
||||
proxyAllowedFromNets []net.IPNet
|
||||
WebIRC []webircConfig `yaml:"webirc"`
|
||||
JwtServices map[string]string `yaml:"jwt-services"`
|
||||
MaxSendQString string `yaml:"max-sendq"`
|
||||
MaxSendQBytes int
|
||||
AllowPlaintextResume bool `yaml:"allow-plaintext-resume"`
|
||||
@ -1177,6 +1178,7 @@ func (config *Config) generateISupport() (err error) {
|
||||
isupport.Add("CHANTYPES", chanTypes)
|
||||
isupport.Add("ELIST", "U")
|
||||
isupport.Add("EXCEPTS", "")
|
||||
isupport.Add("EXTJWT", "1")
|
||||
isupport.Add("INVEX", "")
|
||||
isupport.Add("KICKLEN", strconv.Itoa(config.Limits.KickLen))
|
||||
isupport.Add("MAXLIST", fmt.Sprintf("beI:%s", strconv.Itoa(config.Limits.ChanListModes)))
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/goshuirc/irc-go/ircfmt"
|
||||
"github.com/goshuirc/irc-go/ircmsg"
|
||||
"github.com/oragono/oragono/irc/caps"
|
||||
@ -911,6 +912,73 @@ func dlineHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
|
||||
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>]
|
||||
func helpHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
||||
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 (|).
|
||||
|
||||
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": {
|
||||
text: `HELP <argument>
|
||||
|
@ -388,6 +388,18 @@ func (set *ModeSet) String() (result 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.
|
||||
func (set *ModeSet) Prefixes(isMultiPrefix bool) (prefixes string) {
|
||||
if set == nil {
|
||||
|
Loading…
Reference in New Issue
Block a user