platforms-core 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +106 -0
- data/Rakefile +18 -0
- data/app/models/platforms/application_record.rb +8 -0
- data/app/models/platforms/certificate.rb +41 -0
- data/app/models/platforms/group.rb +26 -0
- data/app/models/platforms/group_member.rb +54 -0
- data/app/models/platforms/network.rb +49 -0
- data/app/models/platforms/tag.rb +51 -0
- data/app/models/platforms/user.rb +49 -0
- data/app/validators/platforms/product_validator.rb +54 -0
- data/db/migrate/20200308091104_create_platforms_networks.rb +16 -0
- data/db/migrate/20200308091113_create_platforms_users.rb +16 -0
- data/db/migrate/20200308091123_create_platforms_tags.rb +12 -0
- data/db/migrate/20200308091132_create_platforms_groups.rb +13 -0
- data/db/migrate/20200308091141_create_platforms_group_members.rb +13 -0
- data/db/migrate/20200313102128_create_platforms_certificates.rb +12 -0
- data/lib/platforms/core/configuration.rb +42 -0
- data/lib/platforms/core/engine.rb +26 -0
- data/lib/platforms/core/o_auth_2.rb +29 -0
- data/lib/platforms/core/omni_auth_setup.rb +53 -0
- data/lib/platforms/core/version.rb +7 -0
- data/lib/platforms/core.rb +18 -0
- data/lib/tasks/platforms/core_tasks.rake +4 -0
- metadata +241 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0d9a611c46fa4c038a9db473fea02df69b9edd694ab55c16a67aeef414ac9781
|
4
|
+
data.tar.gz: b7b810c1215f79251ba3b3aa5fe765492a1ba1d2a8e654a0fa1ea8f143ecefa9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e4a25cf6f11910d9b9779356e669c88714de557efa3c3ecaa69a7ba3e9ea389afe9d31aafcb68ee5f2c38d0991ef8e4f57aabf167117f5df1800669bac95763b
|
7
|
+
data.tar.gz: f3ed523de72afb35712e71ac52df053a0522d10e6261a758b2513cdca710e2c488ffb16510199c60989e001676617f4279023c3c653e3ffffb3e302d1bd12dde
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2020 Benjamin Elias
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# Platforms::Core
|
2
|
+
This is a Ruby library which brings together open source and (at least
|
3
|
+
initially) Microsoft platforms. There are various gems out there to
|
4
|
+
do OAuth2 authentication, REST calls, and so on. However, this brings
|
5
|
+
everything together in one easy-to-use package.
|
6
|
+
|
7
|
+
The goal is to create a minimal representation of the external data service in order to build fuller integrations.
|
8
|
+
There is no particular integration in mind, so Platforms::Core should be as generic as possible to enable
|
9
|
+
different platforms together.
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
The Core gem is typically not used on its own. Instead use it in conjunction with one (or more!) platform-specific
|
14
|
+
libraries. Those should require Platforms::Core themselves.
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'platforms-core'
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
```bash
|
26
|
+
$ bundle
|
27
|
+
```
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
```bash
|
32
|
+
$ gem install platforms-core
|
33
|
+
```
|
34
|
+
|
35
|
+
Once the gem is installed, from your Rails directory you will need to install the gem's migrations:
|
36
|
+
|
37
|
+
```bash
|
38
|
+
$ rake platforms_core:install:migrations
|
39
|
+
```
|
40
|
+
|
41
|
+
## Configuration
|
42
|
+
|
43
|
+
REST-based APIs require authentication to get started. Please refer to
|
44
|
+
your platform for instructions on how to do this.
|
45
|
+
|
46
|
+
The gem assumes that you have a class called `User` and a class called `Network` which might look something like the following:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
class User < ApplicationRecord
|
50
|
+
|
51
|
+
# A User belongs to a Platforms::User
|
52
|
+
belongs_to :platforms_user,
|
53
|
+
class_name: "Platforms::User",
|
54
|
+
inverse_of: :app_user
|
55
|
+
|
56
|
+
# Ensure a User only maps to one Platforms::User
|
57
|
+
validates :platforms_user_id, uniqueness: true
|
58
|
+
end
|
59
|
+
```
|
60
|
+
and Network:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
class Network < ApplicationRecord
|
64
|
+
|
65
|
+
# A Network belongs to a Platforms::Network
|
66
|
+
belongs_to :platforms_network,
|
67
|
+
class_name: "Platforms::Network",
|
68
|
+
inverse_of: :app_network
|
69
|
+
|
70
|
+
# Ensure a Network only maps to one Platforms::Network
|
71
|
+
validates :platforms_network_id, uniqueness: true
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
If you need the User and Network classes to be named differently,
|
76
|
+
use configurations:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
# config/initializers/platforms.rb
|
80
|
+
|
81
|
+
Platforms::Core.configure do |config|
|
82
|
+
config.network_class = "SampleNetwork"
|
83
|
+
config.user_class = "SampleUser"
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
## Documentation
|
88
|
+
|
89
|
+
You can generate the documentation by running
|
90
|
+
|
91
|
+
```bash
|
92
|
+
$ rake yard
|
93
|
+
```
|
94
|
+
|
95
|
+
If not everything is documented, check this with:
|
96
|
+
```bash
|
97
|
+
$ yard stats --list-undoc
|
98
|
+
```
|
99
|
+
|
100
|
+
|
101
|
+
## Contributing
|
102
|
+
|
103
|
+
Please see [CONTRIBUTING.md](CONTRIBUTING.md).
|
104
|
+
|
105
|
+
## License
|
106
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'yard'
|
8
|
+
YARD::Rake::YardocTask.new do |t|
|
9
|
+
end
|
10
|
+
|
11
|
+
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
12
|
+
load 'rails/tasks/engine.rake'
|
13
|
+
|
14
|
+
load 'rails/tasks/statistics.rake'
|
15
|
+
|
16
|
+
require 'bundler/gem_tasks'
|
17
|
+
|
18
|
+
task :default => ["app:spec"]
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module Platforms
|
2
|
+
# An abstract class for the Platform models to inherit from
|
3
|
+
# Note that these are not namespaced to Platforms::Core, for
|
4
|
+
# convenience of the Engine's parent application.
|
5
|
+
class ApplicationRecord < ActiveRecord::Base
|
6
|
+
self.abstract_class = true
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Platforms
|
2
|
+
# Stores customer-specific certificates for authentication
|
3
|
+
class Certificate < ApplicationRecord
|
4
|
+
|
5
|
+
self.table_name = "platforms_certificates"
|
6
|
+
|
7
|
+
# OmniAuth::OAuth2 client ID must exist
|
8
|
+
validates :client_id, presence: true
|
9
|
+
|
10
|
+
# OmniAuth::OAuth2 client secret must exist
|
11
|
+
validates :client_secret, presence: true
|
12
|
+
|
13
|
+
# The strategy, according to OmniAuth. This allows a customer to
|
14
|
+
# have multiple vendor platforms.
|
15
|
+
validates :strategy, presence: true
|
16
|
+
|
17
|
+
# Name must exist and be unique
|
18
|
+
validates :name, presence: true, uniqueness: true
|
19
|
+
|
20
|
+
validates :strategy, 'platforms/product' => true
|
21
|
+
|
22
|
+
# Show a redacted client_secret for admin purposes.
|
23
|
+
# If the client_secret is too short, redacts entirely.
|
24
|
+
# Otherwise shows the last 3 characters.
|
25
|
+
# @return [String] redacted client_secret
|
26
|
+
def client_secret_privacy
|
27
|
+
return nil if client_secret.nil?
|
28
|
+
return "*****" if client_secret.length < 10
|
29
|
+
"*****#{client_secret.last(3)}"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Credentials formatted for OmniAuth
|
33
|
+
# @return [Hash] with keys of client_id and client_secret
|
34
|
+
def credentials
|
35
|
+
{
|
36
|
+
client_id: client_id,
|
37
|
+
client_secret: client_secret
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Platforms
|
2
|
+
# Used to define a collection of {Platforms::User}, at a level below
|
3
|
+
# a {Platforms::Network}. This a group, community, team, etc.
|
4
|
+
#
|
5
|
+
# @author Benjamin Elias
|
6
|
+
# @since 0.1.0
|
7
|
+
class Group < ApplicationRecord
|
8
|
+
|
9
|
+
self.table_name = "platforms_groups"
|
10
|
+
|
11
|
+
has_many :platforms_group_members,
|
12
|
+
class_name: "Platforms::GroupMember",
|
13
|
+
inverse_of: :platforms_group,
|
14
|
+
dependent: :destroy
|
15
|
+
|
16
|
+
# A group must belong to a {Platforms::Network}
|
17
|
+
belongs_to :platforms_network, class_name: "Platforms::Network"
|
18
|
+
|
19
|
+
# Name, Platform ID, and {Platforms::Network} must exist
|
20
|
+
validates :name, :platform_id, :platforms_network, presence: true
|
21
|
+
|
22
|
+
# Platform ID is unique, for that {Platforms::Network}
|
23
|
+
validates :platform_id, uniqueness: { scope: :platforms_network }
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Platforms
|
2
|
+
# Join table between {Platforms::Group} and {Platforms::User},
|
3
|
+
# to indicate membership.
|
4
|
+
#
|
5
|
+
# Includes a role, to indicate more specific membership as defined by
|
6
|
+
# the platform.
|
7
|
+
class GroupMember < ApplicationRecord
|
8
|
+
|
9
|
+
self.table_name = "platforms_group_members"
|
10
|
+
|
11
|
+
# Belongs to a {Platforms::User}
|
12
|
+
belongs_to :platforms_user, class_name: "Platforms::User"
|
13
|
+
|
14
|
+
# Belongs to a {Platforms::Group}
|
15
|
+
belongs_to :platforms_group, class_name: "Platforms::Group"
|
16
|
+
|
17
|
+
# Require valid {Platforms::User}
|
18
|
+
validates :platforms_user, presence: true
|
19
|
+
|
20
|
+
# Require valid {Platforms::Group}
|
21
|
+
validates :platforms_group, presence: true
|
22
|
+
|
23
|
+
# Require role to exist, must be in the following enum:
|
24
|
+
# * member 0
|
25
|
+
# * admin 1
|
26
|
+
# * read-only 2
|
27
|
+
validates :role, presence: true
|
28
|
+
|
29
|
+
# Ensure [{Platforms::User}, {Platforms::Group}] keys are unique
|
30
|
+
validates :platforms_user, uniqueness: {scope: :platforms_group}
|
31
|
+
|
32
|
+
# Network of #platforms_user is equal to that of #platforms_group
|
33
|
+
validate :platforms_network_match
|
34
|
+
|
35
|
+
# Role indicates admin rights
|
36
|
+
enum role: { member: 0, admin: 1, read_only: 2 }
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# {Network} of {#platforms_user} is equal to that of #platforms_group
|
41
|
+
#
|
42
|
+
# Checks the {Network} of platforms_user against the {Network} of
|
43
|
+
# platforms_group and confirms they are the same.
|
44
|
+
def platforms_network_match
|
45
|
+
delegated = platforms_user.platforms_network_id rescue nil
|
46
|
+
alternate = platforms_group.platforms_network_id rescue nil
|
47
|
+
|
48
|
+
unless delegated == alternate and !delegated.nil?
|
49
|
+
errors.add(:network, "IDs not equal")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Platforms
|
2
|
+
# The basic network concept for a platform. Users are grouped together on
|
3
|
+
# within an information boundary. Could be a 'tenant'.
|
4
|
+
class Network < ApplicationRecord
|
5
|
+
|
6
|
+
self.table_name = "platforms_networks"
|
7
|
+
|
8
|
+
# Has a Network from the parent app
|
9
|
+
has_one :app_network,
|
10
|
+
class_name: Platforms::Core.configuration.network_class,
|
11
|
+
foreign_key: "platforms_network_id"
|
12
|
+
|
13
|
+
# {Platforms::Group} belong to this network
|
14
|
+
has_many :platforms_groups, class_name: "Platforms::Group"
|
15
|
+
|
16
|
+
# {Platforms::User} belong to this network
|
17
|
+
has_many :platforms_users, class_name: "Platforms::User"
|
18
|
+
|
19
|
+
# {Platforms::Tag} belong to this network
|
20
|
+
has_many :platforms_tags, class_name: "Platforms::Tag"
|
21
|
+
|
22
|
+
# Name of a network must exist.
|
23
|
+
validates :name, presence: true
|
24
|
+
|
25
|
+
# Permalink of the network must exist. Mostly for Yammer, but
|
26
|
+
# also applicable to Office 365. Should default to name if
|
27
|
+
# no concept is defined by the platform.
|
28
|
+
validates :permalink, presence: true
|
29
|
+
|
30
|
+
# Must have a platform type, which is effectively the Engine
|
31
|
+
# that the network should authenticated against.
|
32
|
+
validates :platform_type, presence: true
|
33
|
+
|
34
|
+
# Do not require app_network to exist, as that is created after
|
35
|
+
# Do not require certificate to exist, as the Certificate may not be
|
36
|
+
# saved in the database (if it is the default).
|
37
|
+
|
38
|
+
# Platform exists and is unique for each platform
|
39
|
+
validates :platform_id,
|
40
|
+
presence: true,
|
41
|
+
uniqueness: { scope: :platform_type }
|
42
|
+
|
43
|
+
# Trial exists as boolean
|
44
|
+
validates :trial, inclusion: { in: [true, false] }
|
45
|
+
|
46
|
+
# Platform is known (can be extended if the Platforms::NewProduct exists)
|
47
|
+
validates :platform_type, 'platforms/product' => true
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Platforms
|
2
|
+
# A Tag within a {Platforms::Network}. Typically a hashtag or similar,
|
3
|
+
# which may not be used in some platforms.
|
4
|
+
class Tag < ApplicationRecord
|
5
|
+
include Twitter::TwitterText::Extractor
|
6
|
+
|
7
|
+
self.table_name = "platforms_tags"
|
8
|
+
|
9
|
+
# Do not use the same strategy as Network/User here
|
10
|
+
# as a Platforms::Tag does not need to has_one (or has_many)
|
11
|
+
# Application-level tags.
|
12
|
+
|
13
|
+
# Belongs to a {Platforms::Network}
|
14
|
+
belongs_to :platforms_network, class_name: "Platforms::Network"
|
15
|
+
|
16
|
+
# Must have an identifying name
|
17
|
+
validates :name, presence: true
|
18
|
+
|
19
|
+
# The {Platforms::Network} must exist
|
20
|
+
validates :platforms_network, presence: true
|
21
|
+
|
22
|
+
# Does not need to validate platform_id, as it could be nil on
|
23
|
+
# the first insert (when creating it as part of some content, for example)
|
24
|
+
|
25
|
+
# platform_id is unique to the {Platforms::Network}. Should be able to have
|
26
|
+
# the same tag on different Networks, and not cause validation errors.
|
27
|
+
validates :platform_id,
|
28
|
+
uniqueness: {scope: :platforms_network},
|
29
|
+
allow_nil: true
|
30
|
+
|
31
|
+
# name is unique to the {Platforms::Network}. Should be able to have
|
32
|
+
# the same tag on different Networks, and not cause validation errors.
|
33
|
+
validates :name,
|
34
|
+
uniqueness: {scope: :platforms_network},
|
35
|
+
allow_nil: true
|
36
|
+
|
37
|
+
# Hashtag syntax is actually quite complicated. Stick to Twitter rules.
|
38
|
+
validate :tag_syntax
|
39
|
+
|
40
|
+
# Minimum of 2 characters (including "#")
|
41
|
+
validates :name, length: {minimum: 2}
|
42
|
+
|
43
|
+
private
|
44
|
+
def tag_syntax
|
45
|
+
expected = [self.name.sub(/\A#/, '')]
|
46
|
+
errors.add(:name, :format_hashtag) unless \
|
47
|
+
extract_hashtags(name) == expected
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Platforms
|
2
|
+
# A User in a {Platforms::Network}. Serves the purpose of a colleague,
|
3
|
+
# but also for authentication identity.
|
4
|
+
class User < ApplicationRecord
|
5
|
+
|
6
|
+
self.table_name = "platforms_users"
|
7
|
+
|
8
|
+
# Has a User from the parent app
|
9
|
+
# Do not require parent app's User to exist, as that is created after.
|
10
|
+
has_one :app_user,
|
11
|
+
class_name: Platforms::Core.configuration.user_class,
|
12
|
+
foreign_key: "platforms_user_id"
|
13
|
+
|
14
|
+
# Belongs to a {Platforms::Network}
|
15
|
+
belongs_to :platforms_network,
|
16
|
+
class_name: "Platforms::Network"
|
17
|
+
|
18
|
+
# Links to Groups through {Platforms::GroupMember}.
|
19
|
+
has_many :platforms_group_members,
|
20
|
+
class_name: "Platforms::GroupMember",
|
21
|
+
inverse_of: :platforms_user,
|
22
|
+
dependent: :destroy
|
23
|
+
|
24
|
+
# Name must exist for display
|
25
|
+
validates :name, presence: true
|
26
|
+
|
27
|
+
# An identifier on the platform, does not have to be numeric.
|
28
|
+
validates :platform_id, presence: true
|
29
|
+
|
30
|
+
# The user's thumbnail photo as a URL
|
31
|
+
validates :thumbnail_url, presence: true
|
32
|
+
|
33
|
+
# A URL related to the user
|
34
|
+
validates :web_url, presence: true
|
35
|
+
|
36
|
+
# Must have some contact details
|
37
|
+
validates :email, presence: true
|
38
|
+
|
39
|
+
# Has a related {Platforms::Network}
|
40
|
+
validates :platforms_network, presence: true
|
41
|
+
|
42
|
+
# Platform ID must be unique
|
43
|
+
validates :platform_id, uniqueness: { scope: :platforms_network_id }
|
44
|
+
|
45
|
+
# Must record whether the user is an admin on the platform
|
46
|
+
validates :admin, inclusion: { in: [true, false] }
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Platforms
|
2
|
+
|
3
|
+
# Checks if a record includes an attribute with value blah, and
|
4
|
+
# the Platforms::Blah namespace includes a class which inherits
|
5
|
+
# from Rails::Engine. That is usually called Platforms::Blah::Engine.
|
6
|
+
#
|
7
|
+
# Adds a validation error if:
|
8
|
+
# * Platforms::Blah does not exist
|
9
|
+
# * Platforms::Blah does not have a class that inherits from Rails::Engine
|
10
|
+
#
|
11
|
+
class ProductValidator < ActiveModel::EachValidator
|
12
|
+
|
13
|
+
# Validate record includes an attribute with the value of a known Engine.
|
14
|
+
#
|
15
|
+
# Checks if a record includes an attribute with value blah, and
|
16
|
+
# the Platforms::Blah namespace includes a class which inherits
|
17
|
+
# from Rails::Engine. That is usually called Platforms::Blah::Engine.
|
18
|
+
#
|
19
|
+
# If Platforms::Blah does not include a known Engine, add an error.
|
20
|
+
#
|
21
|
+
# Also requires that value is lowercase.
|
22
|
+
#
|
23
|
+
# @param [ActiveModel] record being validated
|
24
|
+
# @param [Symbol] attribute name of record
|
25
|
+
# @param [Object] value of record.attribute
|
26
|
+
# @return no suitable return value, just adds errors to record.
|
27
|
+
def validate_each(record, attribute, value)
|
28
|
+
return if value.blank?
|
29
|
+
|
30
|
+
unless value == value.downcase
|
31
|
+
record.errors.add(attribute, "should be lower case")
|
32
|
+
return
|
33
|
+
end
|
34
|
+
|
35
|
+
kamel = value.camelize
|
36
|
+
|
37
|
+
begin
|
38
|
+
klass = Platforms.const_get(kamel)
|
39
|
+
children = klass.constants(false)
|
40
|
+
children.select! do |k|
|
41
|
+
# Do not allow non-Class looking things like String constants
|
42
|
+
next unless klass.const_get(k).respond_to? :ancestors
|
43
|
+
klass.const_get(k).ancestors.include? Rails::Engine
|
44
|
+
end
|
45
|
+
unless children.length == 1
|
46
|
+
record.errors.add(attribute, "Platforms::#{kamel} is not a Rails::Engine")
|
47
|
+
end
|
48
|
+
rescue NameError => e
|
49
|
+
record.errors.add(attribute, e)
|
50
|
+
return
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreatePlatformsNetworks < ActiveRecord::Migration[6.0]
|
2
|
+
def change
|
3
|
+
create_table :platforms_networks, force: :cascade do |t|
|
4
|
+
t.string :platform_id
|
5
|
+
t.string :platform_type
|
6
|
+
t.string :name
|
7
|
+
t.string :permalink
|
8
|
+
t.boolean :trial
|
9
|
+
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
add_index :platforms_networks, :platform_id
|
13
|
+
add_index :platforms_networks, [:platform_id, :platform_type],
|
14
|
+
name: "platforms_ids"
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreatePlatformsUsers < ActiveRecord::Migration[6.0]
|
2
|
+
def change
|
3
|
+
create_table :platforms_users do |t|
|
4
|
+
t.string :platform_id
|
5
|
+
t.string :name
|
6
|
+
t.string :thumbnail_url
|
7
|
+
t.boolean :admin
|
8
|
+
t.string :web_url
|
9
|
+
t.string :email
|
10
|
+
t.integer :platforms_network_id
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
add_index :platforms_users, [:platform_id, :platforms_network_id]
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class CreatePlatformsTags < ActiveRecord::Migration[6.0]
|
2
|
+
def change
|
3
|
+
create_table :platforms_tags do |t|
|
4
|
+
t.string :platform_id
|
5
|
+
t.string :name
|
6
|
+
t.integer :platforms_network_id
|
7
|
+
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
add_index :platforms_tags, [:platform_id, :platforms_network_id]
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreatePlatformsGroups < ActiveRecord::Migration[6.0]
|
2
|
+
def change
|
3
|
+
create_table :platforms_groups do |t|
|
4
|
+
t.string :platform_id
|
5
|
+
t.string :name
|
6
|
+
t.integer :platforms_network_id
|
7
|
+
t.string :web_url
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
add_index :platforms_groups, [:platform_id, :platforms_network_id]
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreatePlatformsGroupMembers < ActiveRecord::Migration[6.0]
|
2
|
+
def change
|
3
|
+
create_table :platforms_group_members do |t|
|
4
|
+
t.integer :platforms_group_id
|
5
|
+
t.integer :platforms_user_id
|
6
|
+
t.integer :role
|
7
|
+
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
add_index :platforms_group_members, [:platforms_group_id, :platforms_user_id],
|
11
|
+
name: "platforms_group_members_index"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Platforms
|
2
|
+
module Core
|
3
|
+
|
4
|
+
# Configuration pattern for Rails, allows defaults and initializers to
|
5
|
+
# override those defaults.
|
6
|
+
# @see https://stackoverflow.com/a/24151439 Stack Overflow
|
7
|
+
class << self
|
8
|
+
|
9
|
+
# Get the configuration
|
10
|
+
# @return [Platforms::Configuration] the current configuration
|
11
|
+
def configuration
|
12
|
+
@configuration ||= Configuration.new
|
13
|
+
end
|
14
|
+
|
15
|
+
# Used by initializers to set the configuration
|
16
|
+
# @yield [configuration] provides the configuration context
|
17
|
+
def configure
|
18
|
+
yield configuration
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# The class which stores the gem's configuration
|
23
|
+
# Should only store core configuration, not related to each product.
|
24
|
+
class Configuration
|
25
|
+
include ActiveSupport::Configurable
|
26
|
+
|
27
|
+
##
|
28
|
+
# The default name for the main application's user class.
|
29
|
+
# @see Platforms::User
|
30
|
+
# (defaults: ::User)
|
31
|
+
config_accessor(:user_class) { "::User" }
|
32
|
+
|
33
|
+
|
34
|
+
##
|
35
|
+
# The default name for the main application's network class.
|
36
|
+
# @see Platforms::Network
|
37
|
+
# (defaults: ::Network)
|
38
|
+
config_accessor(:network_class) { "::Network" }
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'twitter-text'
|
2
|
+
|
3
|
+
module Platforms
|
4
|
+
module Core
|
5
|
+
# Isolated Rails Engine
|
6
|
+
# @see https://guides.rubyonrails.org/engines.html
|
7
|
+
class Engine < ::Rails::Engine
|
8
|
+
isolate_namespace Platforms::Core
|
9
|
+
|
10
|
+
config.generators do |g|
|
11
|
+
g.test_framework :rspec
|
12
|
+
g.fixture_replacement :factory_bot
|
13
|
+
g.factory_bot dir: 'spec/factories'
|
14
|
+
end
|
15
|
+
|
16
|
+
initializer "platforms.factories", :after => "factory_bot.set_factory_paths" do
|
17
|
+
FactoryBot.definition_file_paths << File.expand_path('../../../spec/factories', __FILE__) if defined?(FactoryBot)
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
# https://tanzu.vmware.com/content/blog/leave-your-migrations-in-your-rails-engines
|
22
|
+
# Note: This is problematic for Travis CI, which runs migrations in rake and not rails
|
23
|
+
# This has therefore been removed.
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Platforms
|
2
|
+
module Core
|
3
|
+
|
4
|
+
# This is a module for common authentication methods, across multiple
|
5
|
+
# platforms.
|
6
|
+
# @todo extract out the common functionality.
|
7
|
+
module OAuth2
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
# Get the token from the OmniAuth credentials store
|
11
|
+
# A convenience method.
|
12
|
+
# @return the OmniAuth auth token
|
13
|
+
def token
|
14
|
+
request.env["omniauth.auth"].credentials.token
|
15
|
+
end
|
16
|
+
|
17
|
+
# Sometimes the return value is a string "true", while others it is
|
18
|
+
# cast as a boolean. This normalises that behaviour to true or false.
|
19
|
+
# Any non-"true" String value should return false.
|
20
|
+
# @param val [Object] value to convert
|
21
|
+
# @return boolean equivalent
|
22
|
+
def bool_safe val
|
23
|
+
return val == "true" if val.is_a? String
|
24
|
+
val.eql?(true)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Platforms
|
2
|
+
module Core
|
3
|
+
|
4
|
+
# Handles the dynamic insertion of client ID and Client Secret
|
5
|
+
# into the OmniAuth regime.
|
6
|
+
#
|
7
|
+
# @author Benjamin Elias
|
8
|
+
# @since 0.1.0
|
9
|
+
# @see https://www.createdbypete.com/dynamic-omniauth-provider-setup/ Dynamic providers.
|
10
|
+
# @see https://github.com/omniauth/omniauth/wiki/Setup-Phase OmniAuth Setup Phase
|
11
|
+
class OmniAuthSetup
|
12
|
+
|
13
|
+
# OmniAuth expects the class passed to setup to respond to the #call
|
14
|
+
# method.
|
15
|
+
# @param env [Hash] Rack environment
|
16
|
+
def self.call(env)
|
17
|
+
new(env).setup
|
18
|
+
end
|
19
|
+
|
20
|
+
# Assign variables and create a request object for use later.
|
21
|
+
# env - Rack environment
|
22
|
+
def initialize(env)
|
23
|
+
@env = env
|
24
|
+
@request = ActionDispatch::Request.new(env)
|
25
|
+
end
|
26
|
+
|
27
|
+
# The main purpose of this method is to set the consumer key and secret.
|
28
|
+
def setup
|
29
|
+
@env['omniauth.strategy'].options.merge!(find_credentials)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Use the subdomain in the request to find the account with credentials
|
33
|
+
def find_credentials
|
34
|
+
strategy = @env['omniauth.strategy'].options.name
|
35
|
+
subdomain = ActionDispatch::Http::URL.extract_subdomains(@env['SERVER_NAME'], 0).first
|
36
|
+
|
37
|
+
certificate = Certificate.find_by(
|
38
|
+
strategy: strategy,
|
39
|
+
name: subdomain
|
40
|
+
)
|
41
|
+
# If subdomain-specific certificate is not found, use default
|
42
|
+
certificate ||= default_certificate
|
43
|
+
return certificate.credentials
|
44
|
+
end
|
45
|
+
|
46
|
+
# As a placeholder, return a blank certificate
|
47
|
+
def default_certificate
|
48
|
+
Certificate.new
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "platforms/core/configuration"
|
2
|
+
require "platforms/core/engine"
|
3
|
+
require "platforms/core/omni_auth_setup"
|
4
|
+
require "platforms/core/o_auth_2"
|
5
|
+
|
6
|
+
# The top level namespace for Platforms, which includes Core and
|
7
|
+
# other vendor-specific implementations.
|
8
|
+
module Platforms
|
9
|
+
# Common functionality across all Platforms should go in the Core module.
|
10
|
+
#
|
11
|
+
# That includes minimal storage of external data, which should of course
|
12
|
+
# be limited to ensure ongoing consistency. For example, Yammer has the
|
13
|
+
# concept of a hashtag, even though Teams does not. The representation
|
14
|
+
# sits in Platforms::Core (not Platforms::Yammer) as it is replicating
|
15
|
+
# an external data object.
|
16
|
+
module Core
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,241 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: platforms-core
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Benjamin Elias
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-04-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 6.0.2
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 6.0.2.1
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 6.0.2
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 6.0.2.1
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: twitter-text
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '3.0'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3.0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: omniauth
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '1.0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: sqlite3
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rspec-rails
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: factory_bot_rails
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: hashie
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: simplecov
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
type: :development
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
- !ruby/object:Gem::Dependency
|
132
|
+
name: webmock
|
133
|
+
requirement: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
type: :development
|
139
|
+
prerelease: false
|
140
|
+
version_requirements: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
- !ruby/object:Gem::Dependency
|
146
|
+
name: sinatra
|
147
|
+
requirement: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
type: :development
|
153
|
+
prerelease: false
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
- !ruby/object:Gem::Dependency
|
160
|
+
name: yard
|
161
|
+
requirement: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - ">="
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: '0'
|
173
|
+
- !ruby/object:Gem::Dependency
|
174
|
+
name: yard-activerecord
|
175
|
+
requirement: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
type: :development
|
181
|
+
prerelease: false
|
182
|
+
version_requirements: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
187
|
+
description: Login to Office 365 Teams, Yammer.
|
188
|
+
email:
|
189
|
+
- ben@collabital.com
|
190
|
+
executables: []
|
191
|
+
extensions: []
|
192
|
+
extra_rdoc_files: []
|
193
|
+
files:
|
194
|
+
- MIT-LICENSE
|
195
|
+
- README.md
|
196
|
+
- Rakefile
|
197
|
+
- app/models/platforms/application_record.rb
|
198
|
+
- app/models/platforms/certificate.rb
|
199
|
+
- app/models/platforms/group.rb
|
200
|
+
- app/models/platforms/group_member.rb
|
201
|
+
- app/models/platforms/network.rb
|
202
|
+
- app/models/platforms/tag.rb
|
203
|
+
- app/models/platforms/user.rb
|
204
|
+
- app/validators/platforms/product_validator.rb
|
205
|
+
- db/migrate/20200308091104_create_platforms_networks.rb
|
206
|
+
- db/migrate/20200308091113_create_platforms_users.rb
|
207
|
+
- db/migrate/20200308091123_create_platforms_tags.rb
|
208
|
+
- db/migrate/20200308091132_create_platforms_groups.rb
|
209
|
+
- db/migrate/20200308091141_create_platforms_group_members.rb
|
210
|
+
- db/migrate/20200313102128_create_platforms_certificates.rb
|
211
|
+
- lib/platforms/core.rb
|
212
|
+
- lib/platforms/core/configuration.rb
|
213
|
+
- lib/platforms/core/engine.rb
|
214
|
+
- lib/platforms/core/o_auth_2.rb
|
215
|
+
- lib/platforms/core/omni_auth_setup.rb
|
216
|
+
- lib/platforms/core/version.rb
|
217
|
+
- lib/tasks/platforms/core_tasks.rake
|
218
|
+
homepage: https://www.collabital.com
|
219
|
+
licenses:
|
220
|
+
- MIT
|
221
|
+
metadata: {}
|
222
|
+
post_install_message:
|
223
|
+
rdoc_options: []
|
224
|
+
require_paths:
|
225
|
+
- lib
|
226
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
227
|
+
requirements:
|
228
|
+
- - ">="
|
229
|
+
- !ruby/object:Gem::Version
|
230
|
+
version: '0'
|
231
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
232
|
+
requirements:
|
233
|
+
- - ">="
|
234
|
+
- !ruby/object:Gem::Version
|
235
|
+
version: '0'
|
236
|
+
requirements: []
|
237
|
+
rubygems_version: 3.0.6
|
238
|
+
signing_key:
|
239
|
+
specification_version: 4
|
240
|
+
summary: Login for various collaboration platforms.
|
241
|
+
test_files: []
|