lita-gsuite 0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 39fffd8c5976caaef1dd7c8b68a7562f95aa6f9b
4
+ data.tar.gz: 342a2b5efcf4f14b2f66a2083ebb201ee9c521dc
5
+ SHA512:
6
+ metadata.gz: 6df288e9de29014dbae5c9523441cfbf4b9bde4d67850ce04ac627963d564830dd32cc06c71e88346baf88f295d8a6f2360954fbaa3b320a1928ab1be6bec1bf
7
+ data.tar.gz: 145eb15b7d0ed9e454cd5bbdab45ad5a4a25470805bb46455e7dee8c34d5706634e1fe2172735092ee06f688f6f71e08ef3db15976a68d318e7c2824e3cae150
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2016 James Healy
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,134 @@
1
+ # lita-gsuite
2
+
3
+ A lita plugin for interacting with a GSuite account.
4
+
5
+ By design, only read-only access is requested. This is intended to provide some visibility
6
+ into the account, not provide administrative functions.
7
+
8
+ This was written for a GSuite account with ~125 active users. It may not scale
9
+ well to larger accounts, but feedback and optimisations are welcome.
10
+
11
+ ## Installation
12
+
13
+ Add this gem to your lita installation by including the following line in your Gemfile:
14
+
15
+ gem "lita-gsuite", git: "http://github.com/yob/lita-gsuite.git"
16
+
17
+ ## Configuration
18
+
19
+ Edit your lita\_config.rb to include the following lines lines. Some of the
20
+ values are sensitive, so using ENV vars is recommended to keep the values out
21
+ of version control.
22
+
23
+ When an API call to google is required, we want to make it with tokens that
24
+ are tied to the specific user that requested data. To do so, we use Google's
25
+ OAuth2 support.
26
+
27
+ That requires an OAuth2 client ID and secret - see "Authentication" below for more
28
+ details on how to generate these:
29
+
30
+ config.handlers.gsuite.oauth_client_id = ENV["GOOGLE_CLIENT_ID"]
31
+ config.handlers.gsuite.oauth_client_secret = ENV["GOOGLE_CLIENT_SECRET"]
32
+
33
+ ## Authentication
34
+
35
+ The lita bot requires an OAuth client ID and secret before it can initiate
36
+ the process to generate an OAuth2 token for each user.
37
+
38
+ These can be created on the [Google Developers
39
+ Console](https://console.developers.google.com/), and Google has [some
40
+ documentation](https://developers.google.com/identity/protocols/OAuth2).
41
+
42
+ You should be given the opportunity to view the new ID and secret. Be sure to copy them
43
+ down, as they can't be retrieved again later.
44
+
45
+ Once the handler is configured and running, each user that wants to interact with it
46
+ will be prompted to complete an OAuth authorisation process before they can start. This
47
+ generates an API token that's specific to them and will be used to make API calls on
48
+ their behalf.
49
+
50
+ ## Enable Google API
51
+
52
+ The GSuite API must be explicitly enabled for your account, and the new service account
53
+ must be whitelisted before it can access any data.
54
+
55
+ 1. Sign in to https://admin.google.com
56
+ 2. Visit the Security tab, click "API reference" and "Enable API Access"
57
+
58
+ ## Chat commands
59
+
60
+ ### Administrators
61
+
62
+ List users with super or delegated administrative privileges, and their two-factor
63
+ auth status.
64
+
65
+ lita gsuite list-admins
66
+
67
+ ### Empty Groups
68
+
69
+ List groups with no members.
70
+
71
+ lita gsuite empty-groups
72
+
73
+ ### Inactive Users
74
+
75
+ List active users that haven't logged in for 8 weeks. This may be helpful for
76
+ identifying accounts that should be suspended or deleted.
77
+
78
+ lita gsuite suspension-candidates
79
+
80
+ ### Suspended Users
81
+
82
+ List suspended users that haven't logged in for 26 weeks. This may be helpful
83
+ for identifying accounts that have been suspended for a long time and may be
84
+ candidates for deletion.
85
+
86
+ lita gsuite deletion-candidates
87
+
88
+ ### User with No Organisational Unit
89
+
90
+ List users not assigned to an Organisational Unit.
91
+
92
+ lita gsuite no-ou
93
+
94
+ ### Two Factor Authentication
95
+
96
+ Print key stats on Second Factor Authentication uptake.
97
+
98
+ lita gsuite two-factor-stats
99
+
100
+ ### Two Factor Authentication Disabled
101
+
102
+ Print users within an Organisational Unit that don't have Second Factor Authentication enabled.
103
+
104
+ lita gsuite two-factor-off /OUPATH
105
+
106
+ ## Periodic Updates
107
+
108
+ To help monitor the above reports automatically, it's possible to schedule them to be printed in
109
+ a channel periodically.
110
+
111
+ List the reports that will periodically print in the current channel:
112
+
113
+ lita gsuite schedule list
114
+
115
+ List the reports that are available to print periodically:
116
+
117
+ lita gsuite schedule commands
118
+
119
+ Schedule a weekly report in the current channel, specified in UTC time:
120
+
121
+ lita gsuite schedule add-weekly <day-of-the-week> HH:MM <report-name>
122
+ lita gsuite schedule add-weekly wednesday 01:00 list-admins
123
+ lita gsuite schedule add-weekly monday 13:45 no-ou
124
+
125
+ Schedule a time-window report that will run regularly and print output when
126
+ new data is available:
127
+
128
+ lita gsuite schedule add-window <report-name>
129
+ lita gsuite schedule add-window list-activities
130
+
131
+ Delete a scheduled report from the current room. The ID can be find in the
132
+ output of "schedule list":
133
+
134
+ lita gsuite schedule del <id>
@@ -0,0 +1,15 @@
1
+ require "lita/commands/deletion_candidates"
2
+ require "lita/commands/empty_groups"
3
+ require "lita/commands/list_admins"
4
+ require "lita/commands/list_activities"
5
+ require "lita/commands/no_org_unit"
6
+ require "lita/commands/suspension_candidates"
7
+ require "lita/commands/two_factor_off"
8
+ require "lita/commands/two_factor_stats"
9
+
10
+ require "lita/weekly_schedule"
11
+ require "lita/window_schedule"
12
+
13
+ require "lita/gsuite_gateway"
14
+ require "lita/gsuite"
15
+ require "lita/redis_token_store"
@@ -0,0 +1,49 @@
1
+ module Lita
2
+ module Commands
3
+
4
+ class DeletionCandidates
5
+ MAX_WEEKS_SUSPENDED = 26
6
+
7
+ def name
8
+ 'deletion-candidates'
9
+ end
10
+
11
+ def run(robot, target, gateway, opts = {})
12
+ msg = build_msg(gateway)
13
+ robot.send_message(target, msg) if msg
14
+ robot.send_message(target, "No users found") if msg.nil? && opts[:negative_ack]
15
+ end
16
+
17
+ private
18
+
19
+ def build_msg(gateway)
20
+ users = long_term_suspended_users(gateway)
21
+ if users.any?
22
+ msg = "The following users are suspended, and have not logged in for #{MAX_WEEKS_SUSPENDED} weeks. "
23
+ msg += "If appropriate, consider deleting their accounts:\n"
24
+ msg += users.map { |user|
25
+ "- #{user.path}"
26
+ }.sort.join("\n")
27
+ end
28
+ end
29
+
30
+ def long_term_suspended_users(gateway)
31
+ timestamp = max_weeks_suspended_ago
32
+
33
+ gateway.users.select { |user|
34
+ user.suspended?
35
+ }.select { |user|
36
+ user.last_login_at < timestamp
37
+ }
38
+ end
39
+
40
+ def max_weeks_suspended_ago
41
+ (Time.now.utc - weeks_in_seconds(MAX_WEEKS_SUSPENDED)).to_datetime
42
+ end
43
+
44
+ def weeks_in_seconds(weeks)
45
+ 60 * 60 * 24 * 7 * weeks.to_i
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,36 @@
1
+ module Lita
2
+ module Commands
3
+
4
+ class EmptyGroups
5
+
6
+ def name
7
+ 'empty-groups'
8
+ end
9
+
10
+ def run(robot, target, gateway, opts = {})
11
+ msg = build_msg(gateway)
12
+ robot.send_message(target, msg) if msg
13
+ robot.send_message(target, "No groups found") if msg.nil? && opts[:negative_ack]
14
+ end
15
+
16
+ private
17
+
18
+ def build_msg(gateway)
19
+ groups = empty_groups(gateway)
20
+
21
+ if groups.any?
22
+ msg = "The following groups have no members, which may result in undelivered email.\n"
23
+ msg += groups.map { |group|
24
+ "- #{group.email}"
25
+ }.join("\n")
26
+ end
27
+ end
28
+
29
+ def empty_groups(gateway)
30
+ gateway.groups.select { |group|
31
+ group.member_count == 0
32
+ }
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ module Lita
2
+ module Commands
3
+
4
+ class ListActivities
5
+
6
+ def name
7
+ 'list-activities'
8
+ end
9
+
10
+ def duration_minutes
11
+ 10
12
+ end
13
+
14
+ def buffer_minutes
15
+ 10
16
+ end
17
+
18
+ def run(robot, target, gateway, window_start, window_end)
19
+ activities = gateway.admin_activities(window_start, window_end)
20
+ activities.sort_by(&:time).map(&:to_msg).each_with_index do |message, index|
21
+ robot.send_message(target, message)
22
+ sleep(1) # TODO ergh. required to stop slack disconnecting us for high sending rates
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,39 @@
1
+ module Lita
2
+ module Commands
3
+
4
+ class ListAdmins
5
+
6
+ def name
7
+ 'list-admins'
8
+ end
9
+
10
+ def run(robot, target, gateway, opts = {})
11
+ msg = build_msg(gateway)
12
+ robot.send_message(target, msg) if msg
13
+ robot.send_message(target, "No admins found") if msg.nil? && opts[:negative_ack]
14
+ end
15
+
16
+ private
17
+
18
+ def build_msg(gateway)
19
+ users = all_admins(gateway)
20
+
21
+ if users.any?
22
+ msg = "The following accounts have administrative privileges:\n"
23
+ msg += users.map { |user|
24
+ "- #{user.ou_path}/#{user.email} (2fa enabled: #{tfa?(user)})"
25
+ }.join("\n")
26
+ end
27
+ end
28
+
29
+ def all_admins(gateway)
30
+ (gateway.delegated_admins + gateway.super_admins).uniq.sort_by(&:path)
31
+ end
32
+
33
+ def tfa?(user)
34
+ user.two_factor_enabled? ? "Y" : "N"
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,38 @@
1
+ module Lita
2
+ module Commands
3
+ class NoOrgUnit
4
+
5
+ def name
6
+ 'no-org-unit'
7
+ end
8
+
9
+ def run(robot, target, gateway, opts = {})
10
+ msg = build_msg(gateway)
11
+ robot.send_message(target, msg) if msg
12
+ robot.send_message(target, "No users are missing an org unit") if msg.nil? && opts[:negative_ack]
13
+ end
14
+
15
+ private
16
+
17
+ def build_msg(gateway)
18
+ users = no_org_unit_users(gateway)
19
+
20
+ if users.any?
21
+ msg = "The following users are not assigned to an organisational unit:\n"
22
+ msg += users.sort_by(&:path).map { |user|
23
+ "- #{user.email}"
24
+ }.join("\n")
25
+ end
26
+ end
27
+
28
+ def no_org_unit_users(gateway)
29
+ gateway.users.reject { |user|
30
+ user.suspended?
31
+ }.select { |user|
32
+ user.ou_path == "/"
33
+ }
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,49 @@
1
+ module Lita
2
+ module Commands
3
+ class SuspensionCandidates
4
+ MAX_WEEKS_WITHOUT_LOGIN = 8
5
+
6
+ def name
7
+ 'suspension-candidates'
8
+ end
9
+
10
+ def run(robot, target, gateway, opts = {})
11
+ msg = build_msg(gateway)
12
+ robot.send_message(target, msg) if msg
13
+ robot.send_message(target, "No users found") if msg.nil? && opts[:negative_ack]
14
+ end
15
+
16
+ private
17
+
18
+ def build_msg(gateway)
19
+ users = active_users_with_no_recent_login(gateway)
20
+
21
+ if users.any?
22
+ msg = "The following users have active accounts, but have not logged in for #{MAX_WEEKS_WITHOUT_LOGIN} weeks. "
23
+ msg += "If appropriate, consider suspending or deleting their accounts:\n"
24
+ msg += users.map { |user|
25
+ "- #{user.path}"
26
+ }.sort.join("\n")
27
+ end
28
+ end
29
+
30
+ def active_users_with_no_recent_login(gateway)
31
+ timestamp = max_weeks_without_login_ago
32
+
33
+ gateway.users.reject { |user|
34
+ user.suspended?
35
+ }.select { |user|
36
+ user.last_login_at < timestamp && user.created_at < timestamp
37
+ }
38
+ end
39
+
40
+ def max_weeks_without_login_ago
41
+ (Time.now.utc - weeks_in_seconds(MAX_WEEKS_WITHOUT_LOGIN)).to_datetime
42
+ end
43
+
44
+ def weeks_in_seconds(weeks)
45
+ 60 * 60 * 24 * 7 * weeks.to_i
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ module Lita
2
+ module Commands
3
+
4
+ class TwoFactorOff
5
+
6
+ def initialize(ou_path = "/")
7
+ @ou_path = ou_path
8
+ end
9
+
10
+ def name
11
+ 'two-factor-off'
12
+ end
13
+
14
+ def run(robot, target, gateway, opts = {})
15
+ msg = build_msg(gateway)
16
+ robot.send_message(target, msg) if msg
17
+ if msg.nil? && opts[:negative_ack]
18
+ robot.send_message(target, "All users in #{@ou_path} have Two Factor Authentication enabled")
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def build_msg(gateway)
25
+ users = active_users_without_tfa(gateway)
26
+
27
+ if users.any?
28
+ msg = "Users in #{@ou_path} with Two Factor Authentication disabled:\n\n"
29
+ users.each do |user|
30
+ msg += "- #{user.email}\n"
31
+ end
32
+ msg
33
+ end
34
+ end
35
+
36
+ def active_users_without_tfa(gateway)
37
+ gateway.users.reject { |user|
38
+ user.suspended?
39
+ }.reject { |user|
40
+ user.two_factor_enabled?
41
+ }.select { |user|
42
+ user.ou_path == @ou_path
43
+ }.sort_by { |user|
44
+ user.path
45
+ }
46
+ end
47
+ end
48
+ end
49
+ end