platforms-core 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|