Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clients: add ignore list to idle user autounsub #349

Merged
merged 1 commit into from Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions brclient/appstate.go
Expand Up @@ -3174,8 +3174,9 @@ func newAppState(sendMsg func(tea.Msg), lndLogLines *sloglinesbuffer.Buffer,
ResourcesProvider: resRouter,
NoLoadChatHistory: args.NoLoadChatHistory,

AutoHandshakeInterval: args.AutoHandshakeInterval,
AutoRemoveIdleUsersInterval: args.AutoRemoveIdleUsersInterval,
AutoHandshakeInterval: args.AutoHandshakeInterval,
AutoRemoveIdleUsersInterval: args.AutoRemoveIdleUsersInterval,
AutoRemoveIdleUsersIgnoreList: args.AutoRemoveIdleUsersIgnore,

CertConfirmer: func(ctx context.Context, cs *tls.ConnectionState,
svrID *zkidentity.PublicIdentity) error {
Expand Down
7 changes: 7 additions & 0 deletions brclient/brclient.conf.go
Expand Up @@ -66,6 +66,13 @@ root = {{ .Root }}
# Set to zero to disable idle user removal.
# autoremoveidleusersinterval = 60d

# Comma-separated list of users to NOT auto-unsubscribe or remove from GCs
# during the idle check. This may be either the nick or UID of the user. By
# default, some well-known bots are included in this list:
# 86abd31f2141b274196d481edd061a00ab7a56b61a31656775c8a590d612b966 - Oprah
# ad716557157c1f191d8b5f8c6757ea41af49de27dc619fc87f337ca85be325ee - GC bot
# autoremoveignorelist =

# logging and debug
[log]

Expand Down
18 changes: 18 additions & 0 deletions brclient/config.go
Expand Up @@ -30,6 +30,16 @@ const (
appName = "brclient"
)

var (
// defaultAutoRemoveIgnoreList is the list of users that should not be
// removed during the auto unsubscribe idle check. By default, these are
// some well-known bots.
defaultAutoRemoveIgnoreList = strings.Join([]string{
"86abd31f2141b274196d481edd061a00ab7a56b61a31656775c8a590d612b966", // Oprah
"ad716557157c1f191d8b5f8c6757ea41af49de27dc619fc87f337ca85be325ee", // GC bot
}, ",")
)

var (
// Error to signal loadConfig() completed everything the cmd had to do
// and main() should exit.
Expand Down Expand Up @@ -99,6 +109,7 @@ type config struct {

AutoHandshakeInterval time.Duration
AutoRemoveIdleUsersInterval time.Duration
AutoRemoveIdleUsersIgnore []string

SyncFreeList bool

Expand Down Expand Up @@ -274,6 +285,7 @@ func loadConfig() (*config, error) {

flagAutoHandshake := fs.String("autohandshakeinterval", "21d", "")
flagAutoRemove := fs.String("autoremoveidleusersinterval", "60d", "")
flagAutoRemoveIgnoreList := fs.String("autoremoveignorelist", defaultAutoRemoveIgnoreList, "")

// log
flagMsgRoot := fs.String("log.msglog", defaultMsgRoot, "Root for message log files")
Expand Down Expand Up @@ -410,6 +422,11 @@ func loadConfig() (*config, error) {
mimeMap[spl[0]] = spl[1]
}

autoRemoveIgnoreList := strings.Split(*flagAutoRemoveIgnoreList, ",")
for i := range autoRemoveIgnoreList {
autoRemoveIgnoreList[i] = strings.TrimSpace(autoRemoveIgnoreList[i])
}

var jrpcListen []string
if *flagJSONRPCListen != "" {
jrpcListen = strings.Split(*flagJSONRPCListen, ",")
Expand Down Expand Up @@ -502,6 +519,7 @@ func loadConfig() (*config, error) {

AutoHandshakeInterval: autoHandshakeInterval,
AutoRemoveIdleUsersInterval: autoRemoveInterval,
AutoRemoveIdleUsersIgnore: autoRemoveIgnoreList,

SyncFreeList: *flagSyncFreeList,
ExtenalEditorForComments: *flagExternalEditorForComments,
Expand Down
19 changes: 18 additions & 1 deletion bruig/flutterui/bruig/lib/config.dart
Expand Up @@ -7,6 +7,11 @@ import 'package:path/path.dart' as path;

const APPNAME = "bruig";

const defaultAutoRemoveIgnoreList = [
"86abd31f2141b274196d481edd061a00ab7a56b61a31656775c8a590d612b966", // Oprah
"ad716557157c1f191d8b5f8c6757ea41af49de27dc619fc87f337ca85be325ee", // GC bot
];

String homeDir() {
var env = Platform.environment;
if (Platform.isWindows) {
Expand Down Expand Up @@ -83,6 +88,7 @@ class Config {
late final bool syncFreeList;
late final int autoHandshakeInterval;
late final int autoRemoveIdleUsersInterval;
late final List<String> autoRemoveIgnoreList;

Config();
Config.filled(
Expand Down Expand Up @@ -111,7 +117,8 @@ class Config {
this.noLoadChatHistory: true,
this.syncFreeList: true,
this.autoHandshakeInterval: 21 * 24 * 60 * 60,
this.autoRemoveIdleUsersInterval: 60 * 24 * 60 * 60});
this.autoRemoveIdleUsersInterval: 60 * 24 * 60 * 60,
this.autoRemoveIgnoreList: defaultAutoRemoveIgnoreList});
factory Config.newWithRPCHost(
Config cfg, String rpcHost, String tlsCert, String macaroonPath) =>
Config.filled(
Expand Down Expand Up @@ -141,6 +148,7 @@ class Config {
syncFreeList: cfg.syncFreeList,
autoHandshakeInterval: cfg.autoHandshakeInterval,
autoRemoveIdleUsersInterval: cfg.autoRemoveIdleUsersInterval,
autoRemoveIgnoreList: cfg.autoRemoveIgnoreList,
);

Future<void> saveConfig(String filepath) async {
Expand Down Expand Up @@ -200,6 +208,13 @@ Future<Config> loadConfig(String filepath) async {
return v != null && v != "" ? int.tryParse(v) : null;
};

var getCommaList = (String section, String opt) {
var v = f.get(section, opt);
return v != null && v != ""
? v.split(",").map((e) => e.trim()).toList()
: null;
};

var iniLogFile = f.get("log", "logfile");
String logfile = path.join(appDataDir, "applogs", "${APPNAME}.log");
if (iniLogFile != null) {
Expand Down Expand Up @@ -255,6 +270,8 @@ Future<Config> loadConfig(String filepath) async {
parseDurationSeconds(f.get("default", "autohandshakeinterval") ?? "21d");
c.autoRemoveIdleUsersInterval = parseDurationSeconds(
f.get("default", "autoremoveidleusersinterval") ?? "60d");
c.autoRemoveIgnoreList = getCommaList("default", "autoremoveignorelist") ??
defaultAutoRemoveIgnoreList;

if (c.walletType != "disabled") {
c.lnRPCHost = f.get("payment", "lnrpchost") ?? "localhost:10009";
Expand Down
3 changes: 2 additions & 1 deletion bruig/flutterui/bruig/lib/main.dart
Expand Up @@ -167,7 +167,8 @@ class _AppState extends State<App> with WindowListener {
cfg.circuitLimit,
cfg.noLoadChatHistory,
cfg.autoHandshakeInterval,
cfg.autoRemoveIdleUsersInterval);
cfg.autoRemoveIdleUsersInterval,
cfg.autoRemoveIgnoreList);
await Golib.initClient(initArgs);

navkey.currentState!.pushReplacementNamed(OverviewScreen.routeName);
Expand Down
5 changes: 4 additions & 1 deletion bruig/flutterui/plugin/lib/definitions.dart
Expand Up @@ -59,6 +59,8 @@ class InitClient {
final int autoHandshakeInterval;
@JsonKey(name: 'auto_remove_idle_users_interval')
final int autoRemoveIdleUsersInterval;
@JsonKey(name: 'auto_remove_idle_users_ignore')
final List<String> autoRemoveIdleUsersIgnore;

InitClient(
this.dbRoot,
Expand All @@ -82,7 +84,8 @@ class InitClient {
this.circuitLimit,
this.noLoadChatHistory,
this.autoHandshakeInterval,
this.autoRemoveIdleUsersInterval);
this.autoRemoveIdleUsersInterval,
this.autoRemoveIdleUsersIgnore);

Map<String, dynamic> toJson() => _$InitClientToJson(this);
}
Expand Down
4 changes: 4 additions & 0 deletions bruig/flutterui/plugin/lib/definitions.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions bruig/golib/command_handlers.go
Expand Up @@ -385,8 +385,9 @@ func handleInitClient(handle uint32, args initClient) error {
ResourcesProvider: resRouter,
NoLoadChatHistory: args.NoLoadChatHistory,

AutoHandshakeInterval: time.Duration(args.AutoHandshakeInterval) * time.Second,
AutoRemoveIdleUsersInterval: time.Duration(args.AutoRemoveIdleUsersInterval) * time.Second,
AutoHandshakeInterval: time.Duration(args.AutoHandshakeInterval) * time.Second,
AutoRemoveIdleUsersInterval: time.Duration(args.AutoRemoveIdleUsersInterval) * time.Second,
AutoRemoveIdleUsersIgnoreList: args.AutoRemoveIdleUsersIgnore,

CertConfirmer: func(ctx context.Context, cs *tls.ConnectionState,
svrID *zkidentity.PublicIdentity) error {
Expand Down
5 changes: 3 additions & 2 deletions bruig/golib/definitions.go
Expand Up @@ -37,8 +37,9 @@ type initClient struct {
TorIsolation bool `json:"torisolation"`
CircuitLimit uint32 `json:"circuit_limit"`

AutoHandshakeInterval int64 `json:"auto_handshake_interval"`
AutoRemoveIdleUsersInterval int64 `json:"auto_remove_idle_users_interval"`
AutoHandshakeInterval int64 `json:"auto_handshake_interval"`
AutoRemoveIdleUsersInterval int64 `json:"auto_remove_idle_users_interval"`
AutoRemoveIdleUsersIgnore []string `json:"auto_remove_idle_users_ignore"`
}

type iDInit struct {
Expand Down
6 changes: 6 additions & 0 deletions client/client.go
Expand Up @@ -189,6 +189,12 @@ type Config struct {
// automatically removed from GCs the local client admins and will be
// automatically unsubscribed from posts.
AutoRemoveIdleUsersInterval time.Duration

// AutoRemoveIdleUsersIgnoreList is a list of users that should NOT be
// forcibly unsubscribed even if they are idle. The values may be an
// user's nick or the prefix of the string representatation of its nick
// (i.e. anything acceptable as returned by UserByNick()).
AutoRemoveIdleUsersIgnoreList []string
}

// logger creates a logger for the given subsystem in the configured backend.
Expand Down
31 changes: 28 additions & 3 deletions client/client_kx.go
Expand Up @@ -719,8 +719,31 @@ func (c *Client) unsubIdleUsers(limitInterval, lastHandshakeInterval time.Durati
adminGCs = append(adminGCs, &gcs[i])
}

c.log.Debugf("Starting auto unsubscribe of idle users with limitDate %s "+
"and limitHandshakeDate %s", limitDate.Format(time.RFC3339),
limitHandshakeDate.Format(time.RFC3339))

// Build ignore list.
ignoreList := make(map[clientintf.UserID]struct{}, len(c.cfg.AutoRemoveIdleUsersIgnoreList))
for _, nick := range c.cfg.AutoRemoveIdleUsersIgnoreList {
uid, err := c.UIDByNick(nick)
if err == nil {
ignoreList[uid] = struct{}{}
} else {
c.log.Warnf("User %q in list to ignore from auto remove "+
"not found", nick)
}
}

users := c.rul.userList()
for _, uid := range users {
// Do not perform autounsub if this user is in the list to
// ignore unsubbing.
if _, ok := ignoreList[uid]; ok {
c.log.Tracef("Ignoring %s in auto unsubscribe action", uid)
continue
}

ru, err := c.rul.byID(uid)
if err != nil {
continue
Expand Down Expand Up @@ -755,9 +778,11 @@ func (c *Client) unsubIdleUsers(limitInterval, lastHandshakeInterval time.Durati
continue
}

ru.log.Infof("User %s is idle (last received msg time is %s). "+
"Removing from any active subscriptions and GCs.",
strescape.Nick(ru.Nick()), lastDecTime.Format(time.RFC3339))
ru.log.Infof("User %s is idle (last received msg time is %s, "+
"last handshake attempt time is %s). Removing from all "+
"active subscriptions and GCs.",
strescape.Nick(ru.Nick()), lastDecTime.Format(time.RFC3339),
ab.LastHandshakeAttempt.Format(time.RFC3339))
c.ntfns.notifyUnsubscribingIdleRemote(ru, lastDecTime)

// Forcibly make user unsub from posts.
Expand Down