summaryrefslogtreecommitdiffstats
path: root/application/controllers/SelfServiceController.php
diff options
context:
space:
mode:
Diffstat (limited to 'application/controllers/SelfServiceController.php')
-rw-r--r--application/controllers/SelfServiceController.php435
1 files changed, 435 insertions, 0 deletions
diff --git a/application/controllers/SelfServiceController.php b/application/controllers/SelfServiceController.php
new file mode 100644
index 0000000..0b3b642
--- /dev/null
+++ b/application/controllers/SelfServiceController.php
@@ -0,0 +1,435 @@
+<?php
+
+namespace Icinga\Module\Director\Controllers;
+
+use Exception;
+use Icinga\Exception\NotFoundError;
+use Icinga\Exception\ProgrammingError;
+use Icinga\Module\Director\Forms\IcingaHostSelfServiceForm;
+use Icinga\Module\Director\Objects\IcingaEndpoint;
+use Icinga\Module\Director\Objects\IcingaHost;
+use Icinga\Module\Director\Objects\IcingaZone;
+use Icinga\Module\Director\Settings;
+use Icinga\Module\Director\Util;
+use Icinga\Module\Director\Web\Controller\ActionController;
+use ipl\Html\Html;
+
+class SelfServiceController extends ActionController
+{
+ /** @var bool */
+ protected $isApified = true;
+
+ /** @var bool */
+ protected $requiresAuthentication = false;
+
+ /** @var Settings */
+ protected $settings;
+
+ protected function assertApiPermission()
+ {
+ // no permission required, we'll check the API key
+ }
+
+ protected function checkDirectorPermissions()
+ {
+ }
+
+ /**
+ * @throws NotFoundError
+ * @throws ProgrammingError
+ * @throws \Zend_Controller_Request_Exception
+ */
+ public function apiVersionAction()
+ {
+ if ($this->getRequest()->isApiRequest()) {
+ $this->sendPowerShellResponse('1.4.0');
+ } else {
+ throw new NotFoundError('Not found');
+ }
+ }
+
+ /**
+ * @throws \Icinga\Exception\ConfigurationError
+ * @throws \Icinga\Exception\IcingaException
+ * @throws \Zend_Controller_Exception
+ */
+ public function registerHostAction()
+ {
+ $request = $this->getRequest();
+ $form = IcingaHostSelfServiceForm::create($this->db());
+ $form->setApiRequest($request->isApiRequest());
+ try {
+ if ($key = $this->params->get('key')) {
+ $form->loadTemplateWithApiKey($key);
+ }
+ } catch (Exception $e) {
+ $this->sendPowerShellError($e->getMessage(), 404);
+ return;
+ }
+ if ($name = $this->params->get('name')) {
+ $form->setHostName($name);
+ }
+
+ if ($request->isApiRequest()) {
+ $data = json_decode($request->getRawBody());
+ $request->setPost((array) $data);
+ $form->handleRequest();
+ if ($newKey = $form->getHostApiKey()) {
+ $this->sendPowerShellResponse($newKey);
+ } else {
+ $error = implode('; ', $form->getErrorMessages());
+ if ($error === '') {
+ if ($form->isMissingRequiredFields()) {
+ $fields = $form->listMissingRequiredFields();
+ if (count($fields) === 1) {
+ $this->sendPowerShellError(
+ sprintf("%s is required", $fields[0]),
+ 400
+ );
+ } else {
+ $this->sendPowerShellError(
+ sprintf("Missing parameters: %s", implode(', ', $fields)),
+ 400
+ );
+ }
+ return;
+ } else {
+ $this->sendPowerShellError('An unknown error ocurred', 500);
+ }
+ } else {
+ $this->sendPowerShellError($error, 400);
+ }
+ }
+ return;
+ }
+
+ $form->handleRequest();
+ $this->addSingleTab($this->translate('Self Service'))
+ ->addTitle($this->translate('Self Service - Host Registration'))
+ ->content()->add(Html::tag('p', null, $this->translate(
+ 'In case an Icinga Admin provided you with a self service API'
+ . ' token, this is where you can register new hosts'
+ )))
+ ->add($form);
+ }
+
+ /**
+ * @throws NotFoundError
+ * @throws \Zend_Controller_Request_Exception
+ * @throws \Zend_Controller_Response_Exception
+ */
+ public function ticketAction()
+ {
+ if (!$this->getRequest()->isApiRequest()) {
+ throw new NotFoundError('Not found');
+ }
+
+ try {
+ $key = $this->params->getRequired('key');
+ $host = IcingaHost::loadWithApiKey($key, $this->db());
+ if ($host->isTemplate()) {
+ throw new NotFoundError('Got invalid API key "%s"', $key);
+ }
+ $name = $host->getObjectName();
+
+ if ($host->getResolvedProperty('has_agent') !== 'y') {
+ throw new NotFoundError('The host "%s" is not an agent', $name);
+ }
+
+ $this->sendPowerShellResponse($this->api()->getTicket($name));
+ } catch (Exception $e) {
+ if ($e instanceof NotFoundError) {
+ $this->sendPowerShellError($e->getMessage(), 404);
+ } else {
+ $this->sendPowerShellError($e->getMessage(), 500);
+ }
+ }
+ }
+
+ /**
+ * @param $response
+ * @throws ProgrammingError
+ * @throws \Zend_Controller_Request_Exception
+ */
+ protected function sendPowerShellResponse($response)
+ {
+ if ($this->getRequest()->getHeader('X-Director-Accept') === 'text/plain') {
+ if (is_array($response)) {
+ echo $this->makePlainTextPowerShellArray($response);
+ } else {
+ echo $response;
+ }
+ } else {
+ $this->sendJson($this->getResponse(), $response);
+ }
+ }
+
+ /**
+ * @param $error
+ * @param $code
+ * @throws \Zend_Controller_Request_Exception
+ * @throws \Zend_Controller_Response_Exception
+ */
+ protected function sendPowerShellError($error, $code)
+ {
+ if ($this->getRequest()->getHeader('X-Director-Accept') === 'text/plain') {
+ $this->getResponse()->setHttpResponseCode($code);
+ echo "ERROR: $error";
+ } else {
+ $this->sendJsonError($this->getResponse(), $error, $code);
+ }
+ }
+
+ /**
+ * @param $value
+ * @return string
+ * @throws ProgrammingError
+ */
+ protected function makePowerShellBoolean($value)
+ {
+ if ($value === 'y' || $value === true) {
+ return 'true';
+ } elseif ($value === 'n' || $value === false) {
+ return 'false';
+ } else {
+ throw new ProgrammingError(
+ 'Expected boolean value, got %s',
+ var_export($value, 1)
+ );
+ }
+ }
+
+ /**
+ * @param array $params
+ * @return string
+ * @throws ProgrammingError
+ */
+ protected function makePlainTextPowerShellArray(array $params)
+ {
+ $plain = '';
+
+ foreach ($params as $key => $value) {
+ if (is_bool($value)) {
+ $value = $this->makePowerShellBoolean($value);
+ } elseif (is_array($value)) {
+ $value = implode('!', $value);
+ }
+ $plain .= "$key: $value\r\n";
+ }
+
+ return $plain;
+ }
+
+ /**
+ * @throws NotFoundError
+ * @throws \Zend_Controller_Request_Exception
+ * @throws \Zend_Controller_Response_Exception
+ */
+ public function powershellParametersAction()
+ {
+ if (!$this->getRequest()->isApiRequest()) {
+ throw new NotFoundError('Not found');
+ }
+
+ try {
+ $this->shipPowershellParams();
+ } catch (Exception $e) {
+ if ($e instanceof NotFoundError) {
+ $this->sendPowerShellError($e->getMessage(), 404);
+ } else {
+ $this->sendPowerShellError($e->getMessage(), 500);
+ }
+ }
+ }
+
+ /**
+ * @throws NotFoundError
+ * @throws ProgrammingError
+ * @throws \Icinga\Exception\ConfigurationError
+ * @throws \Icinga\Exception\IcingaException
+ * @throws \Icinga\Exception\MissingParameterException
+ * @throws \Zend_Controller_Request_Exception
+ * @throws \Zend_Controller_Response_Exception
+ */
+ protected function shipPowershellParams()
+ {
+ $db = $this->db();
+ $key = $this->params->getRequired('key');
+ $host = IcingaHost::loadWithApiKey($key, $db);
+
+ $settings = $this->getSettings();
+ $transform = $settings->get('self-service/transform_hostname');
+ $params = [
+ 'fetch_agent_name' => $settings->get('self-service/agent_name') === 'hostname',
+ 'fetch_agent_fqdn' => $settings->get('self-service/agent_name') === 'fqdn',
+ 'transform_hostname' => $transform,
+ 'flush_api_directory' => $settings->get('self-service/flush_api_dir') === 'y',
+ // ConvertEndpointIPConfig:
+ 'resolve_parent_host' => $settings->get('self-service/resolve_parent_host'),
+ // InstallFrameworkService:
+ 'install_framework_service' => '0',
+ // ServiceDirectory => framework_service_directory
+ // FrameworkServiceUrl => framework_service_url
+ // InstallFrameworkPlugins:
+ 'install_framework_plugins' => '0',
+ // PluginsUrl => framework_plugins_url
+ ];
+ $username = $settings->get('self-service/icinga_service_user');
+ if ($username !== null && strlen($username) > 0) {
+ $params['icinga_service_user'] = $username;
+ }
+
+ if ($transform === '2') {
+ $transformMethod = '.upperCase';
+ } elseif ($transform === '1') {
+ $transformMethod = '.lowerCase';
+ } else {
+ $transformMethod = '';
+ }
+
+ $hostObject = (object) [
+ 'address' => '&ipaddress&',
+ ];
+
+ switch ($settings->get('self-service/agent_name')) {
+ case 'hostname':
+ $hostObject->display_name = "&fqdn$transformMethod&";
+ break;
+ case 'fqdn':
+ $hostObject->display_name = "&hostname$transformMethod&";
+ break;
+ }
+ $params['director_host_object'] = json_encode($hostObject);
+
+ if ($settings->get('self-service/download_type')) {
+ $params['download_url'] = $settings->get('self-service/download_url');
+ $params['agent_version'] = $settings->get('self-service/agent_version');
+ $params['allow_updates'] = $settings->get('self-service/allow_updates') === 'y';
+ $params['agent_listen_port'] = $host->getAgentListenPort();
+ if ($hashes = $settings->get('self-service/installer_hashes')) {
+ $params['installer_hashes'] = $hashes;
+ }
+
+ if ($settings->get('self-service/install_nsclient') === 'y') {
+ $params['install_nsclient'] = true;
+ $this->addBooleanSettingsToParams($settings, [
+ 'nsclient_add_defaults',
+ 'nsclient_firewall',
+ 'nsclient_service',
+ ], $params);
+
+
+ $this->addStringSettingsToParams($settings, [
+ 'nsclient_directory',
+ 'nsclient_installer_path'
+ ], $params);
+ }
+ }
+
+ $this->addHostToParams($host, $params);
+
+ if ($this->getRequest()->getHeader('X-Director-Accept') === 'text/plain') {
+ echo $this->makePlainTextPowerShellArray($params);
+ } else {
+ $this->sendJson($this->getResponse(), $params);
+ }
+ }
+
+ /**
+ * @param IcingaHost $host
+ * @param array $params
+ * @throws NotFoundError
+ * @throws ProgrammingError
+ * @throws \Icinga\Exception\ConfigurationError
+ * @throws \Icinga\Exception\IcingaException
+ * @throws \Zend_Controller_Request_Exception
+ * @throws \Zend_Controller_Response_Exception
+ */
+ protected function addHostToParams(IcingaHost $host, array &$params)
+ {
+ if (! $host->isObject()) {
+ return;
+ }
+
+ $db = $this->db();
+ $settings = $this->getSettings();
+ $name = $host->getObjectName();
+ if ($host->getSingleResolvedProperty('has_agent') !== 'y') {
+ $this->sendPowerShellError(sprintf(
+ '%s is not configured for Icinga Agent usage',
+ $name
+ ), 403);
+ return;
+ }
+
+ $zoneName = $host->getRenderingZone();
+ if ($zoneName === IcingaHost::RESOLVE_ERROR) {
+ $this->sendPowerShellError(sprintf(
+ 'Could not resolve target Zone for %s',
+ $name
+ ), 404);
+ return;
+ }
+
+ $masterConnectsToAgent = $host->getSingleResolvedProperty(
+ 'master_should_connect'
+ ) === 'y';
+ $params['agent_add_firewall_rule'] = $masterConnectsToAgent;
+
+ $params['global_zones'] = $settings->get('self-service/global_zones');
+
+ $zone = IcingaZone::load($zoneName, $db);
+ $endpointNames = $zone->listEndpoints();
+ if (! $masterConnectsToAgent) {
+ $endpointsConfig = [];
+ foreach ($endpointNames as $endpointName) {
+ $endpoint = IcingaEndpoint::load($endpointName, $db);
+ $endpointsConfig[] = sprintf(
+ '%s;%s',
+ $endpoint->getSingleResolvedProperty('host'),
+ $endpoint->getResolvedPort()
+ );
+ }
+
+ $params['endpoints_config'] = $endpointsConfig;
+ }
+ $master = $db->getDeploymentEndpoint();
+ $params['parent_zone'] = $zoneName;
+ $params['ca_server'] = $master->getObjectName();
+ $params['parent_endpoints'] = $endpointNames;
+ $params['accept_config'] = $host->getSingleResolvedProperty('accept_config')=== 'y';
+ }
+
+ protected function addStringSettingsToParams(Settings $settings, array $keys, array &$params)
+ {
+ foreach ($keys as $key) {
+ $value = $settings->get("self-service/$key");
+ if (strlen($value)) {
+ $params[$key] = $value;
+ }
+ }
+ }
+
+ protected function addBooleanSettingsToParams(Settings $settings, array $keys, array &$params)
+ {
+ foreach ($keys as $key) {
+ $value = $settings->get("self-service/$key");
+ if ($value !== null) {
+ $params[$key] = $value === 'y';
+ }
+ }
+ }
+
+ /**
+ * @return Settings
+ * @throws \Icinga\Exception\ConfigurationError
+ */
+ protected function getSettings()
+ {
+ if ($this->settings === null) {
+ $this->settings = new Settings($this->db());
+ }
+
+ return $this->settings;
+ }
+}