gecko-dev/devtools/shared/security/auth.js

221 lines
6.3 KiB
JavaScript

/* 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);
},
};