summaryrefslogtreecommitdiffstats
path: root/comm/mail/components/addrbook/test/browser/browser_cardDAV_oAuth.js
blob: 137a13e2217e902112ae44f3f9a769b06c829ecd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, you can obtain one at http://mozilla.org/MPL/2.0/. */

// Creates address books in various configurations (current and legacy) and
// performs requests in each of them to prove that OAuth2 authentication is
// working as expected.

var { CardDAVDirectory } = ChromeUtils.import(
  "resource:///modules/CardDAVDirectory.jsm"
);

var LoginInfo = Components.Constructor(
  "@mozilla.org/login-manager/loginInfo;1",
  Ci.nsILoginInfo,
  "init"
);

// Ideal login info. This is what would be saved if you created a new calendar.
const ORIGIN = "oauth://mochi.test";
const SCOPE = "test_scope";
const USERNAME = "bob@test.invalid";
const VALID_TOKEN = "bobs_refresh_token";

const PATH = "comm/mail/components/addrbook/test/browser/data/";
const URL = `http://mochi.test:8888/browser/${PATH}`;

/**
 * Set a string pref for the given directory.
 *
 * @param {string} dirPrefId
 * @param {string} key
 * @param {string} value
 */
function setPref(dirPrefId, key, value) {
  Services.prefs.setStringPref(`ldap_2.servers.${dirPrefId}.${key}`, value);
}

/**
 * Clear any existing saved logins and add the given ones.
 *
 * @param {string[][]} - Zero or more arrays consisting of origin, realm,
 *   username, and password.
 */
function setLogins(...logins) {
  Services.logins.removeAllLogins();
  for (let [origin, realm, username, password] of logins) {
    Services.logins.addLogin(
      new LoginInfo(origin, null, realm, username, password, "", "")
    );
  }
}

/**
 * Create a directory with the given id, perform a request, and check that the
 * correct authorisation header was used. If the user is required to
 * re-authenticate with the provider, check that the new token is stored in the
 * right place.
 *
 * @param {string} dirPrefId - Pref ID of the new directory.
 * @param {string} uid - UID of the new directory.
 * @param {string} [newTokenUsername] - If given, re-authentication must happen
 *   and the new token stored with this user name.
 */
async function subtest(dirPrefId, uid, newTokenUsername) {
  let directory = new CardDAVDirectory();
  directory._dirPrefId = dirPrefId;
  directory._uid = uid;
  directory.__prefBranch = Services.prefs.getBranch(
    `ldap_2.servers.${dirPrefId}.`
  );
  directory.__prefBranch.setStringPref("carddav.url", URL);

  let response = await directory._makeRequest("auth_headers.sjs");
  Assert.equal(response.status, 200);
  let headers = JSON.parse(response.text);

  if (newTokenUsername) {
    Assert.equal(headers.authorization, "Bearer new_access_token");

    let logins = Services.logins
      .findLogins(ORIGIN, null, SCOPE)
      .filter(l => l.username == newTokenUsername);
    Assert.equal(logins.length, 1);
    Assert.equal(logins[0].username, newTokenUsername);
    Assert.equal(logins[0].password, "new_refresh_token");
  } else {
    Assert.equal(headers.authorization, "Bearer bobs_access_token");
  }

  Services.logins.removeAllLogins();
}

// Test making a request when there is no matching token stored.

/** No token stored, no username set. */
add_task(function testAddressBookOAuth_uid_none() {
  let dirPrefId = "uid_none";
  let uid = "testAddressBookOAuth_uid_none";
  return subtest(dirPrefId, uid, uid);
});

// Test making a request when there IS a matching token, but the server rejects
// it. Currently a new token is not requested on failure.

/** Expired token stored with UID. */
add_task(function testAddressBookOAuth_uid_expired() {
  let dirPrefId = "uid_expired";
  let uid = "testAddressBookOAuth_uid_expired";
  setLogins([ORIGIN, SCOPE, uid, "expired_token"]);
  return subtest(dirPrefId, uid, uid);
}).skip(); // Broken.

// Test making a request with a valid token.

/** Valid token stored with UID. This is the old way of storing the token. */
add_task(function testAddressBookOAuth_uid_valid() {
  let dirPrefId = "uid_valid";
  let uid = "testAddressBookOAuth_uid_valid";
  setLogins([ORIGIN, SCOPE, uid, VALID_TOKEN]);
  return subtest(dirPrefId, uid);
});

/** Valid token stored with username, exact scope. */
add_task(function testAddressBookOAuth_username_validSingle() {
  let dirPrefId = "username_validSingle";
  let uid = "testAddressBookOAuth_username_validSingle";
  setPref(dirPrefId, "carddav.username", USERNAME);
  setLogins(
    [ORIGIN, SCOPE, USERNAME, VALID_TOKEN],
    [ORIGIN, "other_scope", USERNAME, "other_refresh_token"]
  );
  return subtest(dirPrefId, uid);
});

/** Valid token stored with username, many scopes. */
add_task(function testAddressBookOAuth_username_validMultiple() {
  let dirPrefId = "username_validMultiple";
  let uid = "testAddressBookOAuth_username_validMultiple";
  setPref(dirPrefId, "carddav.username", USERNAME);
  setLogins([ORIGIN, "scope test_scope other_scope", USERNAME, VALID_TOKEN]);
  return subtest(dirPrefId, uid);
});