3 namespace Drupal\security_review\Form;
5 use Drupal\Core\Config\ConfigFactoryInterface;
6 use Drupal\Core\Datetime\DateFormatterInterface;
7 use Drupal\Core\Form\ConfigFormBase;
8 use Drupal\Core\Form\FormStateInterface;
9 use Drupal\Core\Session\AccountInterface;
10 use Drupal\security_review\Checklist;
11 use Drupal\security_review\Security;
12 use Drupal\security_review\SecurityReview;
13 use Symfony\Component\DependencyInjection\ContainerInterface;
16 * Settings page for Security Review.
18 class SettingsForm extends ConfigFormBase {
21 * The security_review.checklist service.
23 * @var \Drupal\security_review\Checklist
28 * The security_review.security service.
30 * @var \Drupal\security_review\Security
35 * The security_review service.
37 * @var \Drupal\security_review\SecurityReview
39 protected $securityReview;
42 * The date.formatter service.
44 * @var \Drupal\Core\Datetime\DateFormatterInterface
46 private $dateFormatter;
49 * Constructs a SettingsForm.
51 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
53 * @param \Drupal\security_review\Checklist $checklist
54 * The security_review.checklist service.
55 * @param \Drupal\security_review\Security $security
56 * The security_review.security service.
57 * @param \Drupal\security_review\SecurityReview $security_review
58 * The security_review service.
59 * @param \Drupal\Core\Datetime\DateFormatterInterface $dateFormatter
60 * The date.formatter service.
62 public function __construct(ConfigFactoryInterface $config_factory, Checklist $checklist, Security $security, SecurityReview $security_review, DateFormatterInterface $dateFormatter) {
63 parent::__construct($config_factory);
64 $this->checklist = $checklist;
65 $this->security = $security;
66 $this->securityReview = $security_review;
67 $this->dateFormatter = $dateFormatter;
73 public static function create(ContainerInterface $container) {
75 $container->get('config.factory'),
76 $container->get('security_review.checklist'),
77 $container->get('security_review.security'),
78 $container->get('security_review'),
79 $container->get('date.formatter')
86 public function getFormId() {
87 return 'security-review-settings';
93 public function buildForm(array $form, FormStateInterface $form_state) {
94 // Get the list of checks.
95 $checks = $this->checklist->getChecks();
97 // Get the user roles.
98 $roles = user_roles();
100 foreach ($roles as $rid => $role) {
101 $options[$rid] = $role->label();
104 // Notify the user if anonymous users can create accounts.
106 if (in_array(AccountInterface::AUTHENTICATED_ROLE, $this->security->defaultUntrustedRoles())) {
107 $message = $this->t('You have allowed anonymous users to create accounts without approval so the authenticated role defaults to untrusted.');
110 // Show the untrusted roles form element.
111 $form['untrusted_roles'] = [
112 '#type' => 'checkboxes',
113 '#title' => $this->t('Untrusted roles'),
114 '#description' => $this->t(
115 'Define which roles are for less trusted users. The anonymous role defaults to untrusted. @message Most Security Review checks look for resources usable by untrusted roles.',
116 ['@message' => $message]
118 '#options' => $options,
119 '#default_value' => $this->security->untrustedRoles(),
122 $form['advanced'] = [
123 '#type' => 'details',
124 '#title' => $this->t('Advanced'),
128 // Show the logging setting.
129 $form['advanced']['logging'] = [
130 '#type' => 'checkbox',
131 '#title' => $this->t('Log checklist results and skips'),
132 '#description' => $this->t('The result of each check and skip can be logged to watchdog for tracking.'),
133 '#default_value' => $this->securityReview->isLogging(),
139 foreach ($checks as $check) {
140 // Determine if check is being skipped.
141 if ($check->isSkipped()) {
142 $values[] = $check->id();
144 '@name <em>skipped by UID @uid on @date</em>',
146 '@name' => $check->getTitle(),
147 '@uid' => $check->skippedBy()->id(),
148 '@date' => $this->dateFormatter->format($check->skippedOn()),
153 $label = $check->getTitle();
155 $options[$check->id()] = $label;
157 $form['advanced']['skip'] = [
158 '#type' => 'checkboxes',
159 '#title' => $this->t('Checks to skip'),
160 '#description' => $this->t('Skip running certain checks. This can also be set on the <em>Run & review</em> page. It is recommended that you do not skip any checks unless you know the result is wrong or the process times out while running.'),
161 '#options' => $options,
162 '#default_value' => $values,
165 // Iterate through checklist and get check-specific setting pages.
166 foreach ($checks as $check) {
167 // Get the check's setting form.
168 $check_form = $check->settings()->buildForm();
170 // If not empty, add it to the form.
171 if (!empty($check_form)) {
172 // If this is the first non-empty setting page initialize the 'details'
173 if (!isset($form['advanced']['check_specific'])) {
174 $form['advanced']['check_specific'] = [
175 '#type' => 'details',
176 '#title' => $this->t('Check-specific settings'),
183 $sub_form = &$form['advanced']['check_specific'][$check->id()];
185 $title = $check->getTitle();
186 // If it's an external check, show its namespace.
187 if ($check->getMachineNamespace() != 'security_review') {
188 $title .= $this->t('%namespace', [
189 '%namespace' => $check->getNamespace(),
193 '#type' => 'details',
197 'form' => $check_form,
202 // Return the finished form.
203 return parent::buildForm($form, $form_state);
209 public function validateForm(array &$form, FormStateInterface $form_state) {
210 // Run validation for check-specific settings.
211 if (isset($form['advanced']['check_specific'])) {
212 $check_specific_values = $form_state->getValue('check_specific');
213 foreach ($this->checklist->getChecks() as $check) {
214 $check_form = &$form['advanced']['check_specific'][$check->id()];
215 if (isset($check_form)) {
217 ->validateForm($check_form, $check_specific_values[$check->id()]);
226 public function submitForm(array &$form, FormStateInterface $form_state) {
227 // Frequently used configuration items.
228 $check_settings = $this->config('security_review.checks');
230 // Save that the module has been configured.
231 $this->securityReview->setConfigured(TRUE);
233 // Save the new untrusted roles.
234 $untrusted_roles = array_keys(array_filter($form_state->getValue('untrusted_roles')));
235 $this->securityReview->setUntrustedRoles($untrusted_roles);
237 // Save the new logging setting.
238 $logging = $form_state->getValue('logging') == 1;
239 $this->securityReview->setLogging($logging);
241 // Skip selected checks.
242 $skipped = array_keys(array_filter($form_state->getValue('skip')));
243 foreach ($this->checklist->getChecks() as $check) {
244 if (in_array($check->id(), $skipped)) {
252 // Save the check-specific settings.
253 if (isset($form['advanced']['check_specific'])) {
254 $check_specific_values = $form_state->getValue('check_specific');
255 foreach ($check_specific_values as $id => $values) {
256 // Get corresponding Check.
257 $check = $this->checklist->getCheckById($id);
259 // Submit parameters.
260 $check_form = &$form['advanced']['check_specific'][$id]['form'];
261 $check_form_values = $check_specific_values[$id]['form'];
264 $check->settings()->submitForm($check_form, $check_form_values);
268 // Commit the settings.
269 $check_settings->save();
271 // Finish submitting the form.
272 parent::submitForm($form, $form_state);
278 protected function getEditableConfigNames() {
279 return ['security_review.checks'];