diff --git a/oc-chef-pedant/lib/pedant/rspec/common.rb b/oc-chef-pedant/lib/pedant/rspec/common.rb
index accd67d516..29c327f5f7 100755
--- a/oc-chef-pedant/lib/pedant/rspec/common.rb
+++ b/oc-chef-pedant/lib/pedant/rspec/common.rb
@@ -387,6 +387,16 @@ def self.isolate(message = nil, &examples)
end
end
+ # Used to easily documented endpoints that don't have tests
+ def self.untested_endpoint(endpoint_name, reason)
+ context endpoint_name do
+ it "returns success" do
+ pending(reason)
+ raise NotImplementedError
+ end
+ end
+ end
+
# Run the given block once (using 'key' to identify
# the block). This allows us to do "before :each"
# cleanup to clean up from previous test runs, since
diff --git a/oc-chef-pedant/spec/api/oc_id_spec.rb b/oc-chef-pedant/spec/api/oc_id_spec.rb
index 0cd3df8221..3754337308 100644
--- a/oc-chef-pedant/spec/api/oc_id_spec.rb
+++ b/oc-chef-pedant/spec/api/oc_id_spec.rb
@@ -2,6 +2,37 @@
require 'openssl'
describe "oc_id API", :oc_id do
+ let(:request_headers) do
+ {
+ "Content-Type" => "application/x-www-form-urlencoded",
+ "Accept" => "text/html",
+ "Cookie" => csrf[:cookie]
+ }
+ end
+
+ let(:username) { "pedant-oc-test-user-#{rand(10**7...10**8).to_s}" }
+ let(:password) { "foobar" } # hardcoded at the platform layer
+ let(:user_overrides) do
+ {
+ :overrides => {
+ "first_name" => "#{username}-first-name",
+ "middle_name" => "#{username}-middle-name",
+ "last_name" => "#{username}-last-name",
+ "display_name" => "#{username}-display-name",
+ "email" => "#{username}-email@host.com",
+ }
+ }
+ end
+
+ # create user before each test so that we don't regen the keys
+ # of the standard user used in other tests.
+ let(:oc_id_user) { platform.create_user(username, user_overrides) }
+
+ # delete user after each test
+ after(:each) do
+ platform.delete_user(oc_id_user)
+ end
+
context "status endpoint" do
let(:request_url) { "#{platform.server}/id/v1/status" }
let(:good_status) do
@@ -11,55 +42,37 @@
end
context "GET /id/v1/status" do
- it "retuns 200" do
+ it "returns 200" do
get(request_url, platform.superuser).should look_like({:status => 200,
:body => good_status})
end
end
end
- let(:username) { "pedant-oc-test-user-#{rand(10**7...10**8).to_s}" }
-
- let(:password) { "foobar" } #hardcoded at the platform layer
-
- # create user before each test so that we don't regen the keys
- # of the standard user used in other tests.
- let(:reset_user) {
- platform.create_user(username)
- }
-
- # delete user after each test
- after(:each) do
- platform.delete_user(reset_user)
+ def signin(with_callback = true)
+ response = get("#{platform.server}/id/signin", oc_id_user, headers: {"Accept" => "text/html"})
+ cookie = response.headers[:set_cookie][1].split(";").first
+ # I KNOW. I'll leave it up to reviewers whether we should pull
+ # in nokogiri or hpricot just do to this
+ re = //
+ token = response.match(re)[1]
+ headers = { "Content-Type" => "application/x-www-form-urlencoded", "Cookie" => cookie }
+ body = "username=#{username}&password=#{password}&authenticity_token=#{CGI.escape(token)}&commit=Sign+In"
+ # Now sign in
+ if with_callback
+ response = post("#{platform.server}/id/auth/chef/callback", oc_id_user, headers: headers, payload: body)
+ cookie = response.headers[:set_cookie][1].split(";").first
+ end
+ { cookie: cookie, token: token}
end
context "key reset" do
- let(:request_headers) do
- {
- "Content-Type" => "application/x-www-form-urlencoded",
- "Cookie" => csrf[:cookie]
- }
- end
-
- let(:csrf) do
- response = get("#{platform.server}/id/signin", reset_user, headers: {"Accept" => "text/html"})
- cookie = response.headers[:set_cookie][1].split(";").first
- # I KNOW. I'll leave it up to reviewers whether we should pull
- # in nokogiri or hpricot just do to this
- re = //
- token = response.match(re)[1]
- headers = { "Content-Type" => "application/x-www-form-urlencoded", "Cookie" => cookie }
- body = "username=#{username}&password=#{password}&authenticity_token=#{CGI.escape(token)}&commit=Sign+In"
- # Now sign in
- response = post("#{platform.server}/id/auth/chef/callback", reset_user, headers: headers, payload: body)
- cookie = response.headers[:set_cookie][1].split(";").first
- { cookie: cookie, token: token}
- end
-
+ let(:csrf) { signin }
let(:request_url) { "#{platform.server}/id/profile/regen_key" }
let(:request_body) { "authenticity_token=#{CGI.escape(csrf[:token])}&commit=Get+a+New+Key" }
- let(:response) { post(request_url, reset_user, headers: request_headers, payload: request_body) }
+ let(:response) { post(request_url, oc_id_user, headers: request_headers, payload: request_body) }
+
it "returns a non-zero file" do
expect(response.code).to eq(200)
expect(response.body.length).to_not eq(0)
@@ -69,44 +82,30 @@
expect(response.code).to eq(200)
# This will raise if it isn't a valid key
expect(OpenSSL::PKey::RSA.new(response.body).class).to eq(OpenSSL::PKey::RSA)
- expect(response.body).to_not eq(reset_user.signing_key.to_s)
+ expect(response.body).to_not eq(oc_id_user.signing_key.to_s)
end
end
context "signin" do
let(:request_url) { "#{platform.server}/id/auth/chef/callback" }
let(:request_body) { "username=#{username}&password=#{password}&authenticity_token=#{CGI.escape(csrf[:token])}&commit=Sign+In" }
- let(:request_headers) do
- {
- "Content-Type" => "application/x-www-form-urlencoded",
- "Cookie" => csrf[:cookie]
- }
- end
-
- let(:csrf) do
- response = get("#{platform.server}/id/signin", platform.superuser, headers: {"Accept" => "text/html"})
- cookie = response.headers[:set_cookie][1].split(";").first
- # I KNOW. I'll leave it up to reviewers whether we should pull
- # in nokogiri or hpricot just do to this
- re = //
- token = response.match(re)[1]
- { cookie: cookie, token: token}
- end
-
- let(:response) { post(request_url, reset_user, headers: request_headers, payload: request_body) }
+ let(:csrf) { signin(false) }
+ let(:response) { post(request_url, oc_id_user, headers: request_headers, payload: request_body) }
context "with correct password" do
- let(:password) { "foobar" } #hardcoded at the platform layer
+ let(:password) { "foobar" } # hardcoded at the platform layer
+
context "POST /id/auth/chef/callback" do
it "redirects us to authorized applications" do
expect(response.code).to eq(302)
- expect(response.headers[:location]).to match(%r{/id/oauth/authorized_applications})
+ expect(response.headers[:location]).to end_with('/id/oauth/authorized_applications')
end
end
end
context "with incorrect password" do
let(:password) { "WRONGWRONGWRONG" }
+
context "POST /id/auth/chef/callback" do
it "redirects us to an error" do
expect(response.code).to eq(302)
@@ -115,4 +114,97 @@
end
end
end
+
+ context "/id/profile#show" do
+ let(:csrf) { signin }
+ let(:request_url) { "#{platform.server}/id/profile" }
+ let(:request_body) { "authenticity_token=#{CGI.escape(csrf[:token])}" }
+ let(:response) { get(request_url, oc_id_user, headers: request_headers, payload: request_body) }
+
+ it "shows the profile" do
+ expect(response.code).to eq(200)
+ expect(response.body).to match(%r{Signed in as: #{username}})
+ expect(response.body).to match(/]*value="#{username}-first-name"[^>]*id="user_first_name"\W*\/>/)
+ expect(response.body).to match(/]*value="#{username}-middle-name"[^>]*id="user_middle_name"\W*\/>/)
+ expect(response.body).to match(/]*value="#{username}-last-name"[^>]*id="user_last_name"\W*\/>/)
+ expect(response.body).to match(/]*value="#{username}-email@host\.com"[^>]*id="user_email"\W*\/>/)
+ end
+ end
+
+ # NOTE 2017/06/02 sr: we don't update the email, since that makes oc_id send
+ # an email and that's not something we'd like to do from
+ # pedant running against real deployments
+ context "/id/profile#update" do
+ let(:csrf) { signin }
+ let(:request_url) { "#{platform.server}/id/profile" }
+ let(:request_body) { "authenticity_token=#{CGI.escape(csrf[:token])}&"\
+ "#{CGI.escape('user[first_name]')}=#{username.upcase}-FIRST-NAME&"\
+ "#{CGI.escape('user[last_name]')}=#{username.upcase}-LAST-NAME&"\
+ "#{CGI.escape('user[middle_name]')}=#{username.upcase}-MIDDLE-NAME&"\
+ "_method=PUT&"\
+ "commit=Save+Changes" }
+ let(:response) { post(request_url, oc_id_user, headers: request_headers, payload: request_body) }
+
+ # NOTE: We're using the API here because another query for profile#show
+ # would, with test code laid out like this, cause another user to be created
+ # and used for login -- hence we'd see that other user's profile.
+ # For the same reason, everything happens in one it block :/
+ it "redirects to /id/profile and updates the user record" do
+ expect(response.code).to eq(302)
+ expect(response.headers[:location]).to end_with('/id/profile')
+
+ request_url = "#{platform.server}/users/#{username}"
+ user_body = {
+ "first_name" => "#{username.upcase}-FIRST-NAME",
+ "middle_name" => "#{username.upcase}-MIDDLE-NAME",
+ "last_name" => "#{username.upcase}-LAST-NAME",
+ "email" => "#{username}-email@host.com", # don't want to update, see note
+ # these currently can't be changed via profile update
+ "display_name" => "#{username}-display-name",
+ "username" => username,
+ "public_key" => oc_id_user.signing_key.public_key.to_s + "\n", # What?
+ }
+ get(request_url, platform.superuser).should look_like({
+ :status => 200,
+ :body_exact => user_body
+ })
+ end
+ end
+
+ context "/id/profile#password" do
+ let(:new_password) { "barfoo" }
+ let(:csrf) { signin }
+ let(:request_url) { "#{platform.server}/id/profile/password" }
+ let(:request_body) { "authenticity_token=#{CGI.escape(csrf[:token])}&"\
+ "username=#{username}&"\
+ "current_password=#{password}&"\
+ "new_password=#{new_password}&"\
+ "password_confirmation=#{new_password}&"\
+ "_method=PUT" }
+ let(:response) { post(request_url, oc_id_user, headers: request_headers, payload: request_body) }
+
+ it "redirects to /id/profile and updates the user's password" do
+ expect(response.code).to eq(302)
+ expect(response.headers[:location]).to end_with('/id/profile')
+
+ request_url = "#{platform.server}/authenticate_user"
+ request_body = { username: username, password: new_password }
+ post(request_url, platform.superuser, payload: request_body).should look_like({
+ :status => 200,
+ :body => { "user" => { "username" => username } },
+ })
+ end
+ end
+
+ #
+ # Untested endpoints
+ #
+ # The following endpoints are not tested because they require
+ # additional test-system setup.
+ #
+ untested_endpoint("/v1/users", "requires a configured oc-id application")
+ untested_endpoint("/v1/me", "requires a configured oc-id application")
+ untested_endpoint("/v1/me/organizations", "requires a configured oc-id application")
+ untested_endpoint("/zendesk", "requires zendseks configuration")
+ untested_endpoint("/password-reset", "requires email configuration")
end
diff --git a/src/oc-id/app/controllers/profiles_controller.rb b/src/oc-id/app/controllers/profiles_controller.rb
index 43ec23f38c..97a0d49dcb 100644
--- a/src/oc-id/app/controllers/profiles_controller.rb
+++ b/src/oc-id/app/controllers/profiles_controller.rb
@@ -37,7 +37,7 @@ def update
end
#
- # PUT /id/profile/change_password
+ # PUT /id/profile/password
#
# Changes the user's password.
#