decidim-bulletin_board 0.4.0 → 0.5.0

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: 6a143d34d670d822c82a6f08abaa0f90faf4e9312875cba0a9117faa09679e5f
4
- data.tar.gz: 27b4d187001dd0aa3ed497bb6c4d21138b794075e91faace0eae5fa8c2738d50
3
+ metadata.gz: e07fa810de254d60c03c695aa6708e8813cf1f71a4df80fb7c0086ea57da74de
4
+ data.tar.gz: fd6bfc7b6afc49fac85f6b8d34d27a6c39cf37d679e2a7faf702a05bc7ec52d4
5
5
  SHA512:
6
- metadata.gz: aebd18c91bc774fc539fc87940fb5da309217e3850770e3b0e4f751d35222295e43eca3f1f0d8702730ffed582f2676f192e4142d2f092b9185f162ff12b9644
7
- data.tar.gz: a458ac71cc856888dff79c2c5e2504e14cd2ef9191a74025fca344e9598ee691740cb29481932ddb9bc71467e6504a1457349ca4c47bc11f5f96d41027b10c33
6
+ metadata.gz: 98aaac12e64a4aafcd67d8058518cf36b389ef2bca2cddffc23f03944ed7194b2d38568e77a76e910a0289974e5e1a4f539b513fcebc935fb1e69b6ca332bb68
7
+ data.tar.gz: 73c0f19adfa8c2365491c4d9a46387fecb7d01098695e734e3653c1f43944e40aa5c07168ba86877394029ac16feb85ccdd0aa2b25ca02f54ec837588a82a4de
@@ -5,7 +5,29 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [Unreleased]
8
+ ## [0.5.0] - 2020-12-19
9
+
10
+ ### Changed
11
+
12
+ - Renamed `setup_election` to `create_election` and moved `election_id` from the `election_data` to a separate argument.
13
+
14
+ ### Fixed
15
+
16
+ - Include the missing `open_ballot_box` and `close_ballot_box` methods from the previous release.
17
+ - Added missing namespace on the seed task
18
+
19
+ ## [0.4.0] - 2020-12-18
20
+
21
+ ### Added
22
+
23
+ - `content_hash` field for the `LogEntry` records with the hash of the `content` field, if included in the message.
24
+ - `Command` base class for all the classes representing GraphQL queries or mutations sent to the Bulletin Board.
25
+ - `open_ballot_box` and `close_ballot_box` methods to the `Decidim::BulletinBoard::Client`.
26
+
27
+ ### Changed
28
+
29
+ - New format for the messages: `iat`, `message_id` and Bulletin Board fields in the root message, and `content` for the Voting Scheme messages.
30
+ - Improved consistency between methods included by `Decidim::BulletinBoard::Client`.
9
31
 
10
32
  ## [0.3.1] - 2020-12-10
11
33
 
@@ -1,24 +1,25 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- decidim-bulletin_board (0.3.1)
5
- activemodel (~> 5.0, >= 5.0.0.1)
6
- activesupport (~> 5.0, >= 5.0.0.1)
4
+ decidim-bulletin_board (0.5.0)
5
+ activemodel (>= 5.0.0)
6
+ activesupport (>= 5.0.0)
7
7
  byebug (~> 11.0)
8
8
  graphlient (~> 0.4.0)
9
- jwt
9
+ jwt (~> 2.2.2)
10
10
  wisper (~> 2.0.0)
11
11
 
12
12
  GEM
13
13
  remote: https://rubygems.org/
14
14
  specs:
15
- activemodel (5.2.4.4)
16
- activesupport (= 5.2.4.4)
17
- activesupport (5.2.4.4)
15
+ activemodel (6.0.3.4)
16
+ activesupport (= 6.0.3.4)
17
+ activesupport (6.0.3.4)
18
18
  concurrent-ruby (~> 1.0, >= 1.0.2)
19
19
  i18n (>= 0.7, < 2)
20
20
  minitest (~> 5.1)
21
21
  tzinfo (~> 1.1)
22
+ zeitwerk (~> 2.2, >= 2.2.2)
22
23
  addressable (2.7.0)
23
24
  public_suffix (>= 2.0.2, < 5.0)
24
25
  ast (2.4.1)
@@ -88,7 +89,7 @@ GEM
88
89
  ruby-progressbar (1.10.1)
89
90
  ruby2_keywords (0.0.2)
90
91
  thread_safe (0.3.6)
91
- tzinfo (1.2.8)
92
+ tzinfo (1.2.9)
92
93
  thread_safe (~> 0.1)
93
94
  unicode-display_width (1.7.0)
94
95
  webmock (3.10.0)
@@ -97,6 +98,7 @@ GEM
97
98
  hashdiff (>= 0.4.0, < 2.0.0)
98
99
  wisper (2.0.1)
99
100
  wisper-rspec (1.1.0)
101
+ zeitwerk (2.4.2)
100
102
 
101
103
  PLATFORMS
102
104
  ruby
@@ -1,17 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_model"
4
+ require "active_support/configurable"
3
5
  require "decidim/bulletin_board/version"
6
+ require "jwt"
4
7
  require "graphlient"
5
8
  require "wisper"
6
- require "active_model"
9
+
7
10
  require "decidim/bulletin_board/jwk_utils"
8
11
  require "decidim/bulletin_board/message_identifier"
12
+
9
13
  require "decidim/bulletin_board/client"
10
- require "decidim/bulletin_board/graphql/client"
11
- require "decidim/bulletin_board/voter"
12
14
  require "decidim/bulletin_board/authority"
13
- require "active_support/configurable"
14
- require "jwt"
15
+ require "decidim/bulletin_board/voter"
15
16
 
16
17
  module Decidim
17
18
  # This module holds all the logic for the Bulletin Board Ruby Client to connect
@@ -2,3 +2,5 @@
2
2
 
3
3
  require "decidim/bulletin_board/authority/create_election"
4
4
  require "decidim/bulletin_board/authority/get_election_status"
5
+ require "decidim/bulletin_board/authority/open_ballot_box"
6
+ require "decidim/bulletin_board/authority/close_ballot_box"
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module BulletinBoard
5
+ module Authority
6
+ # This command uses the GraphQL client to request the closing of the ballot box.
7
+ class CloseBallotBox < Decidim::BulletinBoard::Command
8
+ # Public: Initializes the command.
9
+ #
10
+ # election_id - The local election identifier
11
+ def initialize(election_id)
12
+ @election_id = election_id
13
+ end
14
+
15
+ # Executes the command. Broadcasts these events:
16
+ #
17
+ # - :ok when everything is valid and the query operation is successful.
18
+ # - :error if query operation was not successful.
19
+ #
20
+ # Returns nothing.
21
+ def call
22
+ message_id = message_id(unique_election_id(election_id), "close_ballot_box")
23
+ signed_data = sign_message(message_id, {})
24
+
25
+ begin
26
+ response = client.query do
27
+ mutation do
28
+ closeBallotBox(messageId: message_id, signedData: signed_data) do
29
+ election do
30
+ status
31
+ end
32
+ error
33
+ end
34
+ end
35
+ end
36
+
37
+ return broadcast(:error, response.data.close_ballot_box.error) if response.data.close_ballot_box.error.present?
38
+
39
+ broadcast(:ok, response.data.close_ballot_box.election)
40
+ rescue Graphlient::Errors::ServerError
41
+ broadcast(:error, "Sorry, something went wrong")
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ attr_reader :election_id
48
+ end
49
+ end
50
+ end
51
+ end
@@ -4,49 +4,39 @@ module Decidim
4
4
  module BulletinBoard
5
5
  module Authority
6
6
  # This class handles the creation of an election.
7
- class CreateElection
8
- def initialize(election_data, message_id)
9
- @client = BulletinBoard::Graphql::Client.client
7
+ class CreateElection < Decidim::BulletinBoard::Command
8
+ def initialize(election_id, election_data)
9
+ @election_id = election_id
10
10
  @election_data = election_data
11
- @message_id = message_id
12
- @private_key = private_key
13
- end
14
-
15
- def self.call(election_data, message_id)
16
- new(election_data, message_id).call
17
11
  end
18
12
 
19
13
  def call
20
- args = {
21
- message_id: message_id,
22
- signed_data: encode_data(election_data)
23
- }
24
-
25
- response = client.query do
26
- mutation do
27
- createElection(messageId: args[:message_id], signedData: args[:signed_data]) do
28
- election do
29
- status
14
+ message_id = message_id(unique_election_id(election_id), "create_election")
15
+ signed_data = sign_message(message_id, election_data)
16
+
17
+ begin
18
+ response = client.query do
19
+ mutation do
20
+ createElection(messageId: message_id, signedData: signed_data) do
21
+ election do
22
+ status
23
+ end
24
+ error
30
25
  end
31
- error
32
26
  end
33
27
  end
34
- end
35
28
 
36
- response.data.create_election
29
+ return broadcast(:error, response.data.create_election.error) if response.data.create_election.error.present?
30
+
31
+ broadcast(:ok, response.data.create_election.election)
32
+ rescue Graphlient::Errors::ServerError
33
+ broadcast(:error, "Sorry, something went wrong")
34
+ end
37
35
  end
38
36
 
39
37
  private
40
38
 
41
- attr_reader :client, :election_data, :message_id
42
-
43
- def private_key
44
- @private_key ||= JwkUtils.import_private_key(BulletinBoard.identification_private_key)
45
- end
46
-
47
- def encode_data(election_data)
48
- JWT.encode(election_data, private_key.keypair, "RS256")
49
- end
39
+ attr_reader :election_data, :election_id
50
40
  end
51
41
  end
52
42
  end
@@ -4,11 +4,10 @@ module Decidim
4
4
  module BulletinBoard
5
5
  module Authority
6
6
  # This command uses the GraphQL client to get the status of the election.
7
- class GetElectionStatus
8
- include Wisper::Publisher
7
+ class GetElectionStatus < Decidim::BulletinBoard::Command
9
8
  # Public: Initializes the command.
10
9
  #
11
- # election - The election to receive the status from.
10
+ # election_id - The local election identifier
12
11
  def initialize(election_id)
13
12
  @election_id = election_id
14
13
  end
@@ -20,14 +19,12 @@ module Decidim
20
19
  #
21
20
  # Returns nothing.
22
21
  def call
23
- args = {
24
- unique_id: election_id
25
- }
22
+ unique_id = unique_election_id(election_id)
26
23
 
27
24
  begin
28
25
  response = client.query do
29
26
  query do
30
- election(uniqueId: args[:unique_id]) do
27
+ election(uniqueId: unique_id) do
31
28
  status
32
29
  end
33
30
  end
@@ -42,10 +39,6 @@ module Decidim
42
39
  private
43
40
 
44
41
  attr_reader :election_id
45
-
46
- def client
47
- @client ||= BulletinBoard::Graphql::Client.client
48
- end
49
42
  end
50
43
  end
51
44
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module BulletinBoard
5
+ module Authority
6
+ # This command uses the GraphQL client to request the opening of the ballot box.
7
+ class OpenBallotBox < Decidim::BulletinBoard::Command
8
+ # Public: Initializes the command.
9
+ #
10
+ # election_id - The local election identifier
11
+ def initialize(election_id)
12
+ @election_id = election_id
13
+ end
14
+
15
+ # Executes the command. Broadcasts these events:
16
+ #
17
+ # - :ok when everything is valid and the query operation is successful.
18
+ # - :error if query operation was not successful.
19
+ #
20
+ # Returns nothing.
21
+ def call
22
+ message_id = message_id(unique_election_id(election_id), "open_ballot_box")
23
+ signed_data = sign_message(message_id, {})
24
+
25
+ begin
26
+ response = client.query do
27
+ mutation do
28
+ openBallotBox(messageId: message_id, signedData: signed_data) do
29
+ election do
30
+ status
31
+ end
32
+ error
33
+ end
34
+ end
35
+ end
36
+
37
+ return broadcast(:error, response.data.open_ballot_box.error) if response.data.open_ballot_box.error.present?
38
+
39
+ broadcast(:ok, response.data.open_ballot_box.election)
40
+ rescue Graphlient::Errors::ServerError
41
+ broadcast(:error, "Sorry, something went wrong")
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ attr_reader :election_id
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "decidim/bulletin_board/command"
4
+
3
5
  module Decidim
4
6
  module BulletinBoard
5
7
  # The Bulletin Board client
@@ -34,26 +36,29 @@ module Decidim
34
36
  private_key && server && api_key
35
37
  end
36
38
 
37
- def sign_data(data)
38
- JWT.encode(data, private_key.keypair, "RS256")
39
+ def create_election(election_id, election_data)
40
+ create_election = Decidim::BulletinBoard::Authority::CreateElection.new(election_id, election_data)
41
+ create_election.on(:ok) { |election| return election }
42
+ create_election.on(:error) { |error_message| raise StandardError, error_message }
43
+ create_election.call
39
44
  end
40
45
 
41
- def setup_election(election_data)
42
- message_id = "#{election_data[:election_id]}.create_election+a.#{authority_slug}"
43
- Decidim::BulletinBoard::Authority::CreateElection.call(election_data, message_id)
46
+ def open_ballot_box(election_id)
47
+ open_ballot_box = Decidim::BulletinBoard::Election::OpenBallotBox.new(election_id)
48
+ open_ballot_box.on(:ok) { |election| return election }
49
+ open_ballot_box.on(:error) { |error_message| raise StandardError, error_message }
50
+ open_ballot_box.call
44
51
  end
45
52
 
46
- def cast_vote(election_data, voter_data, encrypted_vote)
47
- form = Decidim::BulletinBoard::Voter::VoteForm.new(self, election_data, voter_data, encrypted_vote)
48
- cast_vote = Decidim::BulletinBoard::Voter::CastVote.new(form)
53
+ def cast_vote(election_id, voter_id, encrypted_vote)
54
+ cast_vote = Decidim::BulletinBoard::Voter::CastVote.new(election_id, voter_id, encrypted_vote)
49
55
  cast_vote.on(:ok) { |pending_message| return pending_message }
50
56
  cast_vote.on(:error) { |error_message| raise StandardError, error_message }
51
57
  cast_vote.call
52
58
  end
53
59
 
54
60
  def get_status(election_id)
55
- unique_election_id = "#{authority_slug}.#{election_id}"
56
- get_status = Decidim::BulletinBoard::Authority::GetElectionStatus.new(unique_election_id)
61
+ get_status = Decidim::BulletinBoard::Authority::GetElectionStatus.new(election_id)
57
62
  get_status.on(:ok) { |status| return status }
58
63
  get_status.on(:error) { |error_message| raise StandardError, error_message }
59
64
  get_status.call
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/bulletin_board/graphql/client"
4
+
5
+ module Decidim
6
+ module BulletinBoard
7
+ # The base class for all commands.
8
+ class Command
9
+ include Wisper::Publisher
10
+
11
+ delegate :authority_slug, :private_key, to: :class
12
+
13
+ def unique_election_id(election_id)
14
+ Decidim::BulletinBoard::MessageIdentifier.unique_election_id(authority_slug, election_id)
15
+ end
16
+
17
+ def message_id(unique_election_id, type_subtype, voter_id = nil)
18
+ Decidim::BulletinBoard::MessageIdentifier.format(unique_election_id, type_subtype, voter_id ? :voter : :authority, voter_id || authority_slug)
19
+ end
20
+
21
+ def sign_message(message_id, message)
22
+ JWT.encode(complete_message(message_id, message), private_key.keypair, "RS256")
23
+ end
24
+
25
+ def client
26
+ @client ||= BulletinBoard::Graphql::Client.client
27
+ end
28
+
29
+ def complete_message(message_id, message)
30
+ message.merge({
31
+ iat: Time.now.to_i,
32
+ message_id: message_id
33
+ })
34
+ end
35
+
36
+ class << self
37
+ def self.call(*args)
38
+ new(*args).call
39
+ end
40
+
41
+ def private_key
42
+ @private_key ||= JwkUtils.import_private_key(BulletinBoard.identification_private_key)
43
+ end
44
+
45
+ def authority_slug
46
+ @authority_slug ||= BulletinBoard.authority_name.parameterize
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Decidim
4
4
  module BulletinBoard
5
- VERSION = "0.4.0"
5
+ VERSION = "0.5.0"
6
6
  end
7
7
  end
@@ -1,4 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "decidim/bulletin_board/voter/cast_vote"
4
- require "decidim/bulletin_board/voter/vote_form"
@@ -4,13 +4,14 @@ module Decidim
4
4
  module BulletinBoard
5
5
  module Voter
6
6
  # This command uses the GraphQL client to cast the vote.
7
- class CastVote
8
- include Wisper::Publisher
7
+ class CastVote < Decidim::BulletinBoard::Command
9
8
  # Public: Initializes the command.
10
9
  #
11
10
  # form - A form object with the params.
12
- def initialize(form)
13
- @form = form
11
+ def initialize(election_id, voter_id, encrypted_vote)
12
+ @election_id = election_id
13
+ @voter_id = voter_id
14
+ @encrypted_vote = encrypted_vote
14
15
  end
15
16
 
16
17
  # Executes the command. Broadcasts these events:
@@ -20,17 +21,13 @@ module Decidim
20
21
  #
21
22
  # Returns nothing.
22
23
  def call
23
- return broadcast(:error, form.errors.full_messages.join(". ")) unless form.valid?
24
-
25
- args = {
26
- message_id: form.message_id,
27
- signed_data: form.signed_data
28
- }
24
+ message_id = message_id(unique_election_id(election_id), "vote.cast", voter_id)
25
+ signed_data = sign_message(message_id, { content: encrypted_vote })
29
26
 
30
27
  begin
31
28
  response = client.query do
32
29
  mutation do
33
- vote(messageId: args[:message_id], signedData: args[:signed_data]) do
30
+ vote(messageId: message_id, signedData: signed_data) do
34
31
  pendingMessage do
35
32
  status
36
33
  end
@@ -49,11 +46,7 @@ module Decidim
49
46
 
50
47
  private
51
48
 
52
- attr_reader :form
53
-
54
- def client
55
- @client ||= BulletinBoard::Graphql::Client.client
56
- end
49
+ attr_reader :election_id, :voter_id, :encrypted_vote
57
50
  end
58
51
  end
59
52
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: decidim-bulletin_board
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Morcillo
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2020-12-18 00:00:00.000000000 Z
12
+ date: 2020-12-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
@@ -175,16 +175,18 @@ files:
175
175
  - decidim-bulletin_board.gemspec
176
176
  - lib/decidim/bulletin_board.rb
177
177
  - lib/decidim/bulletin_board/authority.rb
178
+ - lib/decidim/bulletin_board/authority/close_ballot_box.rb
178
179
  - lib/decidim/bulletin_board/authority/create_election.rb
179
180
  - lib/decidim/bulletin_board/authority/get_election_status.rb
181
+ - lib/decidim/bulletin_board/authority/open_ballot_box.rb
180
182
  - lib/decidim/bulletin_board/client.rb
183
+ - lib/decidim/bulletin_board/command.rb
181
184
  - lib/decidim/bulletin_board/graphql/client.rb
182
185
  - lib/decidim/bulletin_board/jwk_utils.rb
183
186
  - lib/decidim/bulletin_board/message_identifier.rb
184
187
  - lib/decidim/bulletin_board/version.rb
185
188
  - lib/decidim/bulletin_board/voter.rb
186
189
  - lib/decidim/bulletin_board/voter/cast_vote.rb
187
- - lib/decidim/bulletin_board/voter/vote_form.rb
188
190
  homepage: https://github.com
189
191
  licenses:
190
192
  - AGPL-3.0
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Decidim
4
- module BulletinBoard
5
- module Voter
6
- # A form object to handle some data transformation and validation to cast a vote.
7
- class VoteForm
8
- include ActiveModel::Validations
9
-
10
- validates :election_data, :voter_data, :encrypted_vote, presence: true
11
- validate :election_id_present
12
- validate :voter_id_present
13
-
14
- # Public: initialize the form
15
- #
16
- # bulletin_board_data - An instance of the bulletin board client
17
- # election_data - A Hash including the necessary data from the election.
18
- # voter_data - A Hash including the necessary data from the voter.
19
- # encrypted_vote - A Hash including the encrypted vote to cast
20
- def initialize(bulletin_board_client, election_data, voter_data, encrypted_vote)
21
- @bulletin_board_client = bulletin_board_client
22
- @election_data = election_data
23
- @voter_data = voter_data
24
- @encrypted_vote = encrypted_vote
25
- end
26
-
27
- # Public: returns a message identifier for the cast vote operation.
28
- def message_id
29
- @message_id ||= "#{election_id}.vote.cast+v.#{voter_id}"
30
- end
31
-
32
- # Public: uses the bulletin board client to sign the encrypted vote merged with the metadata
33
- def signed_data
34
- @signed_data ||= bulletin_board_client.sign_data(message)
35
- end
36
-
37
- private
38
-
39
- attr_reader :bulletin_board_client, :election_data, :voter_data, :encrypted_vote
40
-
41
- def election_id_present
42
- errors.add(:election_data, "doesn't include the election id") unless election_id.present?
43
- end
44
-
45
- def election_id
46
- return if election_data.blank?
47
-
48
- election_data[:election_id]
49
- end
50
-
51
- def voter_id_present
52
- errors.add(:voter_data, "doesn't include the voter id") unless voter_id.present?
53
- end
54
-
55
- def voter_id
56
- return if voter_data.blank?
57
-
58
- voter_data[:voter_id]
59
- end
60
-
61
- def message
62
- {
63
- iat: Time.now.to_i,
64
- message_id: message_id,
65
- content: encrypted_vote
66
- }
67
- end
68
- end
69
- end
70
- end
71
- end