ogam 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -0
- data/.rubocop_metrics_todo.yml +59 -0
- data/.rubocop_todo.yml +1 -1
- data/.travis.yml +5 -7
- data/Gemfile.lock +24 -5
- data/README.md +9 -15
- data/bin/ogam +111 -1
- data/lib/ogam/execute.rb +50 -0
- data/lib/ogam/group.rb +27 -0
- data/lib/ogam/group_assigner.rb +132 -0
- data/lib/ogam/job_title.rb +49 -0
- data/lib/ogam/organisation.rb +35 -0
- data/lib/ogam/signature.rb +22 -0
- data/lib/ogam/user.rb +21 -0
- data/lib/ogam/version.rb +1 -1
- data/lib/ogam.rb +9 -0
- data/ogam.gemspec +3 -1
- metadata +41 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d4a72e5717ef8b802f79499f53fb603ddc9ba61dbd06fa166c9a3045d877b748
|
4
|
+
data.tar.gz: d4cf4b93af132811e4a6ba058e0440ddc60115a414a7c967bb96c0a34363ffbd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4443a1c829534b5ebea13518d00e8d7d1b66dd8cfb2a2b095bbec52478392ac9f09cb8fc0d240482dfaa202e04ef210149e310ecc3a65316be51960a72440bf
|
7
|
+
data.tar.gz: 93d3527909a5d33d90f66cb70dcd170622ae9a947294548552431052005e3e2cd66235d6930cbe6e4e5f57a02952f09baa36cb9c3804bdad5672dcb42d0d986c
|
data/.rubocop.yml
CHANGED
data/.rubocop_metrics_todo.yml
CHANGED
@@ -0,0 +1,59 @@
|
|
1
|
+
Metrics/AbcSize:
|
2
|
+
Exclude:
|
3
|
+
- db/**/*
|
4
|
+
- spec/**/*
|
5
|
+
- config/routes.rb
|
6
|
+
- lib/ogam/group_assigner.rb
|
7
|
+
|
8
|
+
Metrics/BlockLength:
|
9
|
+
Exclude:
|
10
|
+
- db/**/*
|
11
|
+
- spec/**/*
|
12
|
+
- config/routes.rb
|
13
|
+
|
14
|
+
Metrics/BlockNesting:
|
15
|
+
Exclude:
|
16
|
+
- db/**/*
|
17
|
+
- spec/**/*
|
18
|
+
- config/routes.rb
|
19
|
+
|
20
|
+
Metrics/ClassLength:
|
21
|
+
Exclude:
|
22
|
+
- db/**/*
|
23
|
+
- spec/**/*
|
24
|
+
- config/routes.rb
|
25
|
+
- lib/ogam/group_assigner.rb
|
26
|
+
|
27
|
+
Metrics/CyclomaticComplexity:
|
28
|
+
Exclude:
|
29
|
+
- db/**/*
|
30
|
+
- spec/**/*
|
31
|
+
- config/routes.rb
|
32
|
+
- lib/ogam/group_assigner.rb
|
33
|
+
|
34
|
+
Metrics/MethodLength:
|
35
|
+
Exclude:
|
36
|
+
- db/**/*
|
37
|
+
- spec/**/*
|
38
|
+
- config/routes.rb
|
39
|
+
- lib/ogam/group_assigner.rb
|
40
|
+
|
41
|
+
Metrics/ModuleLength:
|
42
|
+
Exclude:
|
43
|
+
- db/**/*
|
44
|
+
- spec/**/*
|
45
|
+
- config/routes.rb
|
46
|
+
|
47
|
+
Metrics/ParameterLists:
|
48
|
+
Exclude:
|
49
|
+
- db/**/*
|
50
|
+
- spec/**/*
|
51
|
+
- config/routes.rb
|
52
|
+
|
53
|
+
Metrics/PerceivedComplexity:
|
54
|
+
Exclude:
|
55
|
+
- db/**/*
|
56
|
+
- spec/**/*
|
57
|
+
- config/routes.rb
|
58
|
+
- lib/ogam/group_assigner.rb
|
59
|
+
|
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config --exclude-limit 1000 --no-offense-counts --no-auto-gen-timestamp`
|
3
|
-
# using RuboCop version 0.
|
3
|
+
# using RuboCop version 0.80.0.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
data/.travis.yml
CHANGED
@@ -3,22 +3,20 @@ language: ruby
|
|
3
3
|
cache: bundler
|
4
4
|
rvm:
|
5
5
|
- 2.6.5
|
6
|
-
before_install:
|
7
|
-
- gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true
|
8
|
-
- gem install bundler -v '~> 1.17.3'
|
9
6
|
before_script:
|
10
|
-
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64
|
7
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64
|
8
|
+
> ./cc-test-reporter
|
11
9
|
- chmod +x ./cc-test-reporter
|
12
|
-
- ./cc-test-reporter before-build
|
10
|
+
- './cc-test-reporter before-build'
|
13
11
|
script:
|
14
12
|
- bundle exec rubocop
|
15
13
|
- bundle exec rake
|
16
14
|
after_script:
|
17
|
-
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
15
|
+
- './cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT'
|
18
16
|
deploy:
|
19
17
|
provider: rubygems
|
20
18
|
api_key:
|
21
|
-
secure:
|
19
|
+
secure: K5oq3QVDaNCEoeDlj9PQgE5oslHTTzzO33SA4XVRPqV/k6S2ASs0iCyAp0LPPFup9NPivw8C64mb41erSY4f/lW8a2tkfVYL28avBiy4n/kioo/hFKjL3WBoUGknsamoDQUBTCrUPtoQE5puducV44cH9AyPFJulXbw4hA8KXisi7xYc+e5NGSsTDhjlj4GBN0G3AOBgZ/x1dqLdG8/NV7Bqwr3fKHqpBEI4nKtGTzWUsoC+4SQVkWKwPzKcme47AiRZa5moKs6xzJQzamJm6sGzk0SQ24PSvDxv/AE6AJe67xbdSzxgN2haSLLTV99AW4ku9dP6JE8i799f9337lmCWNE24GCqlAAURR3GDGtMcv0pUCR12V39wJSdA0C3h4xDriGgSHM69xnY6I4hh0DZCSXMDDAHFQMSF5JU4zOQDKZhme2pUTQwTw0uNoutDp6/YF9kMeq+dzZygKMjtDFVy95IV3N9vXBMHvNQAohNYGVZ47Yy4n6Ss/AOHu6aqWPyFo0VSD4WAzFNJBtZvnMlleS+nRrpxjxaH+LkeqE0VkQU6IFeqipAihEeJDizWMXKcXd7RbFPxmsvcyDHh7kF5th5xCGxqW6O3rC2B64mDNA9NKaROJDMQYszeRb7pOuhoysMnzDOy0CwI9R7jZhlWVSHEZeGv4phMhTKhFGc=
|
22
20
|
gem: ogam
|
23
21
|
on:
|
24
22
|
rvm: 2.6.5
|
data/Gemfile.lock
CHANGED
@@ -1,23 +1,37 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ogam (
|
4
|
+
ogam (1.0.0)
|
5
|
+
activesupport (~> 6.0.2)
|
6
|
+
attr_extras (~> 6.2.3)
|
5
7
|
thor (~> 1.0.1)
|
6
8
|
|
7
9
|
GEM
|
8
10
|
remote: https://rubygems.org/
|
9
11
|
specs:
|
12
|
+
activesupport (6.0.2.1)
|
13
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
14
|
+
i18n (>= 0.7, < 2)
|
15
|
+
minitest (~> 5.1)
|
16
|
+
tzinfo (~> 1.1)
|
17
|
+
zeitwerk (~> 2.2)
|
10
18
|
ast (2.4.0)
|
19
|
+
attr_extras (6.2.3)
|
20
|
+
concurrent-ruby (1.1.6)
|
11
21
|
diff-lcs (1.3)
|
12
22
|
docile (1.3.2)
|
23
|
+
i18n (1.8.2)
|
24
|
+
concurrent-ruby (~> 1.0)
|
13
25
|
jaro_winkler (1.5.4)
|
14
26
|
json (2.3.0)
|
27
|
+
minitest (5.14.0)
|
15
28
|
parallel (1.19.1)
|
16
29
|
parser (2.7.0.2)
|
17
30
|
ast (~> 2.4.0)
|
18
31
|
rack (2.2.2)
|
19
32
|
rainbow (3.0.0)
|
20
33
|
rake (13.0.1)
|
34
|
+
rexml (3.2.4)
|
21
35
|
rspec (3.9.0)
|
22
36
|
rspec-core (~> 3.9.0)
|
23
37
|
rspec-expectations (~> 3.9.0)
|
@@ -31,15 +45,16 @@ GEM
|
|
31
45
|
diff-lcs (>= 1.2.0, < 2.0)
|
32
46
|
rspec-support (~> 3.9.0)
|
33
47
|
rspec-support (3.9.2)
|
34
|
-
rubocop (0.
|
48
|
+
rubocop (0.80.0)
|
35
49
|
jaro_winkler (~> 1.5.1)
|
36
50
|
parallel (~> 1.10)
|
37
51
|
parser (>= 2.7.0.1)
|
38
52
|
rainbow (>= 2.2.2, < 4.0)
|
53
|
+
rexml
|
39
54
|
ruby-progressbar (~> 1.7)
|
40
55
|
unicode-display_width (>= 1.4.0, < 1.7)
|
41
|
-
rubocop-ogat (1.
|
42
|
-
rubocop (= 0.
|
56
|
+
rubocop-ogat (1.47.0)
|
57
|
+
rubocop (= 0.80.0)
|
43
58
|
rubocop-performance (= 1.5.2)
|
44
59
|
rubocop-rails (= 2.4.2)
|
45
60
|
rubocop-rspec (= 1.38.1)
|
@@ -57,7 +72,11 @@ GEM
|
|
57
72
|
simplecov-html (~> 0.10.0)
|
58
73
|
simplecov-html (0.10.2)
|
59
74
|
thor (1.0.1)
|
75
|
+
thread_safe (0.3.6)
|
76
|
+
tzinfo (1.2.6)
|
77
|
+
thread_safe (~> 0.1)
|
60
78
|
unicode-display_width (1.6.1)
|
79
|
+
zeitwerk (2.2.2)
|
61
80
|
|
62
81
|
PLATFORMS
|
63
82
|
ruby
|
@@ -67,7 +86,7 @@ DEPENDENCIES
|
|
67
86
|
ogam!
|
68
87
|
rake (~> 13.0)
|
69
88
|
rspec (~> 3.0)
|
70
|
-
rubocop-ogat (~> 1.
|
89
|
+
rubocop-ogat (~> 1.47.0)
|
71
90
|
simplecov (~> 0.17.1)
|
72
91
|
|
73
92
|
BUNDLED WITH
|
data/README.md
CHANGED
@@ -1,28 +1,22 @@
|
|
1
1
|
# Ogam
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
Wrapper around GAM for common tasks with OGAT's G Suite instance.
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
```ruby
|
12
|
-
gem 'ogam'
|
13
|
-
```
|
14
|
-
|
15
|
-
And then execute:
|
7
|
+
This tool relies on GAM being installed. First install that using the normal installation procedure, then run:
|
16
8
|
|
17
|
-
$
|
9
|
+
$ gem install ogam
|
18
10
|
|
19
|
-
|
11
|
+
If running on Google Cloud Shell, ensure the gems bin directory is in your path by appending the following line to ~/.bashrc:
|
20
12
|
|
21
|
-
|
13
|
+
`PATH=".gems/bin:$PATH"`
|
22
14
|
|
23
15
|
## Usage
|
24
16
|
|
25
|
-
|
17
|
+
For a list of available commands, run:
|
18
|
+
|
19
|
+
$ ogam help
|
26
20
|
|
27
21
|
## Development
|
28
22
|
|
@@ -32,4 +26,4 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
32
26
|
|
33
27
|
## Contributing
|
34
28
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
29
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Outwood/ogam.
|
data/bin/ogam
CHANGED
@@ -4,9 +4,119 @@
|
|
4
4
|
require 'bundler/setup'
|
5
5
|
require 'ogam'
|
6
6
|
require 'thor'
|
7
|
+
require 'shellwords'
|
7
8
|
|
9
|
+
# Signature subcommand cli
|
10
|
+
class SignatureCLI < Thor
|
11
|
+
desc('user EMAIL', 'Sets the signature for user with primary email EMAIL based on their name, job title, and org')
|
12
|
+
def user(email)
|
13
|
+
set_signatures(Ogam::User.active_by_email(email), pretend: parent_options[:pretend])
|
14
|
+
end
|
15
|
+
|
16
|
+
desc('domain DOMAIN', 'Sets the signature for all users @DOMAIN based on their name, job title, and org')
|
17
|
+
def domain(domain)
|
18
|
+
set_signatures(Ogam::User.active_for_domain(domain), pretend: parent_options[:pretend])
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def set_signatures(users, pretend:)
|
24
|
+
users.each do |user|
|
25
|
+
Ogam::Execute.single(Ogam::Signature.update_signature_command(user), pretend: pretend)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Command line interface for OGAM
|
8
31
|
class CLI < Thor
|
9
|
-
|
32
|
+
class_option :pretend, type: :boolean, default: false
|
33
|
+
|
34
|
+
desc(
|
35
|
+
'assign_to_standard_groups DOMAIN_OR_EMAIL',
|
36
|
+
'Assigns users at DOMAIN (e.g. newbold.outwood.com) or EMAIL address to standard groups based on their job title'
|
37
|
+
)
|
38
|
+
def assign_to_standard_groups(domain_or_email)
|
39
|
+
method = domain_or_email.include?('@') ? :for_user : :for_domain
|
40
|
+
Ogam::GroupAssigner.public_send(method, domain_or_email, pretend: options[:pretend])
|
41
|
+
end
|
42
|
+
|
43
|
+
desc(
|
44
|
+
'populate_titles_and_orgs PATH_TO_CSV',
|
45
|
+
'Populates job titles and organisation names from a CSV with columns primary_email, job_title, organisation'
|
46
|
+
)
|
47
|
+
def populate_titles_and_orgs(csv_path)
|
48
|
+
Ogam::Execute.csv_command(
|
49
|
+
csv_path,
|
50
|
+
'gam update user ~primary_email organization name ~organisation title ~job_title primary',
|
51
|
+
pretend: options[:pretend]
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
desc('create_shared_mailbox EMAIL NAME', 'Create a shared mailbox')
|
56
|
+
def create_shared_mailbox(email, name)
|
57
|
+
Ogam::Execute.single(Ogam::Group.create_shared_mailbox_command(email, name), pretend: options[:pretend])
|
58
|
+
end
|
59
|
+
|
60
|
+
option :moderated, type: :boolean, default: false
|
61
|
+
desc('create_group EMAIL NAME [--moderated]', 'Create a distribution list group, optionally moderated')
|
62
|
+
def create_group(email, name)
|
63
|
+
Ogam::Execute.single(
|
64
|
+
Ogam::Group.create_distribution_list_command(email, name, options[:moderated]), pretend: options[:pretend]
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
option :organisation, type: :string
|
69
|
+
desc(
|
70
|
+
'set_job_title EMAIL TITLE [--organisation ORG_NAME]',
|
71
|
+
'Sets job title for a user, and updates their signature, groups, and organisation name. Optionally override org.'
|
72
|
+
)
|
73
|
+
def set_job_title(email, title)
|
74
|
+
validate_job_title!(title)
|
75
|
+
|
76
|
+
approved_organisations = Ogam::Organisation.all
|
77
|
+
org = infer_organisation_if_necessary(options[:organisation], email, approved_organisations)
|
78
|
+
validate_organisation!(org, approved_organisations)
|
79
|
+
|
80
|
+
set_title_and_org_and_make_changes(email, title, org, pretend: options[:pretend])
|
81
|
+
end
|
82
|
+
|
83
|
+
desc('set_signature user/domain EMAIL/DOMAIN', 'Set email signatures for users/domains based on user attributes')
|
84
|
+
subcommand 'set_signature', SignatureCLI
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def validate_job_title!(title)
|
89
|
+
approved_job_titles = Ogam::JobTitle.all
|
90
|
+
new_job_titles = Ogam::JobTitle.job_titles(title)
|
91
|
+
new_job_titles.each do |s|
|
92
|
+
raise ArgumentError, "'#{s}' is not an approved job title." unless approved_job_titles.include?(s)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def validate_organisation!(org, approved_organisations)
|
97
|
+
org.split(' / ').each do |org_part|
|
98
|
+
unless approved_organisations.value?(org_part)
|
99
|
+
raise ArgumentError, "'#{org_part}' is not an approved organisation name."
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def infer_organisation_if_necessary(org, email, approved_organisations)
|
105
|
+
return org if org.present?
|
106
|
+
|
107
|
+
approved_organisations.fetch(email.split('@').last, '')
|
108
|
+
end
|
109
|
+
|
110
|
+
def set_title_and_org_and_make_changes(email, title, org, pretend: true)
|
111
|
+
Ogam::Execute.single(
|
112
|
+
"gam update user #{email} organization name #{Shellwords.escape(org)} title #{Shellwords.escape(title)} primary",
|
113
|
+
pretend: pretend
|
114
|
+
)
|
115
|
+
Ogam::GroupAssigner.for_user(email, pretend: pretend)
|
116
|
+
Ogam::User.active_by_email(email).each do |user|
|
117
|
+
Ogam::Execute.single(Ogam::Signature.update_signature_command(user), pretend: pretend)
|
118
|
+
end
|
119
|
+
end
|
10
120
|
end
|
11
121
|
|
12
122
|
CLI.start(ARGV)
|
data/lib/ogam/execute.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'csv'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'English'
|
6
|
+
|
7
|
+
module Ogam
|
8
|
+
# Methods to execute GAM commands
|
9
|
+
class Execute
|
10
|
+
def self.single(command, pretend: true)
|
11
|
+
puts "#{pretend ? '[PRETEND] ' : nil}Executing #{command}"
|
12
|
+
return if pretend
|
13
|
+
|
14
|
+
system("~/bin/gam/#{command}", exception: true)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.fetch(command, pretend: true)
|
18
|
+
puts "#{pretend ? '[PRETEND] ' : nil}Fetching results from #{command}"
|
19
|
+
return if pretend
|
20
|
+
|
21
|
+
`~/bin/gam/#{command}`
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.fetch_csv(command, pretend: true)
|
25
|
+
result = fetch(command, pretend: pretend)
|
26
|
+
return unless result
|
27
|
+
|
28
|
+
CSV.parse(result, headers: true)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.csv_command(csv_path, command, pretend: true)
|
32
|
+
single("gam csv #{Shellwords.escape(csv_path)} #{command}", pretend: pretend)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.multiple(commands, pretend: true)
|
36
|
+
return if commands.empty?
|
37
|
+
|
38
|
+
puts "#{pretend ? '[PRETEND] ' : nil}Executing multiple commands:"
|
39
|
+
commands.each do |command|
|
40
|
+
puts "\t#{command}"
|
41
|
+
end
|
42
|
+
return if pretend
|
43
|
+
|
44
|
+
Tempfile.create do |f|
|
45
|
+
commands.each { |command| f.puts command }
|
46
|
+
single("gam batch #{f.path}", pretend: pretend)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/ogam/group.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'shellwords'
|
4
|
+
module Ogam
|
5
|
+
# Methods relating to groups
|
6
|
+
class Group
|
7
|
+
def self.groups_for_domain(domain)
|
8
|
+
Ogam::Execute.fetch_csv("gam print groups domain #{domain}", pretend: false).map { |r| r['Email'] }
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.add_group_member_command(user:, group:)
|
12
|
+
"gam update group #{group} add member user #{user}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.create_shared_mailbox_command(email_address, name)
|
16
|
+
<<~COMMAND
|
17
|
+
gam create group #{email_address} name #{Shellwords.escape(name)} allow_external_members false who_can_join invited_can_join primary_language en-GB who_can_view_membership all_in_domain_can_view include_in_global_address_list true is_archived true members_can_post_as_the_group false allow_web_posting true send_message_deny_notification false reply_to reply_to_ignore message_moderation_level moderate_none who_can_contact_owner all_in_domain_can_contact who_can_leave_group all_members_can_leave who_can_add all_managers_can_add who_can_post_message anyone_can_post who_can_invite all_managers_can_invite who_can_view_group all_members_can_view show_in_group_directory true archive_only false spam_moderation_level moderate
|
18
|
+
COMMAND
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.create_distribution_list_command(email_address, name, moderated)
|
22
|
+
<<~COMMAND
|
23
|
+
gam create group #{email_address} name #{Shellwords.escape(name)} allow_external_members false who_can_join invited_can_join primary_language en-GB who_can_view_membership all_in_domain_can_view include_in_global_address_list true is_archived false members_can_post_as_the_group false allow_web_posting false send_message_deny_notification true reply_to reply_to_sender message_moderation_level #{moderated ? 'moderate_all_messages' : 'moderate_none'} who_can_contact_owner all_managers_can_contact who_can_leave_group none_can_leave who_can_add none_can_add who_can_post_message all_in_domain_can_post who_can_invite none_can_invite who_can_view_group all_managers_can_view show_in_group_directory true archive_only false spam_moderation_level moderate
|
24
|
+
COMMAND
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ogam
|
4
|
+
# Assign users from a domain to standard groups based on their job titles
|
5
|
+
class GroupAssigner
|
6
|
+
static_facade :call, :users, :domain, [pretend: true]
|
7
|
+
|
8
|
+
SUBJECT_TEACHER_TITLES = ['Teacher of ', 'Head of ', 'Second in ', 'Third in '].freeze
|
9
|
+
SUBJECT_MAPPING = {
|
10
|
+
'art-teachers' => ['Art'],
|
11
|
+
'business-teachers' => ['Business Studies'],
|
12
|
+
'dance-teachers' => ['Performing Arts'],
|
13
|
+
'design-technology-teachers' => ['Technology'],
|
14
|
+
'drama-teachers' => ['Drama', 'Music & Drama', 'Performing Arts'],
|
15
|
+
'english-teachers' => ['English'],
|
16
|
+
'epq-teachers' => [],
|
17
|
+
'geography-teachers' => ['Humanities'],
|
18
|
+
'guidance-teachers' => [],
|
19
|
+
'health-social-care-teachers' => ['Health & Social Care'],
|
20
|
+
'history-teachers' => %w[Humanities History],
|
21
|
+
'ict-teachers' => ['Computer Science'],
|
22
|
+
'law-teachers' => ['Law'],
|
23
|
+
'maths-teachers' => ['Maths'],
|
24
|
+
'mfl-teachers' => ['MFL'],
|
25
|
+
'music-teachers' => ['Music', 'Music & Drama'],
|
26
|
+
'pe-teachers' => ['PE'],
|
27
|
+
're-life-teachers' => ['Humanities'],
|
28
|
+
'science-teachers' => ['Science'],
|
29
|
+
'social-sciences-teachers' => ['Social Sciences']
|
30
|
+
}.freeze
|
31
|
+
|
32
|
+
def self.for_domain(domain, pretend: true)
|
33
|
+
call(User.active_for_domain(domain), domain, pretend: pretend)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.for_user(email, pretend: true)
|
37
|
+
call(User.active_by_email(email), email.split('@').last, pretend: pretend)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.groups_for_job_title(job_title, entry, domain)
|
41
|
+
groups_domain = "groups.#{domain}"
|
42
|
+
|
43
|
+
groups = groups_from_entry(entry, groups_domain)
|
44
|
+
groups += subject_groups(job_title, groups_domain) if entry['Teaching']
|
45
|
+
|
46
|
+
groups << "heads-of-departments@#{groups_domain}" if entry['Teaching'] && job_title.include?('Head of ')
|
47
|
+
groups << "principal@#{groups_domain}" if entry['SLT'] && ['Principal', 'Acting Principal'].include?(job_title)
|
48
|
+
|
49
|
+
groups << "learning-managers@#{groups_domain}" if job_title.include?('Learning Manager')
|
50
|
+
groups << "academy-council@#{groups_domain}" if job_title.include?('Academy Council')
|
51
|
+
groups << 'business-managers@trust-wide.outwood.com' if job_title == 'Business Manager'
|
52
|
+
|
53
|
+
groups
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.groups_from_entry(entry, groups_domain)
|
57
|
+
groups = []
|
58
|
+
groups << "teaching-staff@#{groups_domain}" if entry['Teaching']
|
59
|
+
groups << "support-staff@#{groups_domain}" if entry['Support']
|
60
|
+
if entry['SLT']
|
61
|
+
groups << "slt@#{groups_domain}"
|
62
|
+
groups << "leadership-team@#{groups_domain}"
|
63
|
+
end
|
64
|
+
groups << 'finance@trust-wide.outwood.com' if entry['Finance']
|
65
|
+
groups << 'data-exams@trust-wide.outwood.com' if entry['Data and Exams']
|
66
|
+
groups << 'human-resources@trust-wide.outwood.com' if entry['HR']
|
67
|
+
groups << 'ict-support@trust-wide.outwood.com' if entry['ICT Support']
|
68
|
+
groups
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.subject_groups(job_title, groups_domain)
|
72
|
+
return [] unless SUBJECT_TEACHER_TITLES.any? { |title| job_title.include?(title) }
|
73
|
+
|
74
|
+
groups = []
|
75
|
+
SUBJECT_MAPPING.each do |group_prefix, subject_words|
|
76
|
+
has_subject =
|
77
|
+
subject_words.any? do |subject|
|
78
|
+
SUBJECT_TEACHER_TITLES.any? { |title| job_title.ends_with? "#{title}#{subject}" }
|
79
|
+
end
|
80
|
+
next unless has_subject
|
81
|
+
|
82
|
+
groups << "#{group_prefix}@#{groups_domain}"
|
83
|
+
end
|
84
|
+
groups
|
85
|
+
end
|
86
|
+
|
87
|
+
def call
|
88
|
+
Execute.multiple(commands(generate_group_assignments), pretend: pretend)
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def commands(group_assignments)
|
94
|
+
group_assignments.find_all { |_user, group| valid_groups.include?(group) }.map do |user, group|
|
95
|
+
Group.add_group_member_command(user: user, group: group)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def valid_groups
|
100
|
+
@valid_groups ||= Group.groups_for_domain("groups.#{domain}") + Group.groups_for_domain('trust-wide.outwood.com')
|
101
|
+
end
|
102
|
+
|
103
|
+
def generate_group_assignments
|
104
|
+
group_assignments = []
|
105
|
+
users.each do |user|
|
106
|
+
group_assignments += group_assignments_for_user(user)
|
107
|
+
end
|
108
|
+
group_assignments
|
109
|
+
end
|
110
|
+
|
111
|
+
def group_assignments_for_user(user)
|
112
|
+
group_assignments = []
|
113
|
+
email = user['primaryEmail']
|
114
|
+
User.job_titles_for_user(user).each do |job_title|
|
115
|
+
entry = approved_job_titles.fetch(job_title, nil)
|
116
|
+
if entry.nil?
|
117
|
+
puts "Warning: Unapproved job title found for user #{email}: #{job_title}"
|
118
|
+
next
|
119
|
+
end
|
120
|
+
|
121
|
+
GroupAssigner.groups_for_job_title(job_title, entry, domain).each do |group|
|
122
|
+
group_assignments << [email, group]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
group_assignments
|
126
|
+
end
|
127
|
+
|
128
|
+
def approved_job_titles
|
129
|
+
@approved_job_titles ||= JobTitle.all
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open-uri'
|
4
|
+
require 'csv'
|
5
|
+
|
6
|
+
module Ogam
|
7
|
+
# Allows working with a list of approved job titles
|
8
|
+
class JobTitle
|
9
|
+
static_facade :all
|
10
|
+
|
11
|
+
CSV_URL = 'https://docs.google.com/spreadsheets/d/12nl5OBGSmOSsWilzWfflmAHO-8EyfCjVXYLeUDqjB60/export?format=csv&id=12nl5OBGSmOSsWilzWfflmAHO-8EyfCjVXYLeUDqjB60&gid=1604665254'
|
12
|
+
|
13
|
+
def self.job_titles(str)
|
14
|
+
return [] if str.blank?
|
15
|
+
|
16
|
+
str.split(' / ')
|
17
|
+
end
|
18
|
+
|
19
|
+
def all
|
20
|
+
@all ||= parse(download)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def download
|
26
|
+
puts 'Fetching list of accepted job titles'
|
27
|
+
URI.parse(CSV_URL).read
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse(str)
|
31
|
+
data = {}
|
32
|
+
CSV.parse(str, headers: true).each do |row|
|
33
|
+
next if row['Job Title'].blank?
|
34
|
+
|
35
|
+
data[row['Job Title']] = entry_for_row(row)
|
36
|
+
end
|
37
|
+
|
38
|
+
data
|
39
|
+
end
|
40
|
+
|
41
|
+
def entry_for_row(row)
|
42
|
+
entry = {}
|
43
|
+
(row.headers - ['Job Title']).each do |col|
|
44
|
+
entry[col] = row[col] == 'TRUE'
|
45
|
+
end
|
46
|
+
entry
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open-uri'
|
4
|
+
require 'csv'
|
5
|
+
|
6
|
+
module Ogam
|
7
|
+
# Allows working with a list of approved organisations
|
8
|
+
class Organisation
|
9
|
+
static_facade :all
|
10
|
+
|
11
|
+
CSV_URL = 'https://docs.google.com/spreadsheets/d/12nl5OBGSmOSsWilzWfflmAHO-8EyfCjVXYLeUDqjB60/export?format=csv&id=12nl5OBGSmOSsWilzWfflmAHO-8EyfCjVXYLeUDqjB60&gid=46181347'
|
12
|
+
|
13
|
+
def all
|
14
|
+
@all ||= parse(download)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def download
|
20
|
+
puts 'Fetching list of accepted organisations'
|
21
|
+
URI.parse(CSV_URL).read
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse(str)
|
25
|
+
data = {}
|
26
|
+
CSV.parse(str, headers: true).each do |row|
|
27
|
+
next if row['Domain'].blank?
|
28
|
+
|
29
|
+
data[row['Domain'].strip] = row['Organisation Name'].strip
|
30
|
+
end
|
31
|
+
|
32
|
+
data
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ogam
|
4
|
+
# Methods relating to signatures
|
5
|
+
class Signature
|
6
|
+
def self.update_signature_command(user)
|
7
|
+
email = user.fetch('primaryEmail')
|
8
|
+
html = signature_html(
|
9
|
+
user.fetch('name.fullName', nil),
|
10
|
+
user.fetch('organizations.0.title', nil),
|
11
|
+
user.fetch('organizations.0.name', nil)
|
12
|
+
)
|
13
|
+
|
14
|
+
"gam user #{email} signature \"#{html}\""
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.signature_html(name, job_title, organisation)
|
18
|
+
parts = [name, job_title, organisation].reject(&:blank?)
|
19
|
+
"<p>#{parts.join('<br />')}</p><hr color=#5b3293 />"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/ogam/user.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ogam
|
4
|
+
# Methods relating to users
|
5
|
+
class User
|
6
|
+
def self.active_for_domain(domain)
|
7
|
+
Ogam::Execute.fetch_csv("gam print users allfields domain #{domain} query 'isSuspended=False'", pretend: false)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.active_by_email(email)
|
11
|
+
Ogam::Execute.fetch_csv(
|
12
|
+
"gam print users allfields query 'email=#{email} isSuspended=False'",
|
13
|
+
pretend: false
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.job_titles_for_user(user)
|
18
|
+
JobTitle.job_titles(user['organizations.0.title'])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/ogam/version.rb
CHANGED
data/lib/ogam.rb
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_support/all'
|
4
|
+
require 'attr_extras'
|
3
5
|
require 'ogam/version'
|
6
|
+
require 'ogam/execute'
|
7
|
+
require 'ogam/group'
|
8
|
+
require 'ogam/job_title'
|
9
|
+
require 'ogam/user'
|
10
|
+
require 'ogam/group_assigner'
|
11
|
+
require 'ogam/signature'
|
12
|
+
require 'ogam/organisation'
|
4
13
|
|
5
14
|
module Ogam
|
6
15
|
class Error < StandardError; end
|
data/ogam.gemspec
CHANGED
@@ -26,11 +26,13 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.executables << 'ogam'
|
27
27
|
spec.require_paths = ['lib']
|
28
28
|
|
29
|
+
spec.add_runtime_dependency 'activesupport', '~> 6.0.2'
|
30
|
+
spec.add_runtime_dependency 'attr_extras', '~> 6.2.3'
|
29
31
|
spec.add_runtime_dependency 'thor', '~> 1.0.1'
|
30
32
|
|
31
33
|
spec.add_development_dependency 'bundler', '~> 2.1'
|
32
34
|
spec.add_development_dependency 'rake', '~> 13.0'
|
33
35
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
34
|
-
spec.add_development_dependency 'rubocop-ogat', '~> 1.
|
36
|
+
spec.add_development_dependency 'rubocop-ogat', '~> 1.47.0'
|
35
37
|
spec.add_development_dependency 'simplecov', '~> 0.17.1'
|
36
38
|
end
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ogam
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Elliot Bowes
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-02-
|
11
|
+
date: 2020-02-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 6.0.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 6.0.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: attr_extras
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 6.2.3
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 6.2.3
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: thor
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,14 +100,14 @@ dependencies:
|
|
72
100
|
requirements:
|
73
101
|
- - "~>"
|
74
102
|
- !ruby/object:Gem::Version
|
75
|
-
version: 1.
|
103
|
+
version: 1.47.0
|
76
104
|
type: :development
|
77
105
|
prerelease: false
|
78
106
|
version_requirements: !ruby/object:Gem::Requirement
|
79
107
|
requirements:
|
80
108
|
- - "~>"
|
81
109
|
- !ruby/object:Gem::Version
|
82
|
-
version: 1.
|
110
|
+
version: 1.47.0
|
83
111
|
- !ruby/object:Gem::Dependency
|
84
112
|
name: simplecov
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -117,6 +145,13 @@ files:
|
|
117
145
|
- bin/ogam
|
118
146
|
- bin/setup
|
119
147
|
- lib/ogam.rb
|
148
|
+
- lib/ogam/execute.rb
|
149
|
+
- lib/ogam/group.rb
|
150
|
+
- lib/ogam/group_assigner.rb
|
151
|
+
- lib/ogam/job_title.rb
|
152
|
+
- lib/ogam/organisation.rb
|
153
|
+
- lib/ogam/signature.rb
|
154
|
+
- lib/ogam/user.rb
|
120
155
|
- lib/ogam/version.rb
|
121
156
|
- ogam.gemspec
|
122
157
|
homepage: https://github.com/Outwood/ogam
|
@@ -139,7 +174,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
174
|
- !ruby/object:Gem::Version
|
140
175
|
version: '0'
|
141
176
|
requirements: []
|
142
|
-
|
177
|
+
rubyforge_project:
|
178
|
+
rubygems_version: 2.7.7
|
143
179
|
signing_key:
|
144
180
|
specification_version: 4
|
145
181
|
summary: Wrapper around GAM for common tasks with OGAT's G Suite instance.
|