Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SHS-5811: Add "logout" button to sites #21

Merged
merged 8 commits into from
Dec 3, 2024
2 changes: 1 addition & 1 deletion src/Plugin/Block/SamlLoginBlock.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*
* @Block(
* id = "stanford_samlauth_login_block",
* admin_label = @Translation("SAML SUNetID Block")
* admin_label = @Translation("SAML SUNetID Login Block")
* )
*/
class SamlLoginBlock extends BlockBase implements ContainerFactoryPluginInterface {
Expand Down
129 changes: 129 additions & 0 deletions src/Plugin/Block/SamlLogoutBlock.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php

namespace Drupal\stanford_samlauth\Plugin\Block;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;

/**
* Provides a 'Saml Logout Block' block.
*
* @Block(
* id = "stanford_samlauth_logout_block",
* admin_label = @Translation("SAML SUNetID Logout Block")
* )
*/
class SamlLogoutBlock extends BlockBase implements ContainerFactoryPluginInterface {

/**
* Current uri the block is displayed on.
*
* @var string
*/
protected $currentUri;

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('request_stack')
);
}

/**
* Block constructor.
*
* @param array $configuration
* Configuration settings.
* @param string $plugin_id
* Block machine name.
* @param array $plugin_definition
* Plugin definition.
* @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
* Current request stack object.
*/
public function __construct(array $configuration, string $plugin_id, array $plugin_definition, RequestStack $requestStack) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->currentUri = $requestStack->getCurrentRequest() ?->getPathInfo();
}

/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return ['link_text' => 'SUNetID Logout'] + parent::defaultConfiguration();
}

/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
$form['link_text'] = [
'#type' => 'textfield',
'#title' => $this->t('SUNetID log-out link text'),
'#description' => $this->t('Add text to show a link for authenticated users.'),
'#default_value' => $this->configuration['link_text'],
'#required' => TRUE,
];
return $form;
}

/**
* {@inheritdoc}
*/
public function getCacheContexts() {
$context = parent::getCacheContexts();
// Make the block cache different for each page since the login link has a
// destination parameter.
return Cache::mergeContexts($context, ['url.path']);
}

/**
* {@inheritdoc}
*/
public function access(AccountInterface $account, $return_as_object = FALSE) {
$access = AccessResult::allowedIf($account->isAuthenticated());
return $return_as_object ? $access : $access->isAllowed();
}

/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
$this->configuration['link_text'] = $form_state->getValue('link_text');
}

/**
* {@inheritdoc}
*/
public function build() {
$url = Url::fromRoute('user.logout', ['destination' => $this->currentUri]);
$build = [];
$build['logout'] = [
'#type' => 'html_tag',
'#tag' => 'a',
'#value' => $this->configuration['link_text'],
'#attributes' => [
'rel' => 'nofollow',
'href' => $url->toString(),
'class' => [
'su-button',
'decanter-button',
],
],
];
return $build;
}

}
96 changes: 96 additions & 0 deletions tests/src/Unit/Plugin/Block/SamlLogoutBlockTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

namespace Drupal\Tests\stanford_samlauth\Unit\Plugin\Block;

use Drupal\Core\Cache\Context\CacheContextsManager;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Form\FormState;
use Drupal\Core\Routing\UrlGeneratorInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\stanford_samlauth\Plugin\Block\SamlLogoutBlock;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\RequestStack;

/**
* Class SamlLogoutBlockTest
*
* @package Drupal\Tests\stanford_samlauth\Unit\Plugin\Block
* @covers \Drupal\stanford_samlauth\Plugin\Block\SamlLoginBlock
*/
class SamlLogoutBlockTest extends UnitTestCase {

/**
* The block plugin.
*
* @var \Drupal\stanford_samlauth\Plugin\Block\SamlLogoutBlock
*/
protected $block;

/**
* {@inheritDoc}
*/
public function setup(): void {
parent::setUp();

$url_generator = $this->createMock(UrlGeneratorInterface::class);
$url_generator->method('generateFromRoute')->willReturn('/foo-bar');

$request_stack = new RequestStack();

$context_manager = $this->createMock(CacheContextsManager::class);
$context_manager->method('assertValidTokens')->willReturn(TRUE);

$container = new ContainerBuilder();
$container->set('string_translation', $this->getStringTranslationStub());
$container->set('url_generator', $url_generator);
$container->set('request_stack', $request_stack);
$container->set('cache_contexts_manager', $context_manager);
\Drupal::setContainer($container);

$this->block = SamlLogoutBlock::create($container, [], 'saml_logout', ['provider' => 'stanford_samlauth']);
}

/**
* Test configuration and form methods.
*/
public function testBlock() {
$this->assertEquals(['link_text' => 'SUNetID Logout'], $this->block->defaultConfiguration());
$form_state = new FormState();
$form = $this->block->blockForm([], $form_state);
$this->assertCount(1, $form);
$this->assertArrayHasKey('link_text', $form);

$link_text = $this->getRandomGenerator()->string();
$form_state->setValue('link_text', $link_text);
$this->block->blockSubmit($form, $form_state);
$new_config = $this->block->getConfiguration();
$this->assertEquals($link_text, $new_config['link_text']);
}

/**
* Test anonymous users would access the block, authenticated would not.
*/
public function testAccess() {
$this->assertContains('url.path', $this->block->getCacheContexts());

$account = $this->createMock(AccountInterface::class);
$account->method('isAuthenticated')->willReturn(TRUE);
$this->assertTrue($this->block->access($account));

$account = $this->createMock(AccountInterface::class);
$account->method('isAuthenticated')->willReturn(FALSE);
$this->assertFALSE($this->block->access($account));
}

/**
* Test build render array is structured correctly.
*/
public function testBuild() {
$build = $this->block->build();
$this->assertCount(1, $build);
$this->assertArrayHasKey('logout', $build);
$this->assertEquals('html_tag', $build['logout']['#type']);
$this->assertEquals('/foo-bar', $build['logout']['#attributes']['href']);
}

}
Loading