Browse Source

Merge pull request #352 from slingamn/chanreglimit.1

track channel registrations per account
tags/v1.0.0-rc1
Daniel Oaks 5 years ago
parent
commit
7cf8aaccf6
No account linked to committer's email address
10 changed files with 154 additions and 14 deletions
  1. 45
    0
      irc/accounts.go
  2. 4
    1
      irc/channel.go
  3. 30
    1
      irc/channelreg.go
  4. 16
    7
      irc/chanserv.go
  5. 13
    4
      irc/config.go
  6. 25
    1
      irc/database.go
  7. 6
    0
      irc/getters.go
  8. 6
    0
      irc/handlers.go
  9. 3
    0
      irc/nickserv.go
  10. 6
    0
      oragono.yaml

+ 45
- 0
irc/accounts.go View File

33
 	keyAccountEnforcement      = "account.customenforcement %s"
33
 	keyAccountEnforcement      = "account.customenforcement %s"
34
 	keyAccountVHost            = "account.vhost %s"
34
 	keyAccountVHost            = "account.vhost %s"
35
 	keyCertToAccount           = "account.creds.certfp %s"
35
 	keyCertToAccount           = "account.creds.certfp %s"
36
+	keyAccountChannels         = "account.channels %s"
36
 
37
 
37
 	keyVHostQueueAcctToId = "vhostQueue %s"
38
 	keyVHostQueueAcctToId = "vhostQueue %s"
38
 	vhostRequestIdx       = "vhostQueue"
39
 	vhostRequestIdx       = "vhostQueue"
856
 	nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, casefoldedAccount)
857
 	nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, casefoldedAccount)
857
 	vhostKey := fmt.Sprintf(keyAccountVHost, casefoldedAccount)
858
 	vhostKey := fmt.Sprintf(keyAccountVHost, casefoldedAccount)
858
 	vhostQueueKey := fmt.Sprintf(keyVHostQueueAcctToId, casefoldedAccount)
859
 	vhostQueueKey := fmt.Sprintf(keyVHostQueueAcctToId, casefoldedAccount)
860
+	channelsKey := fmt.Sprintf(keyAccountChannels, casefoldedAccount)
859
 
861
 
860
 	var clients []*Client
862
 	var clients []*Client
861
 
863
 
864
+	var registeredChannels []string
865
+	// on our way out, unregister all the account's channels and delete them from the db
866
+	defer func() {
867
+		for _, channelName := range registeredChannels {
868
+			info := am.server.channelRegistry.LoadChannel(channelName)
869
+			if info != nil && info.Founder == casefoldedAccount {
870
+				am.server.channelRegistry.Delete(channelName, *info)
871
+			}
872
+			channel := am.server.channels.Get(channelName)
873
+			if channel != nil {
874
+				channel.SetUnregistered(casefoldedAccount)
875
+			}
876
+		}
877
+	}()
878
+
862
 	var credText string
879
 	var credText string
863
 	var rawNicks string
880
 	var rawNicks string
864
 
881
 
866
 	defer am.serialCacheUpdateMutex.Unlock()
883
 	defer am.serialCacheUpdateMutex.Unlock()
867
 
884
 
868
 	var accountName string
885
 	var accountName string
886
+	var channelsStr string
869
 	am.server.store.Update(func(tx *buntdb.Tx) error {
887
 	am.server.store.Update(func(tx *buntdb.Tx) error {
870
 		tx.Delete(accountKey)
888
 		tx.Delete(accountKey)
871
 		accountName, _ = tx.Get(accountNameKey)
889
 		accountName, _ = tx.Get(accountNameKey)
879
 		credText, err = tx.Get(credentialsKey)
897
 		credText, err = tx.Get(credentialsKey)
880
 		tx.Delete(credentialsKey)
898
 		tx.Delete(credentialsKey)
881
 		tx.Delete(vhostKey)
899
 		tx.Delete(vhostKey)
900
+		channelsStr, _ = tx.Get(channelsKey)
901
+		tx.Delete(channelsKey)
902
+
882
 		_, err := tx.Delete(vhostQueueKey)
903
 		_, err := tx.Delete(vhostQueueKey)
883
 		am.decrementVHostQueueCount(casefoldedAccount, err)
904
 		am.decrementVHostQueueCount(casefoldedAccount, err)
884
 		return nil
905
 		return nil
899
 
920
 
900
 	skeleton, _ := Skeleton(accountName)
921
 	skeleton, _ := Skeleton(accountName)
901
 	additionalNicks := unmarshalReservedNicks(rawNicks)
922
 	additionalNicks := unmarshalReservedNicks(rawNicks)
923
+	registeredChannels = unmarshalRegisteredChannels(channelsStr)
902
 
924
 
903
 	am.Lock()
925
 	am.Lock()
904
 	defer am.Unlock()
926
 	defer am.Unlock()
925
 	if err != nil {
947
 	if err != nil {
926
 		return errAccountDoesNotExist
948
 		return errAccountDoesNotExist
927
 	}
949
 	}
950
+
928
 	return nil
951
 	return nil
929
 }
952
 }
930
 
953
 
954
+func unmarshalRegisteredChannels(channelsStr string) (result []string) {
955
+	if channelsStr != "" {
956
+		result = strings.Split(channelsStr, ",")
957
+	}
958
+	return
959
+}
960
+
961
+func (am *AccountManager) ChannelsForAccount(account string) (channels []string) {
962
+	cfaccount, err := CasefoldName(account)
963
+	if err != nil {
964
+		return
965
+	}
966
+
967
+	var channelStr string
968
+	key := fmt.Sprintf(keyAccountChannels, cfaccount)
969
+	am.server.store.View(func(tx *buntdb.Tx) error {
970
+		channelStr, _ = tx.Get(key)
971
+		return nil
972
+	})
973
+	return unmarshalRegisteredChannels(channelStr)
974
+}
975
+
931
 func (am *AccountManager) AuthenticateByCertFP(client *Client) error {
976
 func (am *AccountManager) AuthenticateByCertFP(client *Client) error {
932
 	if client.certfp == "" {
977
 	if client.certfp == "" {
933
 		return errAccountInvalidCredentials
978
 		return errAccountInvalidCredentials

+ 4
- 1
irc/channel.go View File

165
 }
165
 }
166
 
166
 
167
 // SetUnregistered deletes the channel's registration information.
167
 // SetUnregistered deletes the channel's registration information.
168
-func (channel *Channel) SetUnregistered() {
168
+func (channel *Channel) SetUnregistered(expectedFounder string) {
169
 	channel.stateMutex.Lock()
169
 	channel.stateMutex.Lock()
170
 	defer channel.stateMutex.Unlock()
170
 	defer channel.stateMutex.Unlock()
171
 
171
 
172
+	if channel.registeredFounder != expectedFounder {
173
+		return
174
+	}
172
 	channel.registeredFounder = ""
175
 	channel.registeredFounder = ""
173
 	var zeroTime time.Time
176
 	var zeroTime time.Time
174
 	channel.registeredTime = zeroTime
177
 	channel.registeredTime = zeroTime

+ 30
- 1
irc/channelreg.go View File

254
 			for _, keyFmt := range channelKeyStrings {
254
 			for _, keyFmt := range channelKeyStrings {
255
 				tx.Delete(fmt.Sprintf(keyFmt, key))
255
 				tx.Delete(fmt.Sprintf(keyFmt, key))
256
 			}
256
 			}
257
+
258
+			// remove this channel from the client's list of registered channels
259
+			channelsKey := fmt.Sprintf(keyAccountChannels, info.Founder)
260
+			channelsStr, err := tx.Get(channelsKey)
261
+			if err == buntdb.ErrNotFound {
262
+				return
263
+			}
264
+			registeredChannels := unmarshalRegisteredChannels(channelsStr)
265
+			var nowRegisteredChannels []string
266
+			for _, channel := range registeredChannels {
267
+				if channel != key {
268
+					nowRegisteredChannels = append(nowRegisteredChannels, channel)
269
+				}
270
+			}
271
+			tx.Set(channelsKey, strings.Join(nowRegisteredChannels, ","), nil)
257
 		}
272
 		}
258
 	}
273
 	}
259
 }
274
 }
260
 
275
 
261
 // saveChannel saves a channel to the store.
276
 // saveChannel saves a channel to the store.
262
 func (reg *ChannelRegistry) saveChannel(tx *buntdb.Tx, channelKey string, channelInfo RegisteredChannel, includeFlags uint) {
277
 func (reg *ChannelRegistry) saveChannel(tx *buntdb.Tx, channelKey string, channelInfo RegisteredChannel, includeFlags uint) {
278
+	// maintain the mapping of account -> registered channels
279
+	chanExistsKey := fmt.Sprintf(keyChannelExists, channelKey)
280
+	_, existsErr := tx.Get(chanExistsKey)
281
+	if existsErr == buntdb.ErrNotFound {
282
+		// this is a new registration, need to update account-to-channels
283
+		accountChannelsKey := fmt.Sprintf(keyAccountChannels, channelInfo.Founder)
284
+		alreadyChannels, _ := tx.Get(accountChannelsKey)
285
+		newChannels := channelKey // this is the casefolded channel name
286
+		if alreadyChannels != "" {
287
+			newChannels = fmt.Sprintf("%s,%s", alreadyChannels, newChannels)
288
+		}
289
+		tx.Set(accountChannelsKey, newChannels, nil)
290
+	}
291
+
263
 	if includeFlags&IncludeInitial != 0 {
292
 	if includeFlags&IncludeInitial != 0 {
264
-		tx.Set(fmt.Sprintf(keyChannelExists, channelKey), "1", nil)
293
+		tx.Set(chanExistsKey, "1", nil)
265
 		tx.Set(fmt.Sprintf(keyChannelName, channelKey), channelInfo.Name, nil)
294
 		tx.Set(fmt.Sprintf(keyChannelName, channelKey), channelInfo.Name, nil)
266
 		tx.Set(fmt.Sprintf(keyChannelRegTime, channelKey), strconv.FormatInt(channelInfo.RegisteredAt.Unix(), 10), nil)
295
 		tx.Set(fmt.Sprintf(keyChannelRegTime, channelKey), strconv.FormatInt(channelInfo.RegisteredAt.Unix(), 10), nil)
267
 		tx.Set(fmt.Sprintf(keyChannelFounder, channelKey), channelInfo.Founder, nil)
296
 		tx.Set(fmt.Sprintf(keyChannelFounder, channelKey), channelInfo.Founder, nil)

+ 16
- 7
irc/chanserv.go View File

224
 		return
224
 		return
225
 	}
225
 	}
226
 
226
 
227
+	account := client.Account()
228
+	channelsAlreadyRegistered := server.accounts.ChannelsForAccount(account)
229
+	if server.Config().Channels.Registration.MaxChannelsPerAccount <= len(channelsAlreadyRegistered) {
230
+		csNotice(rb, client.t("You have already registered the maximum number of channels; try dropping some with /CS UNREGISTER"))
231
+		return
232
+	}
233
+
227
 	// this provides the synchronization that allows exactly one registration of the channel:
234
 	// this provides the synchronization that allows exactly one registration of the channel:
228
-	err = channelInfo.SetRegistered(client.Account())
235
+	err = channelInfo.SetRegistered(account)
229
 	if err != nil {
236
 	if err != nil {
230
 		csNotice(rb, err.Error())
237
 		csNotice(rb, err.Error())
231
 		return
238
 		return
270
 		return
277
 		return
271
 	}
278
 	}
272
 
279
 
273
-	hasPrivs := client.HasRoleCapabs("chanreg")
274
-	if !hasPrivs {
275
-		founder := channel.Founder()
276
-		hasPrivs = founder != "" && founder == client.Account()
280
+	founder := channel.Founder()
281
+	if founder == "" {
282
+		csNotice(rb, client.t("That channel is not registered"))
283
+		return
277
 	}
284
 	}
285
+
286
+	hasPrivs := client.HasRoleCapabs("chanreg") || founder == client.Account()
278
 	if !hasPrivs {
287
 	if !hasPrivs {
279
 		csNotice(rb, client.t("Insufficient privileges"))
288
 		csNotice(rb, client.t("Insufficient privileges"))
280
 		return
289
 		return
288
 		return
297
 		return
289
 	}
298
 	}
290
 
299
 
291
-	channel.SetUnregistered()
292
-	go server.channelRegistry.Delete(channelKey, info)
300
+	channel.SetUnregistered(founder)
301
+	server.channelRegistry.Delete(channelKey, info)
293
 	csNotice(rb, fmt.Sprintf(client.t("Channel %s is now unregistered"), channelKey))
302
 	csNotice(rb, fmt.Sprintf(client.t("Channel %s is now unregistered"), channelKey))
294
 }
303
 }
295
 
304
 

+ 13
- 4
irc/config.go View File

181
 
181
 
182
 // ChannelRegistrationConfig controls channel registration.
182
 // ChannelRegistrationConfig controls channel registration.
183
 type ChannelRegistrationConfig struct {
183
 type ChannelRegistrationConfig struct {
184
-	Enabled bool
184
+	Enabled               bool
185
+	MaxChannelsPerAccount int `yaml:"max-channels-per-account"`
185
 }
186
 }
186
 
187
 
187
 // OperClassConfig defines a specific operator class.
188
 // OperClassConfig defines a specific operator class.
293
 	Accounts AccountConfig
294
 	Accounts AccountConfig
294
 
295
 
295
 	Channels struct {
296
 	Channels struct {
296
-		DefaultModes *string `yaml:"default-modes"`
297
-		defaultModes modes.Modes
298
-		Registration ChannelRegistrationConfig
297
+		DefaultModes         *string `yaml:"default-modes"`
298
+		defaultModes         modes.Modes
299
+		MaxChannelsPerClient int `yaml:"max-channels-per-client"`
300
+		Registration         ChannelRegistrationConfig
299
 	}
301
 	}
300
 
302
 
301
 	OperClasses map[string]*OperClassConfig `yaml:"oper-classes"`
303
 	OperClasses map[string]*OperClassConfig `yaml:"oper-classes"`
789
 		config.Accounts.Registration.BcryptCost = passwd.DefaultCost
791
 		config.Accounts.Registration.BcryptCost = passwd.DefaultCost
790
 	}
792
 	}
791
 
793
 
794
+	if config.Channels.MaxChannelsPerClient == 0 {
795
+		config.Channels.MaxChannelsPerClient = 100
796
+	}
797
+	if config.Channels.Registration.MaxChannelsPerAccount == 0 {
798
+		config.Channels.Registration.MaxChannelsPerAccount = 15
799
+	}
800
+
792
 	// in the current implementation, we disable history by creating a history buffer
801
 	// in the current implementation, we disable history by creating a history buffer
793
 	// with zero capacity. but the `enabled` config option MUST be respected regardless
802
 	// with zero capacity. but the `enabled` config option MUST be respected regardless
794
 	// of this detail
803
 	// of this detail

+ 25
- 1
irc/database.go View File

22
 	// 'version' of the database schema
22
 	// 'version' of the database schema
23
 	keySchemaVersion = "db.version"
23
 	keySchemaVersion = "db.version"
24
 	// latest schema of the db
24
 	// latest schema of the db
25
-	latestDbSchema = "4"
25
+	latestDbSchema = "5"
26
 )
26
 )
27
 
27
 
28
 type SchemaChanger func(*Config, *buntdb.Tx) error
28
 type SchemaChanger func(*Config, *buntdb.Tx) error
390
 	return nil
390
 	return nil
391
 }
391
 }
392
 
392
 
393
+// create new key tracking channels that belong to an account
394
+func schemaChangeV4ToV5(config *Config, tx *buntdb.Tx) error {
395
+	founderToChannels := make(map[string][]string)
396
+	prefix := "channel.founder "
397
+	tx.AscendGreaterOrEqual("", prefix, func(key, value string) bool {
398
+		if !strings.HasPrefix(key, prefix) {
399
+			return false
400
+		}
401
+		channel := strings.TrimPrefix(key, prefix)
402
+		founderToChannels[value] = append(founderToChannels[value], channel)
403
+		return true
404
+	})
405
+
406
+	for founder, channels := range founderToChannels {
407
+		tx.Set(fmt.Sprintf("account.channels %s", founder), strings.Join(channels, ","), nil)
408
+	}
409
+	return nil
410
+}
411
+
393
 func init() {
412
 func init() {
394
 	allChanges := []SchemaChange{
413
 	allChanges := []SchemaChange{
395
 		{
414
 		{
407
 			TargetVersion:  "4",
426
 			TargetVersion:  "4",
408
 			Changer:        schemaChangeV3ToV4,
427
 			Changer:        schemaChangeV3ToV4,
409
 		},
428
 		},
429
+		{
430
+			InitialVersion: "4",
431
+			TargetVersion:  "5",
432
+			Changer:        schemaChangeV4ToV5,
433
+		},
410
 	}
434
 	}
411
 
435
 
412
 	// build the index
436
 	// build the index

+ 6
- 0
irc/getters.go View File

200
 	return
200
 	return
201
 }
201
 }
202
 
202
 
203
+func (client *Client) NumChannels() int {
204
+	client.stateMutex.RLock()
205
+	defer client.stateMutex.RUnlock()
206
+	return len(client.channels)
207
+}
208
+
203
 func (client *Client) WhoWas() (result WhoWas) {
209
 func (client *Client) WhoWas() (result WhoWas) {
204
 	return client.Details().WhoWas
210
 	return client.Details().WhoWas
205
 }
211
 }

+ 6
- 0
irc/handlers.go View File

1159
 		keys = strings.Split(msg.Params[1], ",")
1159
 		keys = strings.Split(msg.Params[1], ",")
1160
 	}
1160
 	}
1161
 
1161
 
1162
+	config := server.Config()
1163
+	oper := client.Oper()
1162
 	for i, name := range channels {
1164
 	for i, name := range channels {
1165
+		if config.Channels.MaxChannelsPerClient <= client.NumChannels() && oper == nil {
1166
+			rb.Add(nil, server.name, ERR_TOOMANYCHANNELS, client.Nick(), name, client.t("You have joined too many channels"))
1167
+			return false
1168
+		}
1163
 		var key string
1169
 		var key string
1164
 		if len(keys) > i {
1170
 		if len(keys) > i {
1165
 			key = keys[i]
1171
 			key = keys[i]

+ 3
- 0
irc/nickserv.go View File

327
 	for _, nick := range account.AdditionalNicks {
327
 	for _, nick := range account.AdditionalNicks {
328
 		nsNotice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick))
328
 		nsNotice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick))
329
 	}
329
 	}
330
+	for _, channel := range server.accounts.ChannelsForAccount(accountName) {
331
+		nsNotice(rb, fmt.Sprintf(client.t("Registered channel: %s"), channel))
332
+	}
330
 }
333
 }
331
 
334
 
332
 func nsRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
335
 func nsRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {

+ 6
- 0
oragono.yaml View File

275
     # see  /QUOTE HELP cmodes  for more channel modes
275
     # see  /QUOTE HELP cmodes  for more channel modes
276
     default-modes: +nt
276
     default-modes: +nt
277
 
277
 
278
+    # how many channels can a client be in at once?
279
+    max-channels-per-client: 100
280
+
278
     # channel registration - requires an account
281
     # channel registration - requires an account
279
     registration:
282
     registration:
280
         # can users register new channels?
283
         # can users register new channels?
281
         enabled: true
284
         enabled: true
282
 
285
 
286
+        # how many channels can each account register?
287
+        max-channels-per-account: 15
288
+
283
 # operator classes
289
 # operator classes
284
 oper-classes:
290
 oper-classes:
285
     # local operator
291
     # local operator

Loading…
Cancel
Save