shimmer 0.0.12 → 0.0.15

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '038bb182d1dad60da8ef884f677bf291baed9ffb2e4a14959cd20e55c43f7ff0'
4
- data.tar.gz: d5f6c1489a4b285202c2d93d3c9bfd7e8a5ac4b3a17688452e3315782b694511
3
+ metadata.gz: 789f3862287d4a4e4ea0ea64429d5855d147853d25a85f896acde0f4a456f805
4
+ data.tar.gz: 5fe592396ee46ae62468f4436835b1a3718fedfe60823287388bbe28cbb51f92
5
5
  SHA512:
6
- metadata.gz: f10bade660c8ef17b62388e7ea9ac5d9b7bf40c658e744a1b9050a70bbabb7e2a360d1d10fdceb6fbffa43a002d94113efb3a794d8c233ad75387840f62d60bd
7
- data.tar.gz: fbdba908476796302ef9db666285fe57e39a69ae095374d11291cf2ea43c7bfe3bc1c5a6d74c23f2d46e815806ff6f95fe3b1c594834196d7169fe171b605f8a
6
+ metadata.gz: 3ca1e5c4a38b5b36da2b9cc20bae29c2a20df60550f8e9904db8de6cca92aea4c02f62cdb57d1c72cbfb0b4d660b2714ac3051e6c9eb8996914d954ba5ec14a4
7
+ data.tar.gz: 50d5ffc3cac315ad169da4da82c621338b565598919930a3f3f72bd32f0321d2c781ba0598020762aaa5492b96a3988d6377ded829e4c650e4ebb3fb48361d5b
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ class AppleProvider < Provider
6
+ self.token_column = :apple_id
7
+
8
+ private
9
+
10
+ def request_details(params)
11
+ name = params[:user] ? JSON.parse(params[:user])["name"] : {}
12
+ headers = {
13
+ 'Content-Type': "application/x-www-form-urlencoded"
14
+ }
15
+ form = {
16
+ grant_type: "authorization_code",
17
+ code: params[:code],
18
+ client_id: Config.instance.apple_bundle_id!,
19
+ client_secret: Config.instance.apple_client_secret,
20
+ scope: "name email"
21
+ }
22
+ response = HTTParty.post("https://appleid.apple.com/auth/token", body: URI.encode_www_form(form), headers: headers)
23
+ raise InvalidTokenError, "Login check failed: #{response.body}" unless response.ok?
24
+
25
+ token = JWT.decode(response["id_token"], nil, false).first
26
+ UserDetails.new token: token["sub"], email: token["email"], first_name: name["firstName"], last_name: name["lastName"]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ module Authenticating
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ before_action :authenticate
10
+ helper_method :current_user
11
+
12
+ def require_login
13
+ redirect_to login_path unless current_user
14
+ end
15
+
16
+ def current_user
17
+ ::Current.user
18
+ end
19
+
20
+ def login(device:)
21
+ ::Current.device = device
22
+ cookies.encrypted[:device_token] = {value: device.token, expires: 2.years.from_now}
23
+ end
24
+
25
+ def logout
26
+ cookies.delete :device_token
27
+ end
28
+
29
+ private
30
+
31
+ def authenticate
32
+ ::Current.device = cookies.encrypted[:device_token].presence&.then { |e| ::Device.find_by token: e }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ module Current
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attribute :device
10
+
11
+ delegate :user, to: :device, allow_nil: true
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ class DevProvider < Provider
6
+ def login(email:, user_agent: nil, ip: nil)
7
+ user = model.find_or_create_by!(email: email)
8
+ device = user.devices.create! user_agent: user_agent
9
+ log_login(user, device_id: device.id, user_agent: user_agent, ip: ip)
10
+ device
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ module Device
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ has_secure_token
10
+
11
+ def name
12
+ [browser.platform.name, browser.name].join(" ")
13
+ end
14
+
15
+ private
16
+
17
+ def browser
18
+ @browser ||= Browser.new user_agent
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ class GoogleProvider < Provider
6
+ self.token_column = :google_id
7
+
8
+ private
9
+
10
+ def request_details(params)
11
+ payload = GoogleIDToken::Validator.new.check(params[:credential], Config.instance.google_client_id!)
12
+ UserDetails.new token: payload["sub"], email: payload["email"], first_name: payload["given_name"].presence, last_name: payload["family_name"].presence
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ module User
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ def authenticate!(user_agent: nil, ip: nil)
10
+ Provider.new(self.class).create_device(user: self, user_agent: user_agent, ip: ip)
11
+ end
12
+ end
13
+
14
+ class_methods do
15
+ def login!(provider:, **attributes)
16
+ "Shimmer::Auth::#{provider.to_s.classify}Provider".constantize
17
+ .new(self).login(**attributes)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module Auth
5
+ class Provider
6
+ class InvalidTokenError < StandardError; end
7
+ UserDetails = Struct.new(:token, :email, :first_name, :last_name, keyword_init: true)
8
+ attr_reader :model
9
+ cattr_accessor :token_column
10
+
11
+ def initialize(model)
12
+ @model = model
13
+ end
14
+
15
+ def login(params:, user_agent: nil, ip: nil)
16
+ user = fetch_user request_details(params)
17
+ create_device user: user, user_agent: user_agent, ip: ip
18
+ end
19
+
20
+ def create_device(user:, user_agent: nil, ip: nil)
21
+ user.devices.create!(user_agent: user_agent).tap do |device|
22
+ log_login(user, device_id: device.id, user_agent: user_agent, ip: ip)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def log_login(user, device_id:, user_agent: nil, ip: nil)
29
+ return unless user.respond_to? :publish
30
+
31
+ user.publish :login, provider: self.class.name.demodulize.underscore, device_id: device_id, user_agent: user_agent, ip: ip
32
+ end
33
+
34
+ def fetch_user(details)
35
+ user = model.find_by(token_column => details.token) || model.find_by(email: details.email) || model.new
36
+ user[token_column] ||= details.token
37
+ user.email ||= details.email
38
+ user.first_name ||= details.first_name
39
+ user.last_name ||= details.last_name
40
+ user.save! if user.changed?
41
+ user
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ Dir["#{File.expand_path("./auth", __dir__)}/*"].sort.each { |e| require e }
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ module MetaHelper
5
+ def meta
6
+ @meta ||= Meta.new.tap do |meta|
7
+ meta.canonical = url_for(only_path: false)
8
+ end
9
+ end
10
+
11
+ def title(value)
12
+ meta.title = value
13
+ end
14
+
15
+ def description(value)
16
+ meta.description = value
17
+ end
18
+
19
+ def image(value)
20
+ meta.image = image_file_url(value, width: 1200)
21
+ end
22
+
23
+ def render_meta
24
+ tags = meta.tags.map do |tag|
25
+ type = tag.delete(:type) || "meta"
26
+ value = tag.delete(:value)
27
+ content_tag(type, value, tag)
28
+ end
29
+ safe_join tags, "\n"
30
+ end
31
+ end
32
+ end
@@ -8,3 +8,11 @@ module Shimmer
8
8
  end
9
9
  end
10
10
  end
11
+
12
+ ActiveSupport.on_load(:action_view) do
13
+ Dir.glob("#{File.expand_path(__dir__)}/helpers/**/*.rb").each do |file|
14
+ load file
15
+ name = file.split("/").last.delete_suffix(".rb").classify
16
+ include "Shimmer::#{name}".constantize
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :auth do
4
+ desc "Generates a Sign in with Apple Token"
5
+ task :apple_token do
6
+ ecdsa_key = OpenSSL::PKey::EC.new IO.read ".apple-key.p8"
7
+ headers = {
8
+ "kid" => Shimmer::Config.instance.apple_key_id!
9
+ }
10
+ claims = {
11
+ "iss" => Shimmer::Config.instance.apple_team_id!,
12
+ "iat" => Time.now.to_i,
13
+ "exp" => 180.days.from_now.to_i,
14
+ "aud" => "https://appleid.apple.com",
15
+ "sub" => Shimmer::Config.instance.apple_bundle_id!
16
+ }
17
+ puts JWT.encode claims, ecdsa_key, "ES256", headers
18
+ end
19
+ end
@@ -43,4 +43,13 @@ namespace :db do
43
43
 
44
44
  desc "Download all app data, including assets"
45
45
  task pull: [:pull_data, :pull_assets]
46
+
47
+ desc "Migrates if the database has any tables."
48
+ task migrate_if_tables: :environment do
49
+ if ActiveRecord::Base.connection.tables.any?
50
+ Rake::Task["db:migrate"].invoke
51
+ else
52
+ puts "No tables in database yet, skipping migration"
53
+ end
54
+ end
46
55
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :s3 do
4
+ desc "Creates a new S3 bucket and outputs or uploads the credentials."
5
+ task :create_bucket do
6
+ puts "Please enter the name for your new bucket"
7
+ name = $stdin.gets.strip
8
+ region = "eu-central-1"
9
+ sh "aws s3 mb s3://#{name} --region #{region}"
10
+ sh "aws iam create-user --user-name #{name}"
11
+ policy = <<~JSON
12
+ {
13
+ "Version": "2012-10-17",
14
+ "Statement": [
15
+ {
16
+ "Effect": "Allow",
17
+ "Action": [
18
+ "s3:CreateBucket",
19
+ "s3:DeleteObject",
20
+ "s3:Put*",
21
+ "s3:Get*",
22
+ "s3:List*"
23
+ ],
24
+ "Resource": [
25
+ "arn:aws:s3:::#{name}",
26
+ "arn:aws:s3:::#{name}/*"
27
+ ]
28
+ }
29
+ ]
30
+ }
31
+ JSON
32
+ File.write("policy.json", policy)
33
+ sh "aws iam put-user-policy --user-name #{name} --policy-name #{name} --policy-document file://policy.json"
34
+ File.delete("policy.json")
35
+ content = JSON.parse `aws iam create-access-key --user-name #{name}`
36
+ id = content.dig("AccessKey", "AccessKeyId")
37
+ secret = content.dig("AccessKey", "SecretAccessKey")
38
+ puts "Credentials and bucket were generated. Automatically assign them to the associated Heroku project? This will override and delete all current keys on Heroku. (y/n)"
39
+ vars = {AWS_REGION: region, AWS_BUCKET: name, AWS_ACCESS_KEY_ID: id, AWS_SECRET_ACCESS_KEY: secret}
40
+ if $stdin.gets.strip == "y"
41
+ sh "heroku config:set #{vars.map { |k, v| "#{k}=#{v}" }.join(" ")}"
42
+ else
43
+ vars.each { |k, v| puts "#{k}=#{v}" }
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ class Config
5
+ include Singleton
6
+ class MissingConfigError < StandardError; end
7
+
8
+ def method_missing(method_name)
9
+ method_name = method_name.to_s
10
+ type = :string
11
+ key = method_name.delete_suffix("!").delete_suffix("?")
12
+ required = method_name.end_with?("!")
13
+ type = :bool if method_name.end_with?("?")
14
+ value = ENV[key.upcase].presence
15
+ value ||= Rails.application.credentials.send(key)
16
+ raise MissingConfigError, "#{key.upcase} environment value is missing" if required && value.blank?
17
+
18
+ coerce value, type
19
+ end
20
+
21
+ def respond_to_missing?(method_name)
22
+ true
23
+ end
24
+
25
+ private
26
+
27
+ def coerce(value, type)
28
+ return !value.in?(["n", "0", "no", "false"]) if type == :bool && value.is_a?(String)
29
+
30
+ value
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shimmer
4
+ class Meta
5
+ class_attribute :app_name
6
+ attr_accessor :title, :description, :image, :canonical
7
+
8
+ def tags
9
+ tags = []
10
+ title = self.title.present? ? "#{self.title} | #{app_name}" : app_name
11
+ tags.push(type: :title, value: title)
12
+ tags.push(property: "og:title", content: title)
13
+ if description.present?
14
+ tags.push(name: :description, content: description)
15
+ tags.push(property: "og:description", content: description)
16
+ end
17
+ if image.present?
18
+ tags.push(property: "og:image", content: image)
19
+ end
20
+ if canonical.present?
21
+ tags.push(type: :link, rel: :canonical, href: canonical)
22
+ tags.push(property: "og:url", content: canonical)
23
+ end
24
+ tags.push(property: "og:type", content: :website)
25
+ tags.push(property: "og:locale", content: I18n.locale)
26
+ tags.push(name: "twitter:card", content: :summary_large_image)
27
+ tags
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Shimmer
4
- VERSION = "0.0.12"
4
+ VERSION = "0.0.15"
5
5
  end
data/lib/shimmer.rb CHANGED
@@ -6,6 +6,7 @@ Dir["#{File.expand_path("../lib/shimmer/middlewares", __dir__)}/*"].sort.each {
6
6
  Dir["#{File.expand_path("../lib/shimmer/controllers", __dir__)}/*"].sort.each { |e| require e }
7
7
  Dir["#{File.expand_path("../lib/shimmer/jobs", __dir__)}/*"].sort.each { |e| require e }
8
8
  Dir["#{File.expand_path("../lib/shimmer/utils", __dir__)}/*"].sort.each { |e| require e }
9
+ require_relative "shimmer/auth"
9
10
 
10
11
  module Shimmer
11
12
  class Error < StandardError; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shimmer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.12
4
+ version: 0.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jens Ravens
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-06-08 00:00:00.000000000 Z
11
+ date: 2022-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -135,16 +135,29 @@ files:
135
135
  - bin/solargraph
136
136
  - config/rubocop_base.yml
137
137
  - lib/shimmer.rb
138
+ - lib/shimmer/auth.rb
139
+ - lib/shimmer/auth/apple_provider.rb
140
+ - lib/shimmer/auth/authenticating.rb
141
+ - lib/shimmer/auth/current.rb
142
+ - lib/shimmer/auth/dev_provider.rb
143
+ - lib/shimmer/auth/device.rb
144
+ - lib/shimmer/auth/google_provider.rb
145
+ - lib/shimmer/auth/user.rb
138
146
  - lib/shimmer/controllers/files_controller.rb
139
147
  - lib/shimmer/controllers/sitemaps_controller.rb
148
+ - lib/shimmer/helpers/meta_helper.rb
140
149
  - lib/shimmer/jobs/sitemap_job.rb
141
150
  - lib/shimmer/middlewares/cloudflare.rb
142
151
  - lib/shimmer/railtie.rb
152
+ - lib/shimmer/tasks/auth.rake
143
153
  - lib/shimmer/tasks/db.rake
144
154
  - lib/shimmer/tasks/lint.rake
155
+ - lib/shimmer/tasks/s3.rake
156
+ - lib/shimmer/utils/config.rb
145
157
  - lib/shimmer/utils/file_helper.rb
146
158
  - lib/shimmer/utils/file_proxy.rb
147
159
  - lib/shimmer/utils/localizable.rb
160
+ - lib/shimmer/utils/meta.rb
148
161
  - lib/shimmer/utils/remote_navigation.rb
149
162
  - lib/shimmer/utils/sitemap_adapter.rb
150
163
  - lib/shimmer/version.rb