import fetch from 'node-fetch';
import { getGeoServerResponseText, GeoServerResponseError } from './util/geoserver.js';
/**
* Client for GeoServer security.
*
* @module SecurityClient
*/
export default class SecurityClient {
/**
* Creates a GeoServer REST SecurityClient instance.
*
* @param {String} url The URL of the GeoServer REST API endpoint
* @param {String} auth The Basic Authentication string
*/
constructor(url, auth) {
this.url = url;
this.auth = auth;
}
/**
* Returns all users registered in GeoServer.
*
* @throws Error if request fails
*
* @returns {Object} An object with all users
*/
async getAllUsers() {
const response = await fetch(this.url + 'security/usergroup/users.json', {
credentials: 'include',
method: 'GET',
headers: {
Authorization: this.auth
}
});
if (!response.ok) {
const geoServerResponse = await getGeoServerResponseText(response);
throw new GeoServerResponseError(null, geoServerResponse);
}
return response.json();
}
/**
* Creates a new user.
*
* @param {String} username The name of the user to be created
* @param {String} password The password of the user to be created
*
* @throws Error if request fails
*/
async createUser(username, password) {
const body = {
user: {
userName: username,
password: password,
enabled: true
}
};
const response = await fetch(this.url + 'security/usergroup/users.json', {
credentials: 'include',
method: 'POST',
headers: {
Authorization: this.auth,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
if (!response.ok) {
const geoServerResponse = await getGeoServerResponseText(response);
switch (response.status) {
case 404:
throw new GeoServerResponseError(
`User ${username} might already exists.`,
geoServerResponse
);
default:
throw new GeoServerResponseError(null, geoServerResponse);
}
}
}
/**
* Updates an existing user. User name is only taken for identification and
* cannot be changed with this API call.
*
* @param {String} username The name of the user to be created
* @param {String} password The password of the user to be created
* @param {Boolean} enabled Enable / disable the user
*
* @throws Error if request fails
*/
async updateUser(username, password, enabled) {
const body = {
user: {
password: password,
enabled: enabled
}
};
const response = await fetch(this.url + 'security/usergroup/user/' + username, {
credentials: 'include',
method: 'POST',
headers: {
Authorization: this.auth,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
if (!response.ok) {
const geoServerResponse = await getGeoServerResponseText(response);
throw new GeoServerResponseError(null, geoServerResponse);
}
}
/**
* Deletes an existing user.
*
* @param {String} username The name of the user to be deleted
*
* @throws Error if request fails
*/
async deleteUser(username) {
const response = await fetch(this.url + 'security/usergroup/user/' + username, {
credentials: 'include',
method: 'DELETE',
headers: {
Authorization: this.auth
}
});
if (!response.ok) {
const geoServerResponse = await getGeoServerResponseText(response);
throw new GeoServerResponseError(null, geoServerResponse);
}
}
/**
* Returns all roles registered in GeoServer.
*
* @throws Error if request fails
*
* @returns {Object} An object with all roles like { "roles": ["ADMIN", "GROUP_ADMIN"] }
*/
async getAllRoles() {
const url = `${this.url}security/roles.json`;
const response = await fetch(url, {
credentials: 'include',
method: 'GET',
headers: {
Authorization: this.auth
}
});
if (!response.ok) {
const geoServerResponse = await getGeoServerResponseText(response);
throw new GeoServerResponseError(null, geoServerResponse);
}
return response.json();
}
/**
* Creates a new role.
*
* @param {String} role The name of the role to be created
*
* @throws Error if request fails
*/
async createRole(role) {
const url = `${this.url}security/roles/role/${role}`;
const response = await fetch(url, {
credentials: 'include',
method: 'POST',
headers: {
Authorization: this.auth
}
});
if (!response.ok) {
const geoServerResponse = await getGeoServerResponseText(response);
switch (response.status) {
case 404:
throw new GeoServerResponseError(`Role ${role} might already exist.`, geoServerResponse);
default:
throw new GeoServerResponseError(null, geoServerResponse);
}
}
}
/**
* Deletes an existing role.
*
* @param {String} role The name of the role to be deleted
*
* @throws Error if request fails
*/
async deleteRole(role) {
const url = `${this.url}security/roles/role/${role}`;
const response = await fetch(url, {
credentials: 'include',
method: 'DELETE',
headers: {
Authorization: this.auth
}
});
if (!response.ok) {
const geoServerResponse = await getGeoServerResponseText(response);
switch (response.status) {
case 404:
throw new GeoServerResponseError(`Role ${role} not existing.`, geoServerResponse);
default:
throw new GeoServerResponseError(null, geoServerResponse);
}
}
}
/**
* Associates the given role to the user.
*
* @param {String} username The name of the user to add the role to
* @param {String} role The role to associate
*
* @throws Error if request fails
*/
async associateUserRole(username, role) {
const response = await fetch(`${this.url}security/roles/role/${role}/user/${username}`, {
credentials: 'include',
method: 'POST',
headers: {
Authorization: this.auth
}
});
if (!response.ok) {
const geoServerResponse = await getGeoServerResponseText(response);
throw new GeoServerResponseError(null, geoServerResponse);
}
}
/**
* Returns all data access rules registered in GeoServer.
*
* @throws Error if request fails
*
* @returns {Object} An object with all data access rules like { "*.*.r": ["ADMIN", "GROUP_ADMIN"] }
*/
async getAllAccessRules() {
const url = `${this.url}security/acl/layers.json`;
const response = await fetch(url, {
credentials: 'include',
method: 'GET',
headers: {
Authorization: this.auth
}
});
if (!response.ok) {
const geoServerResponse = await getGeoServerResponseText(response);
throw new GeoServerResponseError(null, geoServerResponse);
}
return response.json();
}
/**
* Creates a new data access rule.
*
* @param {String} rule The rule in the form '<MY_WS>.<MY_LAYER>.r'
* @param {String[]} roles The roles to allow access to rule
*
* @throws Error if request fails
*/
async createDataAccessRule(rule, roles) {
const url = `${this.url}security/acl/layers`;
const body = {};
body[rule] = roles.join(',');
const response = await fetch(url, {
credentials: 'include',
method: 'POST',
headers: {
Authorization: this.auth,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
if (!response.ok) {
const geoServerResponse = await getGeoServerResponseText(response);
switch (response.status) {
case 404:
throw new GeoServerResponseError(`Rule ${rule} might already exists.`, geoServerResponse);
default:
throw new GeoServerResponseError(null, geoServerResponse);
}
}
}
/**
* Deletes an existing data access rule.
*
* @param {String} rule The rule to be deleted, like '<MY_WS>.<MY_LAYER>.r'
*
* @throws Error if request fails
*/
async deleteDataAccessRule(rule) {
const url = `${this.url}security/acl/layers/${rule}`;
const response = await fetch(url, {
credentials: 'include',
method: 'DELETE',
headers: {
Authorization: this.auth
}
});
if (!response.ok) {
const geoServerResponse = await getGeoServerResponseText(response);
switch (response.status) {
case 404:
throw new GeoServerResponseError(`Rule ${rule} not existing.`, geoServerResponse);
default:
throw new GeoServerResponseError(null, geoServerResponse);
}
}
}
}