diff --git a/index.html b/index.html
index a088e2f..1326bad 100644
--- a/index.html
+++ b/index.html
@@ -64,7 +64,7 @@
-
+
diff --git a/js/controller/controller.js b/js/controller/controller.js
index c41977e..8d2fd61 100644
--- a/js/controller/controller.js
+++ b/js/controller/controller.js
@@ -2,7 +2,7 @@
function createClient(endpoint) {
const api = new mm_client.MattermostApi(normalizedEndpoint(endpoint));
- return new mm_client.MattermostClient(api);
+ return new mm_client.MattermostClient(api, localstorage_credentials);
}
function buttonDisable(element, text) {
diff --git a/js/model/credentials.js b/js/model/localstorage_credentials.js
similarity index 94%
rename from js/model/credentials.js
rename to js/model/localstorage_credentials.js
index 4cb1339..911fd6e 100644
--- a/js/model/credentials.js
+++ b/js/model/localstorage_credentials.js
@@ -1,4 +1,4 @@
-const credentials = (function() { "use strict";
+const localstorage_credentials = (function() { "use strict";
const LOCALSTORAGE_KEY_SERVER = "mattermostServer";
const RE_SERVER_ITEM = new RegExp(`^${LOCALSTORAGE_KEY_SERVER}_(.*)\$`, "");
diff --git a/js/model/mm_client.js b/js/model/mm_client.js
index 1006772..41f2741 100644
--- a/js/model/mm_client.js
+++ b/js/model/mm_client.js
@@ -31,74 +31,84 @@ class MattermostApi {
class MattermostClient {
- constructor (api) {
+ constructor (api, credentials_provider) {
this.api = api;
+ this.credentials = credentials_provider;
+ const creds = this.credentials.get(this.api.id);
+ this.token = creds ? creds.token : null;
+ console.info(`Created MattermostClient for ${this.api.id}, ${this.token ? "found token" : "did not find token"}`);
+ }
+
+ async authenticatedGet(url, queryParams) {
+ assert(this.token, "logged in");
+ const response = await this.api.get(url, this.token, queryParams);
+ return response.responseJson;
+ }
+
+ async loggedIn() {
+ if (!this.token) {
+ return false;
+ }
+ try {
+ const meResponse = await this.userMe();
+ return true;
+ } catch (e) {
+ if (e instanceof ajax.NotOkError && e.xhr.status == 401) {
+ return false;
+ } else {
+ throw e;
+ }
+ }
}
async logIn(login_id, password) {
+ if (this.token && await this.tokenWorks()) {
+ throw Error("Already logged in on this server");
+ }
+
const response = await this.api.post("/users/login", undefined, {login_id, password});
const token = response.getResponseHeader("Token");
if (!token) {
throw Error("No Token header in response to log in request");
}
- credentials.store(this.api.id, login_id, token);
+ this.credentials.store(this.api.id, login_id, token);
+ this.token = token;
return response.responseJson;
}
async logOut() {
- const stored = credentials.get(this.api.id);
- if (!stored || !stored.token) {
- throw Error("No token stored");
- }
- const response = await this.api.post("/users/logout", stored.token);
+ assert(this.token, "logged in");
+ const response = await this.api.post("/users/logout", this.token);
// Verify that the token is now invalidated
- let tokenWorks;
- try {
- const meResponse = await this.usersMe();
- tokenWorks = true;
- } catch (e) {
- if (e instanceof ajax.NotOkError && e.xhr.status == 401) {
- tokenWorks = false;
- } else {
- throw e;
- }
- }
- if (tokenWorks) {
+ if (await loggedIn()) {
throw new Error("Failed to log out: token still works after trying to log out");
}
- credentials.clear(this.api.id);
+ this.credentials.clear(this.api.id);
+ this.token = null;
return response.responseJson;
}
- async usersMe() {
- const stored = credentials.get(this.api.id);
- if (!stored || !stored.token) {
- throw Error("No token stored");
- }
- const response = await this.api.get("/users/me", stored.token);
- return response.responseJson;
+ user(user_id) {
+ assertIsMattermostId(user_id);
+ return this.authenticatedGet(`/users/${user_id}`);
+ }
+ userMe() { return this.authenticatedGet("/users/me"); }
+ myTeams() { return this.authenticatedGet("/users/me/teams"); }
+
+ myChannels(team_id) {
+ assertIsMattermostId(team_id);
+ return this.authenticatedGet(`/users/me/teams/${team_id}/channels`);
}
- async myTeams() {
- const stored = credentials.get(this.api.id);
- const response = await this.api.get("/users/me/teams", stored.token);
- return response.responseJson;
- }
-
- async myChannels(team_id) {
- const stored = credentials.get(this.api.id);
- const response = await this.api.get(`/users/me/teams/${team_id}/channels`, stored.token);
- return response.responseJson;
- }
-
- async channelPosts(channel_id, beforePost=null, afterPost=null, since=null) {
- const stored = credentials.get(this.api.id);
- const response = await this.api.get(`/channels/${channel_id}/posts`, stored.token, {
+ channelPosts(channel_id, beforePost=null, afterPost=null, since=null) {
+ assertIsMattermostId(channel_id);
+ assertIsNullOrMattermostId(beforePost);
+ assertIsNullOrMattermostId(afterPost);
+ return this.authenticatedGet(`/channels/${channel_id}/posts`, {
before: beforePost, after: afterPost, since
});
- return response.responseJson;
}
}
diff --git a/js/util.js b/js/util.js
index 6e289eb..02284cd 100644
--- a/js/util.js
+++ b/js/util.js
@@ -44,3 +44,25 @@ function thisToArg(f) {
}
}
+
+class AssertionError extends Error {}
+/**
+ * Throw an AssertionError if the first argument is not true
+ */
+function assert(condition, message) {
+ if (!condition) {
+ if (message) {
+ throw new AssertionError(`Assertion failed: ${message}`);
+ } else {
+ throw new AssertionError("Assertion failed");
+ }
+ }
+}
+
+const MATTERMOST_ID_REGEXP = /^[a-z0-9]{26}$/;
+function assertIsMattermostId(string, name="") {
+ assert(MATTERMOST_ID_REGEXP.test(string), `${name} has the form of a Mattermost ID`);
+}
+function assertIsNullOrMattermostId(string, name="") {
+ assert(string === null || MATTERMOST_ID_REGEXP.test(string), `${name} is null or has the form of a Mattermost ID`);
+}
diff --git a/js/view/view.js b/js/view/view.js
index d9314b6..f263ff6 100644
--- a/js/view/view.js
+++ b/js/view/view.js
@@ -1,5 +1,5 @@
function populateServerSelectionList() {
- const servers = credentials.getServers();
+ const servers = localstorage_credentials.getServers();
let nodes = [];
for (let server of servers) {
@@ -49,7 +49,7 @@ function populateChannelList() {
}
}
- const servers = credentials.getServers();
+ const servers = localstorage_credentials.getServers();
byId("channel_list").innerHTML = "";
for (let server of servers) {