Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Keybase proof integration #10013

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b664211
create account_identity_proofs table
xgess Dec 27, 2018
73b9867
add endpoint for keybase to check local proofs
xgess Dec 27, 2018
be92df5
add async task to update validity and liveness of proofs from keybase
xgess Dec 28, 2018
391a714
first pass keybase proof CRUD
xgess Jan 8, 2019
5554b60
second pass keybase proof creation
xgess Jan 9, 2019
fd670b5
clean up proof list and add badges
xgess Jan 10, 2019
09886f5
add avatar url to keybase api
xgess Jan 14, 2019
90aa86b
Always highlight the “Identity Proofs” navigation item when interacti…
adamjspooner Jan 16, 2019
bed4005
Update translations.
adamjspooner Jan 16, 2019
b7af7d9
Add profile URL.
adamjspooner Jan 16, 2019
82e7a46
Reorder proofs.
adamjspooner Jan 16, 2019
cc09a00
Add proofs to bio.
adamjspooner Jan 16, 2019
3c42d2d
Update settings/identity_proofs front-end.
adamjspooner Jan 16, 2019
9451e8f
Use `link_to`.
adamjspooner Jan 16, 2019
3c7a42f
Only encode query params if they exist.
adamjspooner Jan 16, 2019
4b341b1
Only show live proofs.
adamjspooner Jan 16, 2019
5781466
change valid to active in proof list and update liveness before displ…
xgess Jan 16, 2019
c8dc078
minor fixes
xgess Jan 16, 2019
729f6ac
add keybase config at well-known path
xgess Jan 24, 2019
6ce8f39
fixes for rubocop
xgess Feb 11, 2019
29c390b
extremely naive feature flagging off the identity proof UI
xgess Jan 24, 2019
9476b28
make identity proofs page resilient to potential keybase issues
xgess Feb 11, 2019
0bf7c56
normalize i18n
xgess Feb 11, 2019
2a41def
tweaks for brakeman
xgess Feb 11, 2019
cda6394
remove two unused translations
xgess Feb 11, 2019
dc2ed71
cleanup and add more localizations
xgess Feb 13, 2019
cf214d3
make keybase_contacts an admin setting
xgess Feb 15, 2019
a93123f
fix ExternalProofService my_domain
xgess Feb 18, 2019
0b3a105
use Addressable::URI in identity proofs
xgess Feb 18, 2019
dcf94d6
use active model serializer for keybase proof config
xgess Feb 18, 2019
6f6e797
more cleanup of keybase proof config
xgess Feb 18, 2019
4c37390
rename proof is_valid and is_live to proof_valid and proof_live
xgess Feb 18, 2019
a93c2bd
cleanup
xgess Feb 18, 2019
1119574
assorted tweaks for more robust communication with keybase
xgess Feb 28, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/controllers/admin/settings_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class SettingsController < BaseController
preview_sensitive_media
custom_css
profile_directory
keybase_contacts
).freeze

BOOLEAN_SETTINGS = %w(
Expand Down
12 changes: 12 additions & 0 deletions app/controllers/api/v1/keybase_proofs_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

class Api::V1::KeybaseProofsController < Api::BaseController
respond_to :json

def index
@account = Account.find_local!(params[:username])
kb_proofs = AccountIdentityProof.keybase.where(account_id: @account.id)

render json: @account, serializer: REST::KeybaseUserSerializer, proofs: kb_proofs
end
end
49 changes: 49 additions & 0 deletions app/controllers/settings/identity_proofs_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# frozen_string_literal: true

class Settings::IdentityProofsController < Settings::BaseController
layout 'admin'
before_action :authenticate_user!

def index
@proofs = AccountIdentityProof.where(account: current_account).order(provider: :asc, provider_username: :asc)
@proofs.each(&:update_liveness)
end

def new
return redirect_to settings_identity_proofs_path unless all_new_params_present?

@proof = AccountIdentityProof.new(
account: current_account,
token: params[:token],
provider: params[:provider],
provider_username: params[:provider_username]
)
end

def create
@proof = AccountIdentityProof.where(
account: current_account,
provider: create_params[:provider],
provider_username: create_params[:provider_username]
).first_or_initialize
@proof.token = create_params[:token]
if @proof.save_if_valid_remotely
KeybaseProofWorker.perform_async(@proof.id) if @proof.keybase?
success_url = @proof.success_redirect(params[:useragent])
redirect_to Addressable::URI.parse(success_url).normalize.to_s
else
flash[:alert] = I18n.t('account_identity_proofs.notices.failed', provider: @proof.provider)
redirect_to settings_identity_proofs_path
end
end

private

def all_new_params_present?
[:provider, :provider_username, :token].all? { |k| params[k].present? }
end

def create_params
params.require(:account_identity_proof).permit(:provider, :provider_username, :token)
end
end
9 changes: 9 additions & 0 deletions app/controllers/well_known/keybase_proof_config_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

module WellKnown
class KeybaseProofConfigController < ActionController::Base
def show
render json: {}, serializer: KeybaseConfigSerializer
end
end
end
17 changes: 17 additions & 0 deletions app/helpers/settings_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,23 @@ def human_locale(locale)
HUMAN_LOCALES[locale]
end

def identity_proof_providers
AccountIdentityProof::PROVIDER_MAP.values
end

def identity_proof_my_domain
ExternalProofService.my_domain
end

def default_provider_pic_url(provider)
case provider
when AccountIdentityProof::PROVIDER_MAP[:keybase]
'/keybase-logo-100@2x.png'
else
full_asset_url('/avatars/original/missing.png')
end
end

def filterable_languages
LanguageDetector.instance.language_names.select(&HUMAN_LOCALES.method(:key?))
end
Expand Down
109 changes: 109 additions & 0 deletions app/javascript/styles/mastodon/admin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,115 @@ $content-width: 840px;
}
}

.identity_proofs {
p {
font-size: 14px;
}

table {
margin: 20px 0;

td {
vertical-align: middle;

&.account {
border-bottom: 0;

a {
align-items: center;
color: $primary-text-color;
display: flex;
flex-direction: row;
text-decoration: none;

&:hover,
&:active,
&:focus {
text-decoration: underline;
}
}
}

&.proof a {
color: $darker-text-color;

&:hover,
&:active,
&:focus {
color: lighten($darker-text-color, 10%);
}
}
}

.account__avatar {
margin-bottom: 0;
margin-right: 8px;
}

.inactive {
color: $error-red;
}

.proof {
text-align: center;
}

.active {
color: $success-green;
}
}

.account__avatar {
margin-bottom: 1.2em;
}

.fa-link {
background-color: darken($ui-base-color, 5%);
border-radius: 100%;
font-size: 24px;
padding: 1em;
}

.column {
align-items: center;
display: flex;
flex: 1;
flex-direction: column;
flex-shrink: 1;

&-2 {
flex-grow: 0;
overflow: visible;
position: relative;
z-index: 1;
}
}

.connection {
background-color: $classic-base-color;
border-radius: 4px;
padding: 1em;
position: relative;
text-align: center;

&::after {
background-color: darken($ui-base-color, 5%);
content: '';
display: block;
height: 100%;
left: 50%;
position: absolute;
width: 1px;
}
}

.row {
align-items: center;
display: flex;
flex-direction: row;
}
}

@media screen and (max-width: $no-columns-breakpoint) {
display: block;
overflow-y: auto;
Expand Down
14 changes: 14 additions & 0 deletions app/javascript/styles/mastodon/containers.scss
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,20 @@

a {
color: lighten($ui-highlight-color, 8%);

&.provider_username {
position: relative;
top: -7px;
}

&.proof {
color: $darker-text-color;
display: block;
font-size: 12px;
position: relative;
text-transform: lowercase;
top: -11px;
}
}

dl:first-child .verified {
Expand Down
94 changes: 94 additions & 0 deletions app/lib/keybase_proof.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# frozen_string_literal: true

module Keybase
class ResponseDataError < StandardError; end
class ExpectedProofLiveError < ResponseDataError; end

class Proof
def initialize(account_identity_proof, username = nil)
@kb_username = account_identity_proof.provider_username
@token = account_identity_proof.token
@account_identity_proof = account_identity_proof
@domain = ExternalProofService.my_domain
@base_url = ExternalProofService::Keybase.base_url
@_local_username = username
end

def local_username
# performance optimization: allow initializing with this
# value to prevent this additional query from running
@_local_username ||= @account_identity_proof.account.username
end

def valid?
get_from_keybase(
endpoint: '/_/api/1.0/sig/proof_valid.json',
query_params: to_keybase_params
).fetch(:proof_valid)
rescue KeyError, NoMethodError, HTTP::Error
false
end

def remote_status
result = get_from_keybase(
endpoint: '/_/api/1.0/sig/proof_live.json',
query_params: to_keybase_params
)
{ proof_valid: result.fetch(:proof_valid), proof_live: result.fetch(:proof_live) }
rescue KeyError, NoMethodError, HTTP::Error => e
raise Keybase::ResponseDataError, e.message
end

def profile_pic_url
get_from_keybase(
endpoint: '/_/api/1.0/user/pic_url.json',
query_params: { username: @kb_username }
).fetch(:pic_url)
rescue KeyError, NoMethodError, HTTP::Error
nil
end

def success_redirect_url(useragent)
useragent ||= 'unknown'
params = to_keybase_params
params[:kb_ua] = useragent
build_url('/_/proof_creation_success', params)
end

def badge_pic_url
params = { domain: @domain, username: local_username }
build_url("/#{@kb_username}/proof_badge/#{@token}", params)
end

def sigchain_url
build_url("/#{@kb_username}/sigchain\##{@token}", {})
end

def profile_url
build_url("/#{@kb_username}", {})
end

private

def to_keybase_params
{
domain: @domain,
kb_username: @kb_username,
username: local_username,
sig_hash: @token,
}
end

def get_from_keybase(endpoint:, query_params:)
Request.new(:get, build_url(endpoint, query_params)).perform do |response|
JSON.parse(response.body, symbolize_names: true)
end
end

def build_url(endpoint, query_params)
uri = Addressable::URI.parse(@base_url + endpoint)
uri.query_values = query_params
uri.to_s
end
end
end
Loading