1<?php 2 3namespace dokuwiki\plugin\twofactor; 4 5use dokuwiki\Extension\Plugin; 6use dokuwiki\Form\Form; 7use dokuwiki\Utf8\PhpString; 8 9/** 10 * Baseclass for all second factor providers 11 */ 12abstract class Provider extends Plugin 13{ 14 /** @var Settings */ 15 protected $settings; 16 17 /** @var string */ 18 protected $providerID; 19 20 /** 21 * Initializes the provider for the given user 22 * @param string $user Current user 23 * @throws \Exception 24 */ 25 public function __construct($user) 26 { 27 $this->providerID = substr(get_called_class(), strlen('helper_plugin_')); 28 $this->settings = new Settings($this->providerID, $user); 29 } 30 31 // region Introspection methods 32 33 /** 34 * The ID of this provider 35 * 36 * @return string 37 */ 38 public function getProviderID() 39 { 40 return $this->providerID; 41 } 42 43 /** 44 * Pretty Label for this provider 45 * 46 * @return string 47 */ 48 public function getLabel() 49 { 50 return PhpString::ucfirst($this->providerID); 51 } 52 53 // endregion 54 // region Configuration methods 55 56 /** 57 * Clear all settings 58 */ 59 public function reset() 60 { 61 $this->settings->purge(); 62 } 63 64 /** 65 * Has this provider been fully configured and verified by the user and thus can be used 66 * for authentication? 67 * 68 * @return bool 69 */ 70 abstract public function isConfigured(); 71 72 /** 73 * Render the configuration form 74 * 75 * This method should add the needed form elements to (re)configure the provider. 76 * The contents of the form may change depending on the current settings. 77 * 78 * No submit button should be added - this is handled by the main plugin. 79 * 80 * @param Form $form The initial form to add elements to 81 * @return Form 82 */ 83 abstract public function renderProfileForm(Form $form); 84 85 /** 86 * Handle any input data 87 * 88 * @return void 89 */ 90 abstract public function handleProfileForm(); 91 92 // endregion 93 // region OTP methods 94 95 /** 96 * Create and store a new secret for this provider 97 * 98 * @return string the new secret 99 * @throws \Exception when no suitable random source is available 100 */ 101 public function initSecret() 102 { 103 $ga = new GoogleAuthenticator(); 104 $secret = $ga->createSecret(); 105 106 $this->settings->set('secret', $secret); 107 return $secret; 108 } 109 110 /** 111 * Get the secret for this provider 112 * 113 * @return string 114 */ 115 public function getSecret() 116 { 117 return $this->settings->get('secret'); 118 } 119 120 /** 121 * Generate an auth code 122 * 123 * @return string 124 * @throws \Exception when no code can be created 125 */ 126 public function generateCode() 127 { 128 $secret = $this->settings->get('secret'); 129 if (!$secret) throw new \Exception('No secret for provider ' . $this->getProviderID()); 130 131 $ga = new GoogleAuthenticator(); 132 return $ga->getCode($secret); 133 } 134 135 /** 136 * Check the given code 137 * 138 * @param string $code 139 * @param int $tolerance 140 * @return bool 141 * @throws \Exception when no code can be created 142 */ 143 public function checkCode($code, $tolerance = 2) 144 { 145 $secret = $this->settings->get('secret'); 146 if (!$secret) throw new \Exception('No secret for provider ' . $this->getProviderID()); 147 148 $ga = new GoogleAuthenticator(); 149 return $ga->verifyCode($secret, $code, $tolerance); 150 } 151 152 /** 153 * Transmits the code to the user 154 * 155 * If a provider does not transmit anything (eg. TOTP) simply 156 * return the message. 157 * 158 * @param string $code The code to transmit 159 * @return string Informational message for the user 160 * @throw \Exception when the message can't be sent 161 */ 162 abstract public function transmitMessage($code); 163 164 // endregion 165} 166