Skip to content
This repository has been archived by the owner on Jul 23, 2024. It is now read-only.

Commit

Permalink
TMP: add unlink from pmo button in user preferences
Browse files Browse the repository at this point in the history
  • Loading branch information
LeoMcA committed Sep 11, 2019
1 parent c419042 commit e11789e
Show file tree
Hide file tree
Showing 13 changed files with 359 additions and 38 deletions.
9 changes: 9 additions & 0 deletions app/controllers/mozilla_iam/dinopark_link_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ def link
render json: { success: true }, status: 200
end

def unlink
profile = Profile.for(current_user)
profile.force_refresh
profile.dinopark_enabled = false
current_user.update(title: nil)
current_user.user_profile.update(website: nil)
render json: { success: true }, status: 200
end

def dont_show
Profile.set(current_user, :never_show_dinopark_modal, true)
cookies.delete(:authentication_data)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import showModal from "discourse/lib/show-modal"
import { ajax } from "discourse/lib/ajax"

export default {
setupComponent(args, component) {
},

reload() {
window.location.reload()
},

actions: {
unlink() {
const container = Discourse.__container__;
const controller = container.lookup("controller:dinopark-unlink-modal")
controller.setProperties({
user: this.get("model")
})
showModal("dinopark-unlink-modal")
},
},

shouldRender(args, component) {
return args.model.get("mozilla_iam.dinopark_enabled")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import ModalFunctionality from "discourse/mixins/modal-functionality"
import { ajax } from "discourse/lib/ajax"

export default Ember.Controller.extend(ModalFunctionality, {

reload(url) {
window.location.reload()
},

actions: {
continue() {
this.set("submitted", true)
return ajax("/mozilla_iam/dinopark_unlink.json", {
type: "POST"
}).then(result => {
if (result.success) {
this.reload()
} else {
this.fail(result.message || I18n.t("dinopark.unlink.failed"))
}
}, () => {
this.fail(I18n.t("dinopark.unlink.failed"))
})
},

cancel() {
this.modal.send("closeModal")
}
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div class="dinopark-preferences-banner alert alert-info">
{{{i18n "dinopark.preferences.linked_description"}}}
<div class="buttons">
<button class="btn btn-medium unlink" title="{{i18n "dinopark.preferences.unlink_long"}}" {{action "unlink"}}>
{{d-icon "unlink" label=(i18n "dinopark.preferences.unlink_long")}}
<span aria-hidden>{{i18n 'dinopark.preferences.unlink'}}</span>
</button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{{#d-modal-body class="dinopark-unlink-modal" title="dinopark.unlink.title"}}
<p>{{i18n "dinopark.unlink.description_1"}}</p>
<p>{{i18n "dinopark.unlink.description_2"}}</p>
<div class="discourse-fields">
<div>{{i18n "dinopark.unlink.username"}}:</div>
<div class="value value-username">{{user.username}}</div>

<div>{{i18n "dinopark.unlink.name"}}:</div>
<div class="value value-name">{{user.name}}</div>

<div>{{i18n "dinopark.unlink.bio"}}:</div>
<div class="value value-bio">{{{user.bio_cooked}}}</div>

<div>{{i18n "dinopark.unlink.location"}}:</div>
<div class="value value-location">{{user.location}}</div>

<div>{{i18n "dinopark.unlink.avatar"}}:</div>
<div class="value value-avatar">{{bound-avatar user "medium"}}</div>
</div>
{{/d-modal-body}}
<div class="modal-footer">
{{#conditional-loading-spinner condition=submitted size="small"}}
{{d-button label="dinopark.unlink.continue" class="btn-primary" action=(action "continue")}}
{{d-button label="dinopark.unlink.cancel" action=(action "cancel")}}
{{/conditional-loading-spinner}}
</div>
26 changes: 24 additions & 2 deletions assets/stylesheets/common/mozilla-iam.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
.dinopark-link-modal {
.dinopark-link-modal, .dinopark-unlink-modal {
label {
display: inline;
}

.dinopark-fields {
p:first-child {
margin-top: 0;
}

.dinopark-fields, .discourse-fields {
display: grid;
grid-template-columns: auto 1fr;
grid-column-gap: 1em;
Expand All @@ -29,6 +33,10 @@
}
}
}

.discourse-fields {
margin-bottom: 0;
}
}

.user-preferences {
Expand Down Expand Up @@ -80,6 +88,20 @@
}
}

.dinopark-preferences-banner {
.buttons {
display: flex;
justify-content: center;
align-items: center;
padding-top: 10px;
padding-bottom: 5px;

button {
margin: 0 10px;
}
}
}

#mozilla-iam-auth-error-page {
font-size: 16px;
max-width: 570px;
Expand Down
16 changes: 16 additions & 0 deletions config/locales/client.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,25 @@ en:
cancel: "Not right now"
continue: "Yes, find and link my profile"
confirm: "Confirm and create linked account"
unlink:
title: "Unlink your people.mozilla.org profile"
description_1: "After unlinking, Discourse will no longer update your account when your people.mozilla.org profile changes."
description_2: "The following fields will maintain these current values until manually edited on Discourse:"
username: "Username"
name: "Name"
bio: "About Me"
location: "Location"
avatar: "Profile Picture"
continue: "Continue, unlink my profile"
cancel: "Cancel, keep my profile linked"
failed: "Failed to unlink account, please try again"
login:
confirm: "Confirm and link account"
dont_show_again: "Don't show this again"
preferences:
linked_description: "Your account is linked with your <em>public</em> <a href='https://people.mozilla.org' target='_blank'>people.mozilla.org</a> profile fields"
unlink: "Unlink"
unlink_long: "Unlink your Discourse account from your people.mozilla.org profile"
fields:
username: Username
full_name: Full Name
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
end
post :notification, to: "notification#notification"
post :dinopark_link, to: "dinopark_link#link"
post :dinopark_unlink, to: "dinopark_link#unlink"
post "/dinopark_link/dont_show", to: "dinopark_link#dont_show"
end
2 changes: 1 addition & 1 deletion lib/mozilla_iam/api/oauth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def get(path, params = false)

def access_token
api_token = ::PluginStore.get('mozilla-iam', "#{@aud}_token")
if api_token.nil? || api_token[:exp] < Time.now.to_i + 60
if api_token.nil? || api_token[:exp].to_i < Time.now.to_i + 60
refresh_token
else
api_token[:access_token]
Expand Down
21 changes: 1 addition & 20 deletions spec/components/mozilla_iam/api/person_v2_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,9 @@
end

describe described_class::Profile do
def single_attribute(value=nil, metadata={})
metadata[:verified] = true if metadata[:verified].nil?
metadata[:public] = true if metadata[:public].nil?
{
metadata: {
verified: metadata[:verified],
display: metadata[:public] ? "public" : "staff"
},
value: value
}
end

def profile_with(attributes, value=nil, metadata={})
raw = {}
if attributes.is_a? Hash
attributes.each do |name, value|
raw[name] = single_attribute(value)
end
else
raw[attributes] = single_attribute(value, metadata)
end
described_class.new(raw)
described_class.new person_v2_profile_with(attributes, value, metadata)
end

shared_examples "one-to-one mapping" do |method, attribute, value, return_value|
Expand Down
76 changes: 61 additions & 15 deletions spec/controllers/mozilla_iam/dinopark_link_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,30 @@
describe MozillaIAM::DinoparkLinkController, type: :request do
let(:user) { Fabricate(:user) }
let!(:profile) { MozillaIAM::Profile.new(user, "uid") }
let!(:last_refresh) { Time.now - 5.minutes }

describe "#link" do
let!(:last_refresh) { Time.now - 5.minutes }
before do
user.custom_fields["mozilla_iam_last_refresh"] = last_refresh
user.save_custom_fields
end

before do
user.custom_fields["mozilla_iam_last_refresh"] = last_refresh
user.save_custom_fields
shared_examples "errors" do |url|
it "doesn't let an unauthenticated user link" do
post url
expect(response.status).to eq 403
expect(JSON.parse(response.body)["error_type"]).to eq "not_logged_in"
end

it "fails without CSRF token" do
ActionController::Base.allow_forgery_protection = true
post url
expect(response.status).to eq 403
expect(JSON.parse(response.body)).to eq ["BAD CSRF"]
ActionController::Base.allow_forgery_protection = false
end
end

describe "#link" do
it "lets a user link themselves" do
authenticate_user(user)
sign_in(user)
Expand All @@ -36,21 +51,52 @@
expect(profile.last_refresh).to be_within(5.seconds).of Time.now
end

it "doesn't let an unauthenticated user link" do
post "/mozilla_iam/dinopark_link.json"
include_examples "errors", "/mozilla_iam/dinopark_link.json"
end

expect(response.status).to eq 403
expect(JSON.parse(response.body)["error_type"]).to eq "not_logged_in"
describe "#unlink" do
before do
user.custom_fields["mozilla_iam_dinopark_enabled"] = true
user.save_custom_fields
end

it "fails without CSRF token" do
ActionController::Base.allow_forgery_protection = true
post "/mozilla_iam/dinopark_link.json"
expect(response.status).to eq 403
expect(JSON.parse(response.body)).to eq ["BAD CSRF"]
ActionController::Base.allow_forgery_protection = false
it "lets a user unlink themselves" do
uid = create_uid(user.username)
stub_apis_profile_request(uid, {})
stub_person_api_v2_profile_request(uid, person_v2_profile_with(
primary_username: user.username,
fun_title: "the builder",
location: "Bobsville"
))
authenticate_with_id_token create_id_token(user)
sign_in(user)

expect(profile.last_refresh).to be_within(5.seconds).of last_refresh
expect(profile.dinopark_enabled?).to eq true
user.reload
expect(user.title).to eq "the builder"
expect(user.user_profile.website).to eq "https://people.mozilla.org/p/#{user.username}"
expect(user.user_profile.location).to eq "Bobsville"

MozillaIAM::SessionData.expects(:find_or_create).returns(MozillaIAM::SessionData.create(
user_auth_token_id: 0,
last_refresh: last_refresh,
aal: "LOW"
))

post "/mozilla_iam/dinopark_unlink.json"

expect(response.status).to eq 200
profile.reload
expect(profile.dinopark_enabled?).to eq false
expect(profile.last_refresh).to be_within(5.seconds).of Time.now
user.reload
expect(user.title).to be_blank
expect(user.user_profile.website).to be_blank
expect(user.user_profile.location).to eq "Bobsville"
end

include_examples "errors", "/mozilla_iam/dinopark_unlink.json"
end

describe "#dont_show" do
Expand Down
31 changes: 31 additions & 0 deletions spec/support/iam_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,37 @@ def stub_people_api_profile_request(uid, profile)
.to_return(status: 200, body: MultiJson.dump(body: MultiJson.dump(profile)))
end

def stub_person_api_v2_profile_request(uid, profile)
stub_oauth_token_request('api.sso.mozilla.com')

stub_request(:get, "https://person.api.sso.mozilla.com/v2/user/user_id/#{uid}")
.to_return(status: 200, body: MultiJson.dump(profile))
end

def single_attribute(value=nil, metadata={})
metadata[:verified] = true if metadata[:verified].nil?
metadata[:public] = true if metadata[:public].nil?
{
metadata: {
verified: metadata[:verified],
display: metadata[:public] ? "public" : "staff"
},
value: value
}
end

def person_v2_profile_with(attributes, value=nil, metadata={})
raw = {}
if attributes.is_a? Hash
attributes.each do |name, value|
raw[name] = single_attribute(value)
end
else
raw[attributes] = single_attribute(value, metadata)
end
raw
end

def stub_management_api_profile_request(uid, profile)
stub_oauth_token_request('https://auth.mozilla.auth0.com/api/v2/')

Expand Down
Loading

0 comments on commit e11789e

Please sign in to comment.