Skip to content

Commit

Permalink
SHS-5811: Add "logout" button to sites (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
codechefmarc authored Dec 3, 2024
1 parent a22df73 commit 3c0a690
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 1 deletion.
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']);
}

}

0 comments on commit 3c0a690

Please sign in to comment.