diff options
Diffstat (limited to 'devtools/shared/security/auth.js')
-rw-r--r-- | devtools/shared/security/auth.js | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/devtools/shared/security/auth.js b/devtools/shared/security/auth.js new file mode 100644 index 0000000000..20995779f4 --- /dev/null +++ b/devtools/shared/security/auth.js @@ -0,0 +1,220 @@ +/* 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/. */ + +"use strict"; + +loader.lazyRequireGetter( + this, + "prompt", + "resource://devtools/shared/security/prompt.js" +); + +/** + * A simple enum-like object with keys mirrored to values. + * This makes comparison to a specfic value simpler without having to repeat and + * mis-type the value. + */ +function createEnum(obj) { + for (const key in obj) { + obj[key] = key; + } + return obj; +} + +/** + * |allowConnection| implementations can return various values as their |result| + * field to indicate what action to take. By specifying these, we can + * centralize the common actions available, while still allowing embedders to + * present their UI in whatever way they choose. + */ +var AuthenticationResult = (exports.AuthenticationResult = createEnum({ + /** + * Close all listening sockets, and disable them from opening again. + */ + DISABLE_ALL: null, + + /** + * Deny the current connection. + */ + DENY: null, + + /** + * Additional data needs to be exchanged before a result can be determined. + */ + PENDING: null, + + /** + * Allow the current connection. + */ + ALLOW: null, + + /** + * Allow the current connection, and persist this choice for future + * connections from the same client. This requires a trustable mechanism to + * identify the client in the future. + */ + ALLOW_PERSIST: null, +})); + +/** + * An |Authenticator| implements an authentication mechanism via various hooks + * in the client and server debugger socket connection path (see socket.js). + * + * |Authenticator|s are stateless objects. Each hook method is passed the state + * it needs by the client / server code in socket.js. + * + * Separate instances of the |Authenticator| are created for each use (client + * connection, server listener) in case some methods are customized by the + * embedder for a given use case. + */ +var Authenticators = {}; + +/** + * The Prompt authenticator displays a server-side user prompt that includes + * connection details, and asks the user to verify the connection. There are + * no cryptographic properties at work here, so it is up to the user to be sure + * that the client can be trusted. + */ +var Prompt = (Authenticators.Prompt = {}); + +Prompt.mode = "PROMPT"; + +Prompt.Client = function() {}; +Prompt.Client.prototype = { + mode: Prompt.mode, + + /** + * When client is about to make a new connection, verify that the connection settings + * are compatible with this authenticator. + * @throws if validation requirements are not met + */ + validateSettings() {}, + + /** + * When client has just made a new socket connection, validate the connection + * to ensure it meets the authenticator's policies. + * + * @param host string + * The host name or IP address of the devtools server. + * @param port number + * The port number of the devtools server. + * @param encryption boolean (optional) + * Whether the server requires encryption. Defaults to false. + * @param s nsISocketTransport + * Underlying socket transport, in case more details are needed. + * @return boolean + * Whether the connection is valid. + */ + validateConnection() { + return true; + }, + + /** + * Work with the server to complete any additional steps required by this + * authenticator's policies. + * + * Debugging commences after this hook completes successfully. + * + * @param host string + * The host name or IP address of the devtools server. + * @param port number + * The port number of the devtools server. + * @param encryption boolean (optional) + * Whether the server requires encryption. Defaults to false. + * @param transport DebuggerTransport + * A transport that can be used to communicate with the server. + * @return A promise can be used if there is async behavior. + */ + authenticate() {}, +}; + +Prompt.Server = function() {}; +Prompt.Server.prototype = { + mode: Prompt.mode, + + /** + * Augment the service discovery advertisement with any additional data needed + * to support this authentication mode. + * + * @param listener SocketListener + * The socket listener that was just opened. + * @param advertisement object + * The advertisement being built. + */ + augmentAdvertisement(listener, advertisement) { + advertisement.authentication = Prompt.mode; + }, + + /** + * Determine whether a connection the server should be allowed or not based on + * this authenticator's policies. + * + * @param session object + * In PROMPT mode, the |session| includes: + * { + * client: { + * host, + * port + * }, + * server: { + * host, + * port + * }, + * transport + * } + * @return An AuthenticationResult value. + * A promise that will be resolved to the above is also allowed. + */ + authenticate({ client, server }) { + if (!Services.prefs.getBoolPref("devtools.debugger.prompt-connection")) { + return AuthenticationResult.ALLOW; + } + return this.allowConnection({ + authentication: this.mode, + client, + server, + }); + }, + + /** + * Prompt the user to accept or decline the incoming connection. The default + * implementation is used unless this is overridden on a particular + * authenticator instance. + * + * It is expected that the implementation of |allowConnection| will show a + * prompt to the user so that they can allow or deny the connection. + * + * @param session object + * In PROMPT mode, the |session| includes: + * { + * authentication: "PROMPT", + * client: { + * host, + * port + * }, + * server: { + * host, + * port + * } + * } + * @return An AuthenticationResult value. + * A promise that will be resolved to the above is also allowed. + */ + allowConnection: prompt.Server.defaultAllowConnection, +}; + +exports.Authenticators = { + get(mode) { + if (!mode) { + mode = Prompt.mode; + } + for (const key in Authenticators) { + const auth = Authenticators[key]; + if (auth.mode === mode) { + return auth; + } + } + throw new Error("Unknown authenticator mode: " + mode); + }, +}; |