├── .gitignore ├── pages ├── index.php └── docs.php ├── plugins ├── auth │ ├── lib │ │ ├── otp │ │ │ ├── exception.php │ │ │ ├── method_interface.php │ │ │ ├── method_totp.php │ │ │ ├── password.php │ │ │ ├── method_email.php │ │ │ └── password_config.php │ │ ├── injections │ │ │ ├── abstract.php │ │ │ ├── passwordchange.php │ │ │ └── termsofuse.php │ │ ├── yform │ │ │ ├── value │ │ │ │ ├── ycom_auth_load_user.php │ │ │ │ ├── ycom_auth_logout.php │ │ │ │ ├── ycom_auth_returnto.php │ │ │ │ ├── ycom_auth_otp.php │ │ │ │ ├── ycom_auth_password.php │ │ │ │ └── ycom_user_token.php │ │ │ ├── validate │ │ │ │ ├── ycom_auth_password.php │ │ │ │ ├── ycom_auth_login.php │ │ │ │ └── ycom_auth.php │ │ │ └── action │ │ │ │ └── ycom_auth_db.php │ │ ├── ycom_auth_rules.php │ │ └── ycom_user_token.php │ ├── ytemplates │ │ └── bootstrap │ │ │ ├── value.ycom_auth_extern.tpl.php │ │ │ ├── value.ycom_auth_saml.tpl.php │ │ │ └── value.ycom_auth_otp_verify.tpl.php │ ├── pages │ │ ├── log.php │ │ ├── index.php │ │ ├── content.ycom_auth.php │ │ ├── tokens.php │ │ ├── system.log.ycom_user.php │ │ └── sessions.php │ ├── update.php │ ├── install │ │ ├── oauth2.php │ │ └── cas.php │ ├── package.yml │ ├── lang │ │ ├── pt_br.lang │ │ ├── sv_se.lang │ │ ├── es_es.lang │ │ └── en_gb.lang │ ├── assets │ │ ├── styles.css │ │ └── clipboard-copy-element.js │ ├── install.php │ └── boot.php ├── group │ ├── update.php │ ├── package.yml │ ├── install.php │ ├── lang │ │ ├── en_gb.lang │ │ ├── sv_se.lang │ │ ├── de_de.lang │ │ ├── es_es.lang │ │ └── pt_br.lang │ ├── uninstall.php │ ├── boot.php │ └── lib │ │ └── ycom_group.php └── media_auth │ ├── update.php │ ├── uninstall.php │ ├── package.yml │ ├── install.php │ ├── lang │ ├── en_gb.lang │ ├── sv_se.lang │ ├── es_es.lang │ └── de_de.lang │ ├── pages │ ├── index.php │ └── settings.php │ └── lib │ ├── ycom_media_auth.php │ └── ycom_media_auth_rules.php ├── update.php ├── .php-cs-fixer.dist.php ├── .tools ├── bootstrap.php ├── composer-code-style.json └── rexstan.php ├── tests └── unit │ ├── login_test.php │ └── user_status_test.php ├── lib ├── yform │ └── value │ │ ├── ycom_user_init.php │ │ └── ycom_user.php ├── ycom_config.php ├── ycom.php ├── ycom_log.php └── ycom_user.php ├── .github └── workflows │ ├── publish-to-redaxo.yml │ ├── code-style.yml │ ├── rexstan.yml │ └── phpunit.yml ├── phpunit.xml.dist ├── package.yml ├── LICENSE.md ├── install.php ├── uninstall.php ├── README.md ├── composer.json ├── assets └── ycom_backend.js ├── lang ├── pt_br.lang ├── en_gb.lang ├── es_es.lang ├── sv_se.lang └── de_de.lang ├── docs ├── 04_navigations.md ├── 01_intro.md ├── 10_otp.md ├── 07_plugin_media_auth.md ├── 12_status_examples.md ├── 06_user_groups.md ├── 08_extern_auth.md ├── 02_settings.md └── 09_tricks.md └── boot.php /.gitignore: -------------------------------------------------------------------------------- 1 | .phpunit.result.cache 2 | .php-cs-fixer.cache 3 | vendor/ 4 | -------------------------------------------------------------------------------- /pages/index.php: -------------------------------------------------------------------------------- 1 | includeFile(__DIR__ . '/install.php'); 9 | -------------------------------------------------------------------------------- /plugins/group/update.php: -------------------------------------------------------------------------------- 1 | includeFile(__DIR__ . '/install.php'); 9 | -------------------------------------------------------------------------------- /plugins/media_auth/update.php: -------------------------------------------------------------------------------- 1 | includeFile(__DIR__ . '/install.php'); 9 | -------------------------------------------------------------------------------- /plugins/group/package.yml: -------------------------------------------------------------------------------- 1 | package: ycom/group 2 | version: '4.4.2' 3 | author: Yakamara Media GmbH & Co. KG 4 | title: 'Group' 5 | nav_icon: fa-minus 6 | description: 'translate:ycom_group_plugin' 7 | 8 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | in(__DIR__) 7 | ; 8 | 9 | return (new Redaxo\PhpCsFixerConfig\Config()) 10 | ->setFinder($finder) 11 | ; 12 | -------------------------------------------------------------------------------- /plugins/auth/ytemplates/bootstrap/value.ycom_auth_extern.tpl.php: -------------------------------------------------------------------------------- 1 | ' . $name . ''; 12 | -------------------------------------------------------------------------------- /plugins/auth/ytemplates/bootstrap/value.ycom_auth_saml.tpl.php: -------------------------------------------------------------------------------- 1 | ' . $name . ''; 12 | -------------------------------------------------------------------------------- /plugins/auth/lib/injections/abstract.php: -------------------------------------------------------------------------------- 1 | removeColumn('ycom_auth_type') 10 | ->removeColumn('ycom_group_type') 11 | ->removeColumn('ycom_groups') 12 | ->alter(); 13 | -------------------------------------------------------------------------------- /plugins/group/install.php: -------------------------------------------------------------------------------- 1 | ensureColumn(new rex_sql_column('ycom_group_type', 'int', false, '0')) 10 | ->ensureColumn(new rex_sql_column('ycom_groups', 'text')) 11 | ->alter() 12 | ; 13 | -------------------------------------------------------------------------------- /plugins/auth/pages/log.php: -------------------------------------------------------------------------------- 1 | i18n('ycom_title')); 9 | 10 | // this file integrates the already existing log-viewer as a syslog page. 11 | // the required registration wiring can be found in the package.yml 12 | require __DIR__ . '/system.log.ycom_user.php'; 13 | -------------------------------------------------------------------------------- /plugins/media_auth/package.yml: -------------------------------------------------------------------------------- 1 | package: ycom/media_auth 2 | version: '4.4.2' 3 | author: Yakamara Media GmbH & Co. KG 4 | title: 'MediaAuth' 5 | nav_icon: fa-minus 6 | description: 'translate:ycom_media_auth' 7 | 8 | page: 9 | title: 'translate:ycom_media_auth_title' 10 | hidden: false 11 | subpages: 12 | settings: { title: 'translate:ycom_media_auth_settings'} 13 | -------------------------------------------------------------------------------- /.tools/bootstrap.php: -------------------------------------------------------------------------------- 1 | includeFile(__DIR__ . '/install.php'); 16 | -------------------------------------------------------------------------------- /plugins/media_auth/install.php: -------------------------------------------------------------------------------- 1 | ensureColumn(new rex_sql_column('ycom_auth_type', 'int(11)', false, '0')) 10 | ->ensureColumn(new rex_sql_column('ycom_group_type', 'int(11)', false, '0')) 11 | ->ensureColumn(new rex_sql_column('ycom_groups', 'text')) 12 | ->alter(); 13 | -------------------------------------------------------------------------------- /plugins/auth/install/oauth2.php: -------------------------------------------------------------------------------- 1 | 'demoapp', // The client ID assigned to you by the provider 5 | 'clientSecret' => 'demopass', // The client password assigned to you by the provider 6 | 'redirectUri' => '', // do not fill out first and wait for the login error message to fill it out 7 | 'urlAuthorize' => '', 8 | 'urlAccessToken' => '', 9 | 'urlResourceOwnerDetails' => '', 10 | ]; 11 | -------------------------------------------------------------------------------- /plugins/group/lang/en_gb.lang: -------------------------------------------------------------------------------- 1 | 2 | ycom_group_title = Groups 3 | ycom_group_name = Groups 4 | ycom_group_yform_enter_name = Please enter the group name 5 | rex_ycom_group = Community Group 6 | 7 | ycom_group_forallgroups = For all, independent of group 8 | ycom_group_inallgroups = Has to be in every group 9 | ycom_group_inonegroup = Has to be in one group 10 | ycom_group_nogroups = Has no groups 11 | 12 | ycom_group_type = Group permissions 13 | ycom_groups = Groups 14 | -------------------------------------------------------------------------------- /plugins/group/lang/sv_se.lang: -------------------------------------------------------------------------------- 1 | 2 | ycom_group_title = Grupper 3 | ycom_group_name = Grupper 4 | ycom_group_yform_enter_name = Var god ange ett gruppnamn 5 | rex_ycom_group = Community Group 6 | 7 | ycom_group_forallgroups = Till alla, oavsett grupp 8 | ycom_group_inallgroups = Måste vara med i alla grupper 9 | ycom_group_inonegroup = Måste vara i en grupp 10 | ycom_group_nogroups = Har inga grupper 11 | 12 | ycom_group_type = Gruppbehörigheter 13 | ycom_groups = Grupper 14 | -------------------------------------------------------------------------------- /plugins/group/lang/de_de.lang: -------------------------------------------------------------------------------- 1 | 2 | ycom_group_title = Gruppen 3 | ycom_group_name = Gruppen 4 | ycom_group_yform_enter_name = Bitte geben Sie den Gruppennamen ein. 5 | rex_ycom_group = Community Group 6 | 7 | ycom_group_forallgroups = Für alle, egal welche Gruppe 8 | ycom_group_inallgroups = Muss in jeder Gruppe sein 9 | ycom_group_inonegroup = Muss in einer Gruppe sein 10 | ycom_group_nogroups = Hat keine Gruppen 11 | 12 | ycom_group_type = Gruppenrechte 13 | ycom_groups = Gruppen 14 | -------------------------------------------------------------------------------- /plugins/group/lang/es_es.lang: -------------------------------------------------------------------------------- 1 | 2 | ycom_group_title = Grupo 3 | ycom_group_name = Grupo 4 | ycom_group_yform_enter_name = Por favor ingrese el nombre del grupo. 5 | rex_ycom_group = Grupo Comunitario 6 | 7 | ycom_group_forallgroups = Para todos, no importa qué grupo 8 | ycom_group_inallgroups = Debe estar en cada grupo 9 | ycom_group_inonegroup = Debe estar en un grupo 10 | ycom_group_nogroups = No tiene grupos 11 | 12 | ycom_group_type = Los derechos de grupo 13 | ycom_groups = Grupo 14 | -------------------------------------------------------------------------------- /plugins/group/lang/pt_br.lang: -------------------------------------------------------------------------------- 1 | 2 | ycom_group_title = Grupos 3 | ycom_group_name = Grupos 4 | ycom_group_yform_enter_name = Por favor, insira o nome do grupo 5 | rex_ycom_group = Grupo comunitário 6 | 7 | ycom_group_forallgroups = Para todos, independente do grupo 8 | ycom_group_inallgroups = Tem que estar em todos os grupos 9 | ycom_group_inonegroup = Tem que estar em um grupo 10 | ycom_group_nogroups = Não tem grupos 11 | 12 | ycom_group_type = Permissões de grupo 13 | ycom_groups = Grupos 14 | -------------------------------------------------------------------------------- /plugins/auth/install/cas.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'protocol' => 'https://', 6 | 'host' => 'xxxxxx.com', 7 | 'uri' => '/cas', 8 | 'port' => 443, 9 | 'CasServerValidation' => false, 10 | 'CasServerCACertPath' => rex_addon::get('ycom')->getDataPath('cas_cert.pem'), 11 | 'ServerVersion' => '2.0', // '2.0' => CAS_VERSION_2_0, '3.0' => CAS_VERSION_3_0, 'S1' => SAML_VERSION_1_1 12 | ], 13 | 'debug' => false, 14 | 'debugPath' => rex_path::log('ycom_auth_cas.log'), 15 | ]; 16 | -------------------------------------------------------------------------------- /plugins/auth/lib/otp/method_interface.php: -------------------------------------------------------------------------------- 1 | 'admin', 14 | 'loginPassword' => 'admin', 15 | 'filter' => [], 16 | 'loginStay' => false, 17 | 'ignorePassword' => false, 18 | ]; 19 | 20 | $status = rex_ycom_auth::login($params); 21 | self::assertEquals(rex_ycom_auth::STATUS_LOGIN_FAILED, $status); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/yform/value/ycom_user_init.php: -------------------------------------------------------------------------------- 1 | params['main_table'] = rex::getTablePrefix() . 'ycom_user'; 9 | $this->params['main_id'] = rex_ycom_auth::getUser()->getId(); 10 | } else { 11 | $this->params['warning'][$this->getId()] = $this->getElement(3); 12 | } 13 | } 14 | 15 | public function getDescription(): string 16 | { 17 | return 'ycom_user_init -> Beispiel: ycom_user_init|[label]|[name]|error_msg'; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/publish-to-redaxo.yml: -------------------------------------------------------------------------------- 1 | name: Publish release 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | redaxo_publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: shivammathur/setup-php@v2 14 | with: 15 | php-version: "8.2" 16 | - uses: ramsey/composer-install@v2 17 | with: 18 | composer-options: "--no-dev" 19 | - uses: FriendsOfREDAXO/installer-action@v1 20 | with: 21 | myredaxo-username: ${{ secrets.MYREDAXO_USERNAME }} 22 | myredaxo-api-key: ${{ secrets.MYREDAXO_API_KEY }} 23 | description: ${{ github.event.release.body }} 24 | 25 | -------------------------------------------------------------------------------- /lib/ycom_config.php: -------------------------------------------------------------------------------- 1 | getConfig() ?? []; 16 | self::$config = rex_extension::registerPoint(new rex_extension_point('YCOM_CONFIG', self::$config)); 17 | 18 | return self::$config; 19 | } 20 | 21 | public static function get(string $key, $default = null): mixed 22 | { 23 | self::init(); 24 | return self::$config[$key] ?? $default; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /plugins/media_auth/lang/en_gb.lang: -------------------------------------------------------------------------------- 1 | ycom_media_auth_title = Media 2 | ycom_media_auth_settings = Settings 3 | ycom_auth_config_media_auth_rules = Rule for denying media 4 | 5 | ycom_media_auth_failed_header_notfound = send header `Not Found` 6 | ycom_media_auth_failed_header_perm_denied = send header `Permission denied` 7 | ycom_media_auth_failed_redirect_login = Redirect to login with redirect to media (if not logged in). In case of error with 401 response header (if logged in) 8 | ycom_media_auth_failed_redirect_login_with_error_page = Redirect to login with redirect to media (if not logged in). In case of erro 9 | ycom_media_auth_failed_header_redirect_notfound = Redirect to NotFound article 10 | 11 | ycom_media_auth_settings_updated = Settings updated 12 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | tests/unit 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.tools/composer-code-style.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yakamara/ycom", 3 | "require": { 4 | "onelogin/php-saml": "^3", 5 | "apereo/phpcas": "^1", 6 | "league/oauth2-client": "^2", 7 | "spomky-labs/otphp": "^11.0", 8 | "redaxo/php-cs-fixer-config": "^2.0", 9 | "friendsofphp/php-cs-fixer": "^3.14", 10 | "phpunit/phpunit": "^9.5", 11 | "symfony/console": "^6.4" 12 | }, 13 | "config": { 14 | "platform": { 15 | "php": "8.1.13" 16 | } 17 | }, 18 | "scripts": { 19 | "cs-dry": "php-cs-fixer fix -v --ansi --dry-run --config=.php-cs-fixer.dist.php", 20 | "cs-fix": "php-cs-fixer fix -v --ansi --config=.php-cs-fixer.dist.php", 21 | "unit-test": "phpunit --testdox" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /plugins/media_auth/lang/sv_se.lang: -------------------------------------------------------------------------------- 1 | ycom_media_auth_title = Media 2 | ycom_media_auth_settings = Inställningar 3 | ycom_auth_config_media_auth_rules = Regel om mediet inte kan öppnas 4 | 5 | ycom_media_auth_failed_header_notfound = Skicka Header `Not Found` 6 | ycom_media_auth_failed_header_perm_denied = Skicka Header `Permission denied` 7 | ycom_media_auth_failed_redirect_login = Ompekning till inloggning med redirect till medium (om inte inloggad) 8 | ycom_media_auth_failed_redirect_login_with_error_page = Omdirigera till inloggning med Redirect till medium (om inte inloggad). Vid fel vidarebefordran till felsida (om du är inloggad) 9 | ycom_media_auth_failed_header_redirect_notfound = Vidarebefordring till NotFound Article 10 | 11 | ycom_media_auth_settings_updated = Inställningarna aktualiserades 12 | -------------------------------------------------------------------------------- /package.yml: -------------------------------------------------------------------------------- 1 | package: ycom 2 | version: '4.4.2' 3 | author: Yakamara Media GmbH & Co. KG 4 | supportpage: https://github.com/yakamara/redaxo_ycom/ 5 | 6 | page: 7 | title: 'translate:ycom_navi_title' 8 | perm: admin[] 9 | block: ycom 10 | icon: rex-icon fa-group 11 | subpages: 12 | docs: 13 | title: 'translate:ycom_docs' 14 | perm: admin[] 15 | pjax: false 16 | itemClass: pull-right 17 | icon: rex-icon fa-info-circle 18 | href: ?page=ycom/docs 19 | 20 | requires: 21 | php: '>=8.2' 22 | packages: 23 | yform: '>=3.2,<6.0.0-dev' 24 | yrewrite: '>=2.6' 25 | redaxo: '^5.17.0' 26 | 27 | installer_ignore: 28 | - .tools 29 | - .github 30 | 31 | system_plugins: 32 | - auth 33 | - group 34 | -------------------------------------------------------------------------------- /plugins/group/uninstall.php: -------------------------------------------------------------------------------- 1 | setQuery('DELETE FROM `' . rex::getTable('yform_table') . '` WHERE table_name = "' . rex::getTable('ycom_group') . '"'); 10 | $sql->setQuery('DELETE FROM `' . rex::getTable('yform_field') . '` WHERE table_name = "' . rex::getTable('ycom_group') . '"'); 11 | $sql->setQuery('DELETE FROM `' . rex::getTable('yform_field') . '` WHERE table_name = "' . rex::getTable('ycom_user') . '" and name="ycom_groups"'); 12 | $sql->setQuery('DELETE FROM `' . rex::getTable('yform_history') . '` WHERE table_name = "' . rex::getTable('ycom_group') . '"'); 13 | 14 | rex_sql_table::get(rex::getTable('ycom_group')) 15 | ->drop(); 16 | 17 | rex_yform_manager_table::deleteCache(); 18 | -------------------------------------------------------------------------------- /plugins/auth/package.yml: -------------------------------------------------------------------------------- 1 | package: ycom/auth 2 | version: '4.4.2' 3 | author: Yakamara Media GmbH & Co. KG 4 | title: 'Auth' 5 | nav_icon: fa-minus 6 | description: 'translate:ycom_auth_plugin' 7 | 8 | page: 9 | title: 'translate:ycom_auth_title' 10 | hidden: false 11 | subpages: 12 | settings: 13 | title: 'translate:ycom_auth_settings' 14 | sessions: 15 | title: 'translate:ycom_current_sessions' 16 | tokens: 17 | title: 'translate:ycom_current_tokens' 18 | log: 19 | title: 'translate:ycom_log' 20 | 21 | pages: 22 | system/log/ycom_user: 23 | title: translate:ycom_user_log 24 | perm: admin 25 | 26 | default_config: 27 | auth_cookie_ttl: '14' 28 | auth_rule: 'login_try_5_pause' 29 | login_field: 'email' 30 | -------------------------------------------------------------------------------- /plugins/media_auth/lang/es_es.lang: -------------------------------------------------------------------------------- 1 | ycom_media_auth_title = Media 2 | ycom_media_auth_settings = Ajustes 3 | ycom_auth_config_media_auth_rules = Regla si no se puede llamar medio 4 | 5 | ycom_media_auth_failed_header_notfound = Encabezado `Not Found` senden 6 | ycom_media_auth_failed_header_perm_denied = Encabezado `Permiso denegado` senden 7 | ycom_media_auth_failed_redirect_login = Redirigir al inicio de sesión con Redirigir a medio (si no se ha iniciado sesión) 8 | ycom_media_auth_failed_redirect_login_with_error_page = Reenvío para iniciar sesión con redireccionamiento en media (si no está conectado). Si los errores se reenvían a la página de error (si está conectado) 9 | ycom_media_auth_failed_header_redirect_notfound = Reenvío a artículo NotFound 10 | 11 | ycom_media_auth_settings_updated = Configuraciones se han actualizado 12 | -------------------------------------------------------------------------------- /plugins/media_auth/lang/de_de.lang: -------------------------------------------------------------------------------- 1 | ycom_media_auth_title = Medien 2 | ycom_media_auth_settings = Einstellungen 3 | ycom_auth_config_media_auth_rules = Regel wenn Medium nicht aufgerufen werden darf 4 | 5 | ycom_media_auth_failed_header_notfound = Header `Not Found` senden 6 | ycom_media_auth_failed_header_perm_denied = Header `Permission denied` senden 7 | ycom_media_auth_failed_redirect_login = Weiterleitung zu Login mit Redirect auf Medium (wenn nicht eingeloggt). Bei Fehler 401 Header (wenn eingeloggt) 8 | ycom_media_auth_failed_redirect_login_with_error_page = Weiterleitung zu Login mit Redirect auf Medium (wenn nicht eingeloggt). Bei Fehler Weiterleitung auf Error-Seite (wenn eingeloggt) 9 | ycom_media_auth_failed_header_redirect_notfound = Weiterleitung zu NotFound Article 10 | 11 | ycom_media_auth_settings_updated = Einstellungen wurden aktualisiert 12 | -------------------------------------------------------------------------------- /plugins/auth/pages/index.php: -------------------------------------------------------------------------------- 1 | i18n('title')); // $this->i18n('title') ist eine Kurzform für rex_i18n::msg('dummy_title') 9 | 10 | // Die Subpages werden nicht mehr über den "subpage"-Parameter gesteuert, sondern mit über "page" (getrennt mit einem Slash, z. B. page=dummy/config) 11 | // Die einzelnen Teile des page-Pfades können mit der folgenden Funktion ausgelesen werden. 12 | // $subpage = rex_be_controller::getCurrentPagePart(2); 13 | 14 | // Subpages können über diese Methode eingebunden werden. So ist sichergestellt, dass auch Subpages funktionieren, 15 | // die von anderen Addons/Plugins hinzugefügt wurden 16 | rex_be_controller::includeCurrentPageSubPath(); 17 | -------------------------------------------------------------------------------- /lib/ycom.php: -------------------------------------------------------------------------------- 1 | */ 6 | public static array $tables = []; 7 | 8 | public static function addTable(string $table_name): void 9 | { 10 | self::$tables[] = $table_name; 11 | } 12 | 13 | /** 14 | * @return array 15 | */ 16 | public static function getTables(): array 17 | { 18 | return self::$tables; 19 | } 20 | 21 | public static function parseText(string $text): string 22 | { 23 | $text = nl2br(trim($text)); 24 | return '

' . $text . '

'; 25 | } 26 | 27 | public static function cut(string $text, int $size = 15, string $t = ' (...) '): string 28 | { 29 | $s = strlen($text); 30 | if ($s > $size) { 31 | $start = (int) ($size / 2); 32 | return substr($text, 0, $start) . $t . substr($text, -$start); 33 | } 34 | return $text; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /plugins/media_auth/pages/index.php: -------------------------------------------------------------------------------- 1 | i18n('title')); // $this->i18n('title') ist eine Kurzform für rex_i18n::msg('dummy_title') 9 | 10 | // Die Subpages werden nicht mehr über den "subpage"-Parameter gesteuert, sondern mit über "page" (getrennt mit einem Slash, z. B. page=dummy/config) 11 | // Die einzelnen Teile des page-Pfades können mit der folgenden Funktion ausgelesen werden. 12 | // $subpage = rex_be_controller::getCurrentPagePart(2); 13 | 14 | // Subpages können über diese Methode eingebunden werden. So ist sichergestellt, dass auch Subpages funktionieren, 15 | // die von anderen Addons/Plugins hinzugefügt wurden 16 | 17 | $currentPage = rex_be_controller::getCurrentPageObject(); 18 | if (null !== $currentPage) { 19 | include $currentPage->getSubPath(); 20 | } 21 | -------------------------------------------------------------------------------- /plugins/auth/lib/yform/value/ycom_auth_load_user.php: -------------------------------------------------------------------------------- 1 | params['send']) { 8 | $fields = $this->getElement(2); 9 | if ('' != $fields) { 10 | $fields = explode(',', $this->getElement(2)); 11 | } else { 12 | $fields = []; 13 | } 14 | foreach ($this->params['values'] as $o) { 15 | if ((0 == count($fields) || in_array($o->getName(), $fields)) && $o->getName() != $this->getName()) { 16 | $o->setValue(@rex_ycom_auth::getUser()->getValue($o->getName())); 17 | } 18 | } 19 | } 20 | } 21 | 22 | public function getDescription(): string 23 | { 24 | return 'ycom_auth_load_user -> Beispiel: ycom_auth_load_user|label|opt:field1,field2,field3'; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /plugins/auth/lib/yform/validate/ycom_auth_password.php: -------------------------------------------------------------------------------- 1 | getValueObject(); 8 | 9 | $user = rex_ycom_auth::getUser(); 10 | if (null === $user) { 11 | // no user available -> error 12 | $this->params['warning'][$Object->getId()] = $this->params['error_class']; 13 | $this->params['warning_messages'][$Object->getId()] = $this->getElement(3); 14 | return; 15 | } 16 | 17 | $status = rex_ycom_auth::checkPassword($Object->getValue(), $user->getId()); 18 | if (!$status) { 19 | // password wrong 20 | $this->params['warning'][$Object->getId()] = $this->params['error_class']; 21 | $this->params['warning_messages'][$Object->getId()] = $this->getElement(3); 22 | } 23 | } 24 | 25 | public function getDescription(): string 26 | { 27 | return 'ycom_auth_password -> validate|ycom_auth_password|pswfield|warning_message'; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 REDAXO, www.redaxo.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /install.php: -------------------------------------------------------------------------------- 1 | delete 21 | $pluginDocs = __DIR__ . '/plugins/docs'; 22 | if (file_exists($pluginDocs)) { 23 | rex_dir::delete($pluginDocs); 24 | } 25 | 26 | foreach ($this->getInstalledPlugins() as $plugin) { 27 | // use path relative to __DIR__ to get correct path in update temp dir 28 | $file = __DIR__ . '/plugins/' . $plugin->getName() . '/install.php'; 29 | 30 | if (file_exists($file)) { 31 | $plugin->includeFile($file); 32 | } 33 | } 34 | 35 | // rex_yform_manager_table_api::generateTablesAndFields(); 36 | 37 | rex_delete_cache(); 38 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 | setQuery('DELETE FROM `' . rex::getTable('yform_table') . '` WHERE table_name = "' . rex::getTable('ycom_user') . '"'); 10 | $sql->setQuery('DELETE FROM `' . rex::getTable('yform_field') . '` WHERE table_name = "' . rex::getTable('ycom_user') . '"'); 11 | $sql->setQuery('DELETE FROM `' . rex::getTable('yform_history') . '` WHERE table_name = "' . rex::getTable('ycom_user') . '"'); 12 | 13 | rex_sql_table::get(rex::getTable('article')) 14 | ->removeColumn('ycom_auth_type') 15 | ->removeColumn('ycom_group_type') 16 | ->removeColumn('ycom_groups') 17 | ->alter(); 18 | 19 | rex_sql_table::get(rex::getTable('ycom_user_session')) 20 | ->drop(); 21 | 22 | rex_sql_table::get(rex::getTable('ycom_user')) 23 | ->drop(); 24 | 25 | foreach ($this->getInstalledPlugins() as $plugin) { 26 | // use path relative to __DIR__ to get correct path in update temp dir 27 | $file = __DIR__ . '/plugins/' . $plugin->getName() . '/uninstall.php'; 28 | 29 | if (file_exists($file)) { 30 | $plugin->includeFile($file); 31 | } 32 | } 33 | 34 | rex_yform_manager_table::deleteCache(); 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | YCom für REDAXO 5.15+ 2 | ============= 3 | 4 | YCom ist ein Addon, das REDAXO um eine Frontend-Authentifizierung erweitert. Dadurch lässt sich bspw. ein einfaches Login im Frontend umsetzen - ebenso wie eine komplexe Community-Verwaltung. 5 | 6 | Features 7 | -------- 8 | 9 | * Benutzerverwaltung: Erstellung und Verwaltung von Frontend-Benutzern 10 | * Selbständige Registierung: Nutzer können sich selbständig für einen geschützten Bereich registrieren 11 | * Passwort zurücksetzen: Frontend-Nutzer können selbständig ihr Passwort zurücksetzen 12 | * Gruppen-Berechtigungen: Frontend-Nutzer können bestimmten Gruppen zugeordnet und verwaltet werden 13 | * Sichtbarkeit: Die Sichtbarkeit von Artikeln und Kategorien kann für bestimmte Frontend-Nutzer aktiviert oder deaktiviert werden 14 | * Einstellungen: Je nach in den Einstellungen gewählten Artikel wird man beim Login/Logout auf einen bestimmten Artikel weitergeleitet. 15 | * Geschützter Zugriff auf Medien 16 | 17 | > Hinweis: Die einzelnen Features können losgelöst voneinander verwendet werden. 18 | 19 | ![Screenshot](https://raw.githubusercontent.com/yakamara/redaxo_ycom/assets/ycom_01.png) 20 | 21 | Installation 22 | ------------ 23 | 24 | * Ins Backend einloggen und mit dem Installer installieren. 25 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yakamara/ycom", 3 | "require": { 4 | "onelogin/php-saml": "^3", 5 | "apereo/phpcas": "^1", 6 | "league/oauth2-client": "^2", 7 | "spomky-labs/otphp": "^11.0" 8 | }, 9 | "replace": { 10 | "psr/container": "*", 11 | "psr/http-message": "*", 12 | "psr/log": "*", 13 | "symfony/console": "*", 14 | "symfony/deprecation-contracts": "*", 15 | "symfony/http-foundation": "*", 16 | "symfony/polyfill-ctype": "*", 17 | "symfony/polyfill-php80": "*", 18 | "symfony/polyfill-php81": "*", 19 | "symfony/polyfill-php83": "*", 20 | "symfony/service-contracts": "*", 21 | "symfony/string": "*", 22 | "symfony/yaml": "*" 23 | }, 24 | "require-dev": { 25 | "redaxo/php-cs-fixer-config": "^2.0", 26 | "friendsofphp/php-cs-fixer": "^3.14", 27 | "phpunit/phpunit": "^9.5" 28 | }, 29 | "config": { 30 | "platform": { 31 | "php": "8.1.13" 32 | } 33 | }, 34 | "scripts": { 35 | "cs-dry": "php-cs-fixer fix -v --ansi --dry-run --config=.php-cs-fixer.dist.php", 36 | "cs-fix": "php-cs-fixer fix -v --ansi --config=.php-cs-fixer.dist.php", 37 | "unit-test": "phpunit --testdox" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/code-style.yml: -------------------------------------------------------------------------------- 1 | name: PHP-CS-Fixer 2 | 3 | on: 4 | push: 5 | branches: [ master, main ] 6 | pull_request: 7 | branches: [ master, main ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | code-style: 14 | 15 | runs-on: ubuntu-latest 16 | permissions: 17 | contents: write # for Git to git apply 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | 22 | - name: Setup PHP 23 | uses: shivammathur/setup-php@v2 24 | with: 25 | php-version: '8.1' 26 | extensions: gd, intl, pdo_mysql 27 | coverage: none # disable xdebug, pcov 28 | 29 | # install dependencies from composer.json 30 | - name: Install test dependencies 31 | env: 32 | COMPOSER: composer.json 33 | run: COMPOSER=.tools/composer-code-style.json composer install --prefer-dist --no-progress 34 | 35 | # run php-cs-fixer 36 | - name: Run PHP CS Fixer 37 | run: composer cs-dry 38 | 39 | # commit and push fixed files 40 | # - uses: stefanzweifel/git-auto-commit-action@v4 41 | # with: 42 | # commit_message: Apply php-cs-fixer changes 43 | -------------------------------------------------------------------------------- /plugins/auth/lib/yform/value/ycom_auth_logout.php: -------------------------------------------------------------------------------- 1 | getElement(3); 19 | $returnTos[] = rex_getUrl(rex_ycom_config::get('article_id_jump_logout')); 20 | 21 | $allowedDomains = ('' != $this->getElement(2)) ? explode(',', $this->getElement(2)) : []; 22 | $returnTo = rex_ycom_auth::getReturnTo($returnTos, $allowedDomains); 23 | 24 | rex_ycom_auth::logout($me); 25 | 26 | if ('' != $returnTo) { 27 | rex_response::sendCacheControl(); 28 | rex_response::sendRedirect($returnTo); 29 | } 30 | } 31 | } 32 | 33 | public function getDescription(): string 34 | { 35 | return 'ycom_auth_logout -> Beispiel: ycom_auth_logout|label|[allowed domains: DomainA,DomainB]|[returnTo]'; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /plugins/auth/lib/otp/method_totp.php: -------------------------------------------------------------------------------- 1 | verify($otp); 19 | } 20 | 21 | public static function getPeriod(): int 22 | { 23 | // default period is 30s and digest is sha1. Google Authenticator is restricted to this settings 24 | return 30; 25 | } 26 | 27 | public static function getloginTries(): int 28 | { 29 | return 10; 30 | } 31 | 32 | public function getProvisioningUri(rex_ycom_user $user): string 33 | { 34 | // create a uri with a random secret 35 | $otp = TOTP::create(null, self::getPeriod()); 36 | 37 | // the label rendered in "Google Authenticator" or similar app 38 | $label = $user->getValue('login') . '@' . rex::getServerName() . ' (' . $_SERVER['HTTP_HOST'] . ')'; 39 | $label = str_replace(':', '_', $label); // colon is forbidden 40 | $otp->setLabel($label); 41 | $otp->setIssuer(str_replace(':', '_', $user->getValue('login'))); 42 | 43 | return $otp->getProvisioningUri(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /plugins/auth/lib/yform/value/ycom_auth_returnto.php: -------------------------------------------------------------------------------- 1 | www.yakamara.de 8 | */ 9 | 10 | class rex_yform_value_ycom_auth_returnto extends rex_yform_value_abstract 11 | { 12 | public function enterObject(): void 13 | { 14 | $returnTo = $this->getValue(); 15 | 16 | $returnTos = []; 17 | $returnTos[] = (string) $returnTo; 18 | $returnTos[] = rex_request('returnTo', 'string'); 19 | $returnTos[] = $this->getElement(3); 20 | $returnTos[] = rex_getUrl(rex_ycom_config::get('article_id_jump_ok'), '', [], '&'); 21 | $allowedDomains = ('' != $this->getElement(2)) ? explode(',', $this->getElement(2)) : []; 22 | $returnTo = rex_ycom_auth::getReturnTo($returnTos, $allowedDomains); 23 | 24 | $this->setValue($returnTo); 25 | 26 | if ($this->needsOutput()) { 27 | $this->params['form_output'][$this->getId()] = $this->parse('value.hidden.tpl.php'); 28 | } 29 | 30 | $this->params['value_pool']['email'][$this->getName()] = $this->getValue(); 31 | } 32 | 33 | public function executeAction(): void 34 | { 35 | if ('' != $this->getValue()) { 36 | header('Location: ' . $this->getValue()); 37 | $this->params['form_exit'] = true; 38 | } 39 | } 40 | 41 | public function getDescription(): string 42 | { 43 | return 'ycom_auth_returnto|label|[allowed domains: DomainA,DomainB]|[URL]'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.tools/rexstan.php: -------------------------------------------------------------------------------- 1 | getPath('docs') . '/*.md') ?: [] as $file) { 10 | $mdFiles[mb_substr(basename($file), 0, -3)] = $file; 11 | } 12 | 13 | $currenMDFile = rex_request('mdfile', 'string', '01_intro'); 14 | if (!array_key_exists($currenMDFile, $mdFiles)) { 15 | $currenMDFile = '01_intro'; 16 | } 17 | 18 | $page = rex_be_controller::getPageObject('ycom/docs'); 19 | 20 | if (null !== $page) { 21 | foreach ($mdFiles as $key => $mdFile) { 22 | $keyWithoudPrio = mb_substr($key, 3); 23 | $page->addSubpage( 24 | (new rex_be_page($key, rex_i18n::msg('ycom_docs_' . $keyWithoudPrio))) 25 | ->setSubPath($mdFile) 26 | ->setHref('index.php?page=ycom/docs&mdfile=' . $key) 27 | ->setIsActive($key === $currenMDFile), 28 | ); 29 | } 30 | } 31 | 32 | echo rex_view::title($this->i18n('ycom_title')); 33 | 34 | [$Toc, $Content] = rex_markdown::factory()->parseWithToc(rex_file::require($mdFiles[$currenMDFile]), 2, 3, [ 35 | rex_markdown::SOFT_LINE_BREAKS => false, 36 | rex_markdown::HIGHLIGHT_PHP => true, 37 | ]); 38 | 39 | $fragment = new rex_fragment(); 40 | $fragment->setVar('content', $Content, false); 41 | $fragment->setVar('toc', $Toc, false); 42 | $content = $fragment->parse('core/page/docs.php'); 43 | 44 | $fragment = new rex_fragment(); 45 | $fragment->setVar('title', rex_i18n::msg('package_help') . ' ', false); 46 | $fragment->setVar('body', $content, false); 47 | echo $fragment->parse('core/page/section.php'); 48 | -------------------------------------------------------------------------------- /plugins/auth/lib/yform/action/ycom_auth_db.php: -------------------------------------------------------------------------------- 1 | getElement(2); 18 | 19 | switch ($action) { 20 | case 'delete': 21 | rex_ycom_log::log($user, rex_ycom_log::TYPE_LOGIN_DELETED, [ 22 | 'self delete', 23 | ]); 24 | rex_ycom_auth::deleteUser($user->getValue('id')); 25 | rex_ycom_auth::clearUserSession(); 26 | 27 | break; 28 | case 'update': 29 | default: 30 | rex_ycom_log::log($user, rex_ycom_log::TYPE_LOGIN_UPDATED, [ 31 | 'self update', 32 | ]); 33 | 34 | $this->params['main_table'] = rex_ycom_user::table()->getTableName(); 35 | $this->params['main_where'] = 'id=' . (int) (rex_ycom_user::getMe() ? rex_ycom_user::getMe()->getId() : 0); 36 | 37 | $this->setElement(2, ''); 38 | $this->setElement(3, 'main_where'); 39 | 40 | parent::executeAction(); 41 | } 42 | } 43 | 44 | public function getDescription(): string 45 | { 46 | return 'action|ycom_auth_db|update(default)/delete'; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/yform/value/ycom_user.php: -------------------------------------------------------------------------------- 1 | getElement(6); 9 | 10 | $this->setValue(-1); 11 | if (null !== rex_ycom_user::getMe()) { 12 | $this->setValue(rex_ycom_user::getMe()->getValue($this->getElement(2))); 13 | $show_value = rex_ycom_user::getMe()->getValue($show_label); 14 | } 15 | 16 | $wc = ''; 17 | if (isset($this->params['warning'][$this->getId()])) { 18 | $wc = $this->params['warning'][$this->getId()]; 19 | } 20 | 21 | if ('hidden' !== trim($this->getElement(4))) { 22 | $this->params['form_output'][$this->getId()] = ' 23 |

24 | 25 | 26 |

'; 27 | } 28 | 29 | $this->params['value_pool']['email'][$this->getElement(1)] = stripslashes($this->getValue()); 30 | if ('no_db' !== $this->getElement(5)) { 31 | $this->params['value_pool']['sql'][$this->getElement(1)] = $this->getValue(); 32 | } 33 | } 34 | 35 | public function getDescription(): string 36 | { 37 | return 'ycom_user -> Beispiel: ycom_user|label|dbfield|Fieldlabel|hidden|[no_db]|showlabel'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /plugins/auth/lang/pt_br.lang: -------------------------------------------------------------------------------- 1 | ycom_auth_title = Autentificação 2 | 3 | ycom_auth_settings = Configurações 4 | ycom_auth_description = Explicação 5 | 6 | ycom_auth_config = Configuração 7 | ycom_auth_config_save = Salvar configuração 8 | ycom_auth_config_status = Status 9 | ycom_auth_config_security = Segurança 10 | ycom_auth_config_forwarder = Seguir para o produto 11 | ycom_auth_config_login_field = Campo para login 12 | 13 | ycom_auth_config_status_authactive = Autentificação ativa 14 | ycom_auth_config_status_stayactive = "Permanecer conectado" ativo 15 | ycom_auth_config_security_hashedpasswd = Senhas criptografadas (sha1) 16 | ycom_auth_config_id_jump_ok = ... ao conectar-se com sucesso 17 | ycom_auth_config_id_jump_not_ok = ... ao não se conectar com sucesso 18 | ycom_auth_config_id_jump_logout = ... ao se desconectar 19 | ycom_auth_config_id_jump_denied = ... ao tentar acessar um produto restrito 20 | ycom_auth_config_id_jump_activation_ok = ... ao ativar com sucesso 21 | ycom_auth_config_id_jump_activation_fail = ... quando a ativação não foi bem sucedida 22 | ycom_auth_config_id_jump_newpassword_ok = ... ao resetar a senha com sucesso 23 | ycom_auth_config_id_jump_newpassword_fail = ... ao resetar a senha sem sucesso 24 | 25 | ycom_auth_settings_updated = A configuração foi salva 26 | 27 | ycom_auth_info_title = Explicação 28 | 29 | ycom_auth_perm = Permissões 30 | ycom_auth_update_perm = Atualizar 31 | ycom_auth_perm_updated = Permissões foram atualizadas 32 | 33 | ycom_auth_password_not_updated = Senha não foi atualizada 34 | ycom_auth_password_updated = Senha foi atualizada 35 | ycom_auth_password_exists = A senha já é utilizada 36 | ycom_auth_password_isempty = A senha ainda não é utilizada -------------------------------------------------------------------------------- /plugins/auth/lib/injections/passwordchange.php: -------------------------------------------------------------------------------- 1 | getValue('new_password_required')) { 11 | if ($article_id_password != rex_article::getCurrentId()) { 12 | return rex_getUrl($article_id_password, '', [], '&'); 13 | } 14 | return true; 15 | } 16 | } 17 | return false; 18 | } 19 | 20 | public function getSettingsContent(): string 21 | { 22 | $addon = rex_plugin::get('ycom', 'auth'); 23 | return ' 24 |
25 | ' . $addon->i18n('ycom_auth_config_passwordchange') . ' 26 |
27 |
28 | 29 |
30 |
31 | ' . rex_var_link::getWidget(9, 'article_id_jump_password', (int) $addon->getConfig('article_id_jump_password')) . ' 32 | [article_id_jump_password] 33 |
34 |
35 |
36 | '; 37 | } 38 | 39 | public function triggerSaveSettings(): void 40 | { 41 | $addon = rex_plugin::get('ycom', 'auth'); 42 | $addon->setConfig('article_id_jump_password', rex_request('article_id_jump_password', 'int')); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /plugins/auth/lib/injections/termsofuse.php: -------------------------------------------------------------------------------- 1 | getValue('termsofuse_accepted')) { 11 | if ($article_id_termsofuse != rex_article::getCurrentId()) { 12 | return rex_getUrl($article_id_termsofuse, '', [], '&'); 13 | } 14 | return true; 15 | } 16 | } 17 | return false; 18 | } 19 | 20 | public function getSettingsContent(): string 21 | { 22 | $addon = rex_plugin::get('ycom', 'auth'); 23 | return ' 24 |
25 | ' . $addon->i18n('ycom_auth_config_termsofuse') . ' 26 |
27 |
28 | 29 |
30 |
31 | ' . rex_var_link::getWidget(10, 'article_id_jump_termsofuse', (int) $addon->getConfig('article_id_jump_termsofuse')) . ' 32 | [article_id_jump_termsofuse] 33 |
34 |
35 |
36 | '; 37 | } 38 | 39 | public function triggerSaveSettings(): void 40 | { 41 | $addon = rex_plugin::get('ycom', 'auth'); 42 | $addon->setConfig('article_id_jump_termsofuse', rex_request('article_id_jump_termsofuse', 'int', 0)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /plugins/group/boot.php: -------------------------------------------------------------------------------- 1 | getSubject(); 15 | $yform->setValueField('choice', [ 16 | 'name' => 'ycom_group_type', 17 | 'label' => rex_i18n::msg('ycom_group_type'), 18 | 'choices' => rex_ycom_group::$perms, 19 | ]); 20 | $yform->setValueField('choice', [ 21 | 'name' => 'ycom_groups', 22 | 'label' => rex_i18n::msg('ycom_groups'), 23 | 'choices' => rex_ycom_group::getGroups(), 24 | 'size' => 5, 25 | 'multiple' => true, 26 | ]); 27 | return $yform; 28 | }); 29 | } 30 | 31 | rex_extension::register('YCOM_AUTH_USER_CHECK', static function (rex_extension_point $ep) { 32 | if (!$ep->getSubject()) { 33 | return false; 34 | } 35 | 36 | /** @var rex_article|rex_category $article */ 37 | $article = $ep->getParam('article'); 38 | 39 | if (1 != $article->getValue('ycom_auth_type')) { 40 | return $ep->getSubject(); 41 | } 42 | 43 | /** @var rex_ycom_user|null $me */ 44 | $me = $ep->getParam('me'); 45 | $type = (string) $article->getValue('ycom_group_type'); 46 | $userGroups = ($me) ? $me->getGroups() : []; 47 | $articleGroups = (string) $article->getValue('ycom_groups'); 48 | 49 | $groups = []; 50 | if ('' != $articleGroups) { 51 | $groups = explode(',', $articleGroups); 52 | } 53 | 54 | return rex_ycom_group::hasGroupPerm($type, $groups, $userGroups); 55 | }); 56 | -------------------------------------------------------------------------------- /assets/ycom_backend.js: -------------------------------------------------------------------------------- 1 | $(document).on('rex:ready', function () { 2 | 3 | // YCom-Artikel Rechte 4 | $('#yform-ycom_auth-perm-ycom_auth_type select,#yform-ycom_auth-perm-ycom_group_type select').on('change', function() { 5 | if($('#yform-ycom_auth-perm-ycom_auth_type select option:selected').val() !== "1") { 6 | $('#yform-ycom_auth-perm-ycom_group_type').hide(); 7 | $('#yform-ycom_auth-perm-ycom_groups').hide(); 8 | } else { 9 | $('#yform-ycom_auth-perm-ycom_group_type').show(); 10 | let group_type = $('#yform-ycom_auth-perm-ycom_group_type option:selected').val(); 11 | if(group_type === "1" || group_type === "2") { 12 | $('#yform-ycom_auth-perm-ycom_groups').show(); 13 | } else { 14 | $('#yform-ycom_auth-perm-ycom_groups').hide(); 15 | } 16 | } 17 | }); 18 | $('#yform-ycom_auth-perm-ycom_auth_type select').trigger('change'); 19 | 20 | // Medienpool Rechte 21 | $('select[name="ycom_auth_type"],select[name="ycom_group_type"]').on('change', function() { 22 | 23 | if($('select[name="ycom_auth_type"] option:selected').val() !== "1") { 24 | $('select[name="ycom_group_type"]').closest('.form-group').hide(); 25 | $('select[name="ycom_groups[]"]').closest('.form-group').hide(); 26 | } else { 27 | $('select[name="ycom_group_type"]').closest('.form-group').show(); 28 | let group_type_selected = $('select[name="ycom_group_type"] option:selected').val(); 29 | if(group_type_selected === "1" || group_type_selected === "2") { 30 | $('select[name="ycom_groups[]"]').closest('.form-group').show(); 31 | } else { 32 | $('select[name="ycom_groups[]"]').closest('.form-group').hide(); 33 | } 34 | } 35 | }); 36 | $('select[name="ycom_auth_type"]').trigger('change'); 37 | }); 38 | -------------------------------------------------------------------------------- /lang/pt_br.lang: -------------------------------------------------------------------------------- 1 | ycom_perm = Permissões comunitárias 2 | 3 | ycom_page_perm = Página de permissões comunitárias 4 | 5 | navigation_ycom = Comunidade 6 | ycom_title = Comunidade: configurações 7 | ycom_navi_title = Configurações 8 | 9 | ycom_main_title = Informações 10 | ycom_info = Readme 11 | ycom_info_title = Informações úteis para trabalhar com o AddOn comunidade 12 | ycom_manual = Manual / Exemplos 13 | ycom_manual_title = Manual / Exemplos 14 | ycom_changelog = Mudar log 15 | ycom_changelog_title = Mudar log 16 | ycom_license = Licença 17 | ycom_license_title = Licença 18 | ycom_overview = Visão geral 19 | ycom_user_management = Gestão de usuário 20 | ycom_field_management = Editar campos 21 | 22 | ycom_user = Usuário 23 | yform_ycom_user = Usuário de comunidade 24 | rex_ycom_user = Usuário de comunidade 25 | 26 | email = E-mail 27 | status = Status 28 | firstname = Primeiro Nome 29 | activation_key = Chave de ativação 30 | session_key = Chave da seção 31 | last_login_time = Último acesso 32 | last_action_time = Última ação 33 | login_failed = Logins falhos 34 | login_tries = Tentativas de login 35 | 36 | ycom_login_tries_info = 10 tentativas falhas de logon causam o bloqueamente do usuário. 37 | 38 | ycom_account_inactive = Acesso desativado 39 | ycom_account_requested = Acesso requisitado 40 | ycom_account_active = Acesso ativo 41 | 42 | ycom_status = Status 43 | 44 | ycom_perm_type = Permissões da página 45 | 46 | ycom_perm_extends = Adotar categoria mais ampla, caso contrário acessível a todos os usuários 47 | ycom_perm_only_logged_in = Acesso para usuários loggados 48 | ycom_perm_only_not_logged_in = Acesso para todos os usuários não loggados 49 | ycom_perm_all = Acesso para todos os usuários 50 | 51 | ycom_this_login_exists_already = Este nome de usuário já existe 52 | ycom_this_email_exists_already = Este e-mail de usuário já existe 53 | ycom_please_enter_login = Por favor, insira um nome de usuário 54 | ycom_please_enter_email = Por favo, insira um endereço de e-mail -------------------------------------------------------------------------------- /docs/04_navigations.md: -------------------------------------------------------------------------------- 1 | # Navigationen 2 | 3 | ## Benutzung der REDAXO Navigation 4 | 5 | Damit die Userberechtigungen in der Navigation berücksichtigt werden muss die REDAXO Klasse rex_navigation::factory ergänzt werden. 6 | 7 | ```php 8 | $nav = rex_navigation::factory(); 9 | $nav->addCallback('rex_ycom_auth::articleIsPermitted'); 10 | $nav->show(0, 1, TRUE, TRUE); 11 | ``` 12 | 13 | > **Hinweis:** Die Ausgabe der Navigation kann angepasst und um Parameter erweitert werden. Details siehe in der Doku: 14 | 15 | > **Hinweis:** Die Klasse `rex_ycom_navigation::factory();` existiert seit Version 3 nicht mehr. 16 | 17 | ## Meta-User-Navigation 18 | 19 | Beispiel für die Entwicklung einer Meta-User-Navigation: 20 | 21 | ```php 22 | $login_article_id = rex_ycom_config::get('article_id_login'); 23 | $logout_article_id = rex_ycom_config::get('article_id_logout'); 24 | 25 | // TODO: please change 26 | $profile_article_id = 1; 27 | $register_article_id = 1; 28 | 29 | $ycom_user = rex_ycom_auth::getUser(); 30 | if ($ycom_user) { 31 | echo ' 32 | '; 42 | } else { 43 | echo ' 44 |
45 | 53 |
'; 54 | } 55 | ``` 56 | -------------------------------------------------------------------------------- /boot.php: -------------------------------------------------------------------------------- 1 | isAvailable()) { 9 | rex_ycom_auth::addInjection(new rex_ycom_injection_otp(), 1); 10 | rex_ycom_auth::addInjection(new rex_ycom_injection_passwordchange(), 4); 11 | rex_ycom_auth::addInjection(new rex_ycom_injection_termsofuse(), 8); 12 | } 13 | 14 | if (rex::isBackend()) { 15 | rex_extension::register('PACKAGES_INCLUDED', static function ($params) { 16 | $addon = rex_addon::get('yform'); 17 | $plugin = rex_plugin::get('yform', 'manager'); 18 | if ($plugin->isAvailable()) { 19 | // YForm <= 5 20 | $pages = $plugin->getProperty('pages'); 21 | $ycom_tables = rex_ycom::getTables(); 22 | if (isset($pages) && is_array($pages)) { 23 | foreach ($pages as $page) { 24 | if (in_array($page->getKey(), $ycom_tables, true)) { 25 | $page->setBlock('ycom'); 26 | // $page->setRequiredPermissions('ycom[]'); 27 | } 28 | } 29 | } 30 | } else { 31 | // YForm >= 5 32 | $pages = $addon->getProperty('pages'); 33 | $ycom_tables = rex_ycom::getTables(); 34 | if (isset($pages) && is_array($pages)) { 35 | foreach ($pages as $page) { 36 | if (in_array($page->getKey(), $ycom_tables, true)) { 37 | $page->setBlock('ycom'); 38 | // $page->setRequiredPermissions('ycom[]'); 39 | } 40 | } 41 | } 42 | } 43 | }); 44 | } 45 | 46 | rex_ycom::addTable(rex::getTablePrefix() . 'ycom_user'); 47 | rex_yform_manager_dataset::setModelClass(rex::getTablePrefix() . 'ycom_user', rex_ycom_user::class); 48 | 49 | if (rex::isBackend() && ('index.php?page=content/edit' === rex_url::currentBackendPage() || 'mediapool' === rex_be_controller::getCurrentPagePart(1))) { 50 | rex_view::addJsFile($this->getAssetsUrl('ycom_backend.js')); 51 | } 52 | -------------------------------------------------------------------------------- /plugins/auth/lib/yform/validate/ycom_auth_login.php: -------------------------------------------------------------------------------- 1 | params['warning'][] = 1; 9 | $this->params['warning_messages'][] = rex_i18n::translate($this->getElement(4)); 10 | return; 11 | } 12 | 13 | $vars = []; 14 | 15 | $e = explode(',', $this->getElement(2)); 16 | foreach ($e as $v) { 17 | $w = explode('=', $v); 18 | $label = $w[0]; 19 | $value = trim(rex_request($w[1], 'string')); 20 | $vars[$label] = $value; 21 | } 22 | 23 | $filter = null; 24 | if ('' != $this->getElement(3)) { 25 | $filter_query = $this->getElement(3); 26 | $filter = static function (rex_yform_manager_query $query) use ($filter_query) { 27 | $query->whereRaw($filter_query); 28 | }; 29 | } 30 | 31 | rex_ycom_auth::loginWithParams($vars, $filter); 32 | 33 | if (null === rex_ycom_auth::getUser()) { 34 | $this->params['warning'][] = 1; 35 | $this->params['warning_messages'][] = rex_i18n::translate($this->getElement(4)); 36 | } else { 37 | // Load fields for eMail or DB 38 | $fields = $this->getElement(5); 39 | if ('' != $fields) { 40 | $fields = explode(',', $fields); 41 | foreach ($fields as $field) { 42 | $this->params['value_pool']['email'][$field] = rex_ycom_auth::getUser()->getValue($field); 43 | if ('no_db' != $this->getElement(6)) { 44 | $this->params['value_pool']['sql'][$field] = rex_ycom_auth::getUser()->getValue($field); 45 | } 46 | } 47 | } 48 | } 49 | } 50 | 51 | public function getDescription(): string 52 | { 53 | return 'ycom_auth_login -> prüft ob leer, beispiel: validate|ycom_auth_login|label1=request1,label2=request2|status>0|warning_message|opt:load_field1,load_field2,load_field3|[no_db] '; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /plugins/media_auth/lib/ycom_media_auth.php: -------------------------------------------------------------------------------- 1 | */ 8 | public static array $perms = [ 9 | '0' => 'translate:ycom_perm_all', 10 | '1' => 'translate:ycom_perm_only_logged_in', 11 | ]; 12 | 13 | public static function checkPerm(rex_media_manager $media_manager): bool 14 | { 15 | // check if original media_path 16 | $media = $media_manager->getMedia(); 17 | if (rex_path::media($media->getMediaFilename()) != $media_manager->getMedia()->getMediaPath()) { 18 | return true; 19 | } 20 | 21 | // is backend login 22 | if (rex_backend_login::hasSession()) { 23 | return true; 24 | } 25 | 26 | // is rex_media 27 | $rex_media = rex_media::get($media->getMediaFilename()); 28 | if (!$rex_media) { 29 | return false; 30 | } 31 | 32 | return self::checkFrontendPerm($rex_media); 33 | } 34 | 35 | public static function checkFrontendPerm(rex_media $rex_media): bool 36 | { 37 | $authType = (int) $rex_media->getValue('ycom_auth_type'); 38 | if (1 != $authType) { 39 | return true; 40 | } 41 | 42 | // from here only logged in Users 43 | 44 | $me = rex_ycom_user::getMe(); 45 | 46 | if (!$me) { 47 | return false; 48 | } 49 | 50 | $group = rex_plugin::get('ycom', 'group')->isAvailable(); 51 | 52 | if ($group) { 53 | $groupType = (int) $rex_media->getValue('ycom_group_type'); 54 | 55 | $groups = []; 56 | if ('' != $rex_media->getValue('ycom_groups')) { 57 | $groups = explode(',', (string) $rex_media->getValue('ycom_groups')); 58 | } 59 | 60 | $userGroups = $me->getValue('ycom_groups'); 61 | if (empty($userGroups)) { 62 | $userGroups = []; 63 | } else { 64 | $userGroups = explode(',', $userGroups); 65 | } 66 | 67 | return rex_ycom_group::hasGroupPerm($groupType, $groups, $userGroups); 68 | } 69 | 70 | return true; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /plugins/auth/assets/styles.css: -------------------------------------------------------------------------------- 1 | #ycom_auth_settings fieldset { 2 | margin-bottom: 30px; 3 | } 4 | 5 | #ycom_auth_settings legend { 6 | font-size: 12px; 7 | } 8 | #ycom_auth_settings label { 9 | font-weight: normal; 10 | } 11 | 12 | #ycom_auth_settings .abstand { 13 | margin-bottom: 10px; 14 | } 15 | 16 | #ycom_auth_settings .select-style { 17 | width: 100%; 18 | overflow: hidden; 19 | background: #E9ECF2 url("data:image/png;base64,R0lGODlhDwAUAIABAAAAAP///yH5BAEAAAEALAAAAAAPABQAAAIXjI+py+0Po5wH2HsXzmw//lHiSJZmUAAAOw==") no-repeat 98% 50%; 20 | margin: 0; 21 | opacity: 1; 22 | border: none; 23 | 24 | } 25 | #ycom_auth_settings select { 26 | font-size: 14px; 27 | padding: 0 15px 0 15px; 28 | width: 100%; 29 | border: 1px solid #c3c9d4; 30 | outline:0; 31 | -webkit-appearance:none; 32 | height: 40px; 33 | color: #36404f; 34 | box-shadow: none; 35 | background: transparent; 36 | background-image: none; 37 | -webkit-appearance: none; 38 | -moz-appearance:none; 39 | -webkit-border-radius: 0px; 40 | border-radius: 3px; 41 | } 42 | #ycom_auth_settings .select-style select:focus { 43 | outline: none; 44 | } 45 | #ycom_auth_settings .select-style select:hover { 46 | cursor: pointer; 47 | border-color: #000; 48 | } 49 | 50 | 51 | #ycom_auth_settings input[type=checkbox] { 52 | display: none; 53 | } 54 | 55 | #ycom_auth_settings input[type=checkbox] + label:before { 56 | font-family: FontAwesome; 57 | font-size: 20px; 58 | width: 30px; 59 | text-align: center; 60 | border-radius: 3px; 61 | background: #E9ECF2; 62 | border: 1px solid #c3c9d4; 63 | display: inline-block; 64 | margin-right: 10px; 65 | } 66 | 67 | #ycom_auth_settings input[type=checkbox] + label:before { 68 | padding-left: 2px; 69 | color: #c3c9d4; 70 | content: "\f00d"; 71 | } 72 | 73 | #ycom_auth_settings input[type=checkbox] + label:before { 74 | 75 | } 76 | 77 | #ycom_auth_settings input[type=checkbox]:checked + label:before { 78 | padding-left: 2px; 79 | color: #3CB594; 80 | content: "\f00c"; 81 | } 82 | 83 | #ycom_auth_settings input[type=checkbox]:checked + label:before { 84 | 85 | } 86 | 87 | #ycom_auth_settings .right { 88 | float: right; 89 | } 90 | 91 | #ycom_auth_settings .btn-save { 92 | width: 100%; 93 | } -------------------------------------------------------------------------------- /plugins/group/lib/ycom_group.php: -------------------------------------------------------------------------------- 1 | */ 8 | public static array $perms = [ 9 | '0' => 'translate:ycom_group_forallgroups', 10 | '1' => 'translate:ycom_group_inallgroups', 11 | '2' => 'translate:ycom_group_inonegroup', 12 | '3' => 'translate:ycom_group_nogroups', 13 | ]; 14 | 15 | /** 16 | * @throws rex_exception 17 | * @return array 18 | */ 19 | public static function getGroups(): array 20 | { 21 | $groups = []; 22 | foreach (self::query()->find() as $group) { 23 | /** @var rex_ycom_group $group */ 24 | $groups[$group->getId()] = $group->getName(); 25 | } 26 | return $groups; 27 | } 28 | 29 | /** 30 | * @param string|int $groupType 31 | * @param array $groups 32 | * @param array $userGroups 33 | */ 34 | public static function hasGroupPerm($groupType, array $groups = [], array $userGroups = []): bool 35 | { 36 | $groupType = (int) $groupType; 37 | 38 | if ($groupType < 1) { 39 | return true; 40 | } 41 | 42 | switch ($groupType) { 43 | // user in every group 44 | case 1: 45 | foreach ($groups as $group) { 46 | if ('' != $group && !in_array($group, $userGroups)) { 47 | return false; 48 | } 49 | } 50 | return true; 51 | 52 | // user in at least one group 53 | case 2: 54 | foreach ($groups as $group) { 55 | if ('' != $group && in_array($group, $userGroups)) { 56 | return true; 57 | } 58 | } 59 | return false; 60 | 61 | // user has no groups 62 | case 3: 63 | if (0 == count($userGroups)) { 64 | return true; 65 | } 66 | return false; 67 | 68 | default: 69 | return false; 70 | } 71 | } 72 | 73 | public function getName(): string 74 | { 75 | return $this->getValue('name'); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /plugins/media_auth/pages/settings.php: -------------------------------------------------------------------------------- 1 | i18n('ycom_title')); 6 | 7 | $table = rex_yform_manager_table::get(rex::getTablePrefix() . 'ycom_user'); 8 | if (null !== $table) { 9 | $xform_user_fields = $table->getValueFields(); 10 | } else { 11 | throw new rex_exception('table `' . rex::getTablePrefix() . 'ycom_user` not found'); 12 | } 13 | 14 | /** 15 | * @var rex_package $this 16 | * @psalm-scope-this rex_package 17 | */ 18 | 19 | if ('update' == rex_request('func', 'string')) { 20 | $this->setConfig('media_auth_rule', rex_request('media_auth_rule', 'string')); 21 | 22 | echo rex_view::success($this->i18n('ycom_media_auth_settings_updated')); 23 | } 24 | 25 | $sel_authrules = new rex_select(); 26 | $sel_authrules->setId('media-auth-rule'); 27 | $sel_authrules->setName('media_auth_rule'); 28 | 29 | $rules = new rex_ycom_media_auth_rules(); 30 | 31 | $sel_authrules->addOptions($rules->getOptions()); 32 | 33 | $sel_authrules->setAttribute('class', 'form-control selectpicker'); 34 | $sel_authrules->setSelected($this->getConfig('media_auth_rule')); 35 | 36 | $content = ' 37 |
38 | 39 | 40 | 41 |
42 | 43 |
44 |
45 | 46 |
47 |
48 | ' . $sel_authrules->get() . ' 49 |
50 |
51 | 52 |
53 | 54 |
55 |
56 | 57 |
58 |
59 | 60 |
61 | 62 | '; 63 | 64 | $fragment = new rex_fragment(); 65 | $fragment->setVar('class', 'edit'); 66 | $fragment->setVar('title', $this->i18n('ycom_auth_settings')); 67 | $fragment->setVar('body', $content, false); 68 | echo $fragment->parse('core/page/section.php'); 69 | -------------------------------------------------------------------------------- /lang/en_gb.lang: -------------------------------------------------------------------------------- 1 | ycom_perm = Community permissions 2 | 3 | ycom_page_perm = Community page permissions 4 | 5 | navigation_ycom = Community 6 | ycom_title = Community: settings 7 | ycom_navi_title = Settings 8 | 9 | ycom_main_title = Info 10 | ycom_info = Readme 11 | ycom_info_title = Useful information working with the Community AddOn 12 | ycom_manual = Manual / Examples 13 | ycom_manual_title = Manual / Examples 14 | ycom_changelog = Change log 15 | ycom_changelog_title = Change log 16 | ycom_license = Licence 17 | ycom_license_title = Licence 18 | ycom_overview = Overview 19 | ycom_user_management = User management 20 | ycom_field_management = Edit fields 21 | 22 | ycom_user = User 23 | yform_ycom_user = Community user 24 | rex_ycom_user = Community user 25 | 26 | email = E-mail 27 | status = Status 28 | firstname = First name 29 | activation_key = Activation key 30 | session_key = Session key 31 | last_login_time = Last sign in 32 | last_login_try_time = Last sign in try 33 | last_action_time = Last action 34 | termination_time = Kündigungszeitpunkt 35 | login_failed = Failed sign in attempts 36 | login_tries = Sign in attempts 37 | 38 | ycom_login_tries_info = Login faile will be defined here Plugin YCom/Auth 39 | 40 | ycom_account_inactive_termination = Access terminated 41 | ycom_account_inactive_logins = Access deactivated [Too many failed Logins] 42 | ycom_account_inactive = Access is deactivated 43 | ycom_account_requested = Access requested 44 | ycom_account_confirm = Access has been confirmed 45 | ycom_account_active = Access is active 46 | 47 | ycom_status = Status 48 | 49 | ycom_perm_type = Page permissions 50 | 51 | ycom_perm_extends = Inherit from parent category otherwise accessible by all users 52 | ycom_perm_only_logged_in = Access for signed in users 53 | ycom_perm_only_not_logged_in = Access for all non signed in users 54 | ycom_perm_all = Access for all users 55 | 56 | ycom_this_login_exists_already = This username already exists 57 | ycom_this_email_exists_already = This email already exists 58 | ycom_please_enter_login = Please enter username 59 | ycom_please_enter_email = Please enter e-mail address 60 | ycom_this_activation_key_exists_already = This Activation-Key already exists 61 | 62 | last_password_time = Last password change 63 | termsofuse_accepted = Confirmed terms of use 64 | new_password_required = A new password needs to be set 65 | -------------------------------------------------------------------------------- /lang/es_es.lang: -------------------------------------------------------------------------------- 1 | ycom_perm = Derechos de la comunidad 2 | 3 | ycom_page_perm = Derechos de página de comunidad 4 | 5 | navigation_ycom = Comunidad 6 | ycom_title = Comunidad: Configuración 7 | ycom_navi_title = Ajustes 8 | 9 | ycom_main_title = Info 10 | ycom_info = Léame 11 | ycom_info_title = Información útil sobre el uso del complemento de la comunidad 12 | ycom_manual = Instrucciones / Ejemplos 13 | ycom_manual_title = Instrucciones / Ejemplos 14 | ycom_changelog = Cambios 15 | ycom_changelog_title = Cambios 16 | ycom_license = Licencia 17 | ycom_license_title = Licencia 18 | ycom_overview = Vista general 19 | ycom_user_management = Administración de usuario 20 | ycom_field_management = Personalizar campos 21 | 22 | ycom_user = Usuario 23 | yform_ycom_user = Usuarios de la comunidad 24 | rex_ycom_user = Usuario 25 | 26 | email = E-mail 27 | status = Estado 28 | firstname = Primer nombre 29 | activation_key = Clave de activación 30 | session_key = Clave de sesión 31 | last_login_time = Ultimo acceso exitoso 32 | last_action_time = Última acción 33 | termination_time = Fecha de terminación 34 | login_failed = Inicios de sesión fallidos 35 | login_tries = Inicia sesión intentos fallidos 36 | 37 | ycom_login_tries_info = Los errores permitidos se definen en el Complemento YCom/Auth. 38 | 39 | ycom_account_inactive_termination = El acceso fue terminado 40 | ycom_account_inactive_logins = El acceso ha sido deshabilitado [Error de inicio de sesión] 41 | ycom_account_inactive = El acceso esta inactivo 42 | ycom_account_requested = El acceso fue solicitado 43 | ycom_account_confirm = El acceso ha sido confirmado y está activo 44 | ycom_account_active = El acceso está activo 45 | 46 | ycom_status = Esado 47 | 48 | ycom_perm_type = Permisos página 49 | 50 | ycom_perm_extends = Desde una categoría superior, de lo contrario, el acceso está disponible para todos los usuarios 51 | ycom_perm_only_logged_in = Acceso para usuarios conectados 52 | ycom_perm_only_not_logged_in = Acceso para todos los usuarios no conectados 53 | ycom_perm_all = Acceso para todos los usuarios 54 | 55 | ycom_this_login_exists_already = Este inicio de sesión ya existe 56 | ycom_this_email_exists_already = Este correo electrónico ya existe 57 | ycom_please_enter_login = Por favor ingrese el inicio de sesión 58 | ycom_please_enter_email = Por favor ingrese el correo electrónico 59 | 60 | last_password_time = Último cambio de contraseña 61 | termsofuse_accepted = Términos de uso confirmados 62 | new_password_required = Se debe establecer una nueva contraseña -------------------------------------------------------------------------------- /plugins/auth/pages/content.ycom_auth.php: -------------------------------------------------------------------------------- 1 | setObjectparams('form_action', rex_url::backendController(['page' => 'content/edit', 'article_id' => $article_id, 'clang' => $clang, 'ctype' => $ctype], false)); 13 | $yform->setObjectparams('form_id', 'ycom_auth-perm'); 14 | $yform->setObjectparams('form_name', 'ycom_auth-perm'); 15 | $yform->setHiddenField('ycom_auth_func', 'perm'); 16 | 17 | $yform->setObjectparams('form_showformafterupdate', 1); 18 | 19 | $yform->setObjectparams('main_table', rex::getTable('article')); 20 | $yform->setObjectparams('main_id', $article_id); 21 | $yform->setObjectparams('main_where', 'id = ' . $article_id . ' and clang_id = ' . $clang); 22 | $yform->setObjectparams('getdata', true); 23 | 24 | $yform->setValueField('choice', [ 25 | 'name' => 'ycom_auth_type', 26 | 'label' => $addon->i18n('ycom_auth_perm'), 27 | 'choices' => rex_ycom_auth::$perms, 28 | ]); 29 | $yform = rex_extension::registerPoint(new rex_extension_point('YCOM_ARTICLE_PERM_SELECT', $yform, [ 30 | 'article_id' => $article_id, 31 | ])); 32 | 33 | $form = ''; 34 | $rexUser = rex::getUser(); 35 | 36 | if ($rexUser) { 37 | $permission_info = ''; 38 | if (!$rexUser->hasPerm('ycomArticlePermissions[]')) { 39 | $yform->setObjectparams('submit_btn_show', false); 40 | 41 | $nonce = ''; 42 | if (method_exists('rex_response', 'getNonce')) { 43 | $nonce = ' nonce="' . rex_response::getNonce() . '"'; 44 | } 45 | 46 | $permission_info .= '$( document ).ready(function() { $("#rex-page-sidebar-ycom_auth-perm :input").attr("disabled", true); }); '; 47 | $permission_info .= '

' . $addon->i18n('no_permission_to_edit') . '

'; 48 | } else { 49 | $yform->setActionField('db', [rex::getTable('article'), 'id = ' . $article_id]); 50 | $yform->setObjectparams('submit_btn_label', $addon->i18n('ycom_auth_update_perm')); 51 | } 52 | 53 | $form = $yform->getForm(); 54 | 55 | if ($yform->objparams['actions_executed'] && $rexUser->hasPerm('ycomArticlePermissions[]')) { 56 | // TODO: trigger ARTICLE_UPDATE 57 | $form = rex_view::success($addon->i18n('ycom_auth_perm_updated')) . $form; 58 | rex_article_cache::delete($article_id, $clang); 59 | } 60 | 61 | $form = '
' . $permission_info . $form . '
'; 62 | } 63 | 64 | return $form; 65 | -------------------------------------------------------------------------------- /plugins/auth/lib/otp/password.php: -------------------------------------------------------------------------------- 1 | getProvisioningUri()); 21 | $this->getMethod()->challenge($uri, $user); 22 | } 23 | 24 | public function verify(string $otp): bool 25 | { 26 | $uri = str_replace('&', '&', (string) rex_ycom_otp_password_config::forCurrentUser()->getProvisioningUri()); 27 | $verified = $this->getMethod()->verify($uri, $otp); 28 | return $verified; 29 | } 30 | 31 | public function isVerified(): bool 32 | { 33 | return rex_session('otp_verified', 'boolean', false); 34 | } 35 | 36 | public function isEnabled(): bool 37 | { 38 | return rex_ycom_otp_password_config::forCurrentUser()->enabled; 39 | } 40 | 41 | /** 42 | * @param self::ENFORCE* $enforce 43 | */ 44 | public function enforce($enforce): void 45 | { 46 | rex_config::set('ycom', 'otp_auth_enforce', $enforce); 47 | } 48 | 49 | /** 50 | * @return self::ENFORCE* 51 | */ 52 | public function isEnforced() 53 | { 54 | return rex_config::get('ycom', 'otp_auth_enforce', self::ENFORCED_DISABLED); 55 | } 56 | 57 | /** 58 | * @return self::OPTION* 59 | */ 60 | public function getAuthOption() 61 | { 62 | return rex_config::get('ycom', 'otp_auth_option', self::OPTION_ALL); 63 | } 64 | 65 | public function setAuthOption(string $option): void 66 | { 67 | rex_config::set('ycom', 'otp_auth_option', $option); 68 | } 69 | 70 | /** 71 | * @return rex_ycom_otp_method_interface 72 | */ 73 | public function getMethod() 74 | { 75 | if (null === $this->method) { 76 | $methodType = rex_ycom_otp_password_config::forCurrentUser()->getMethod(); 77 | 78 | if ('totp' === $methodType) { 79 | $this->method = new rex_ycom_otp_method_totp(); 80 | } elseif ('email' === $methodType) { 81 | $this->method = new rex_ycom_otp_method_email(); 82 | } else { 83 | throw new InvalidArgumentException("Unknown method: $methodType"); 84 | } 85 | } 86 | 87 | return $this->method; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /docs/01_intro.md: -------------------------------------------------------------------------------- 1 | # Einrichtung 2 | 3 | Nach der Installation von YCom werden für die Inbetriebnahme einige Schritte benötigt. 4 | 5 | ## 1. Formbuilder installieren 6 | 7 | YForm Formbuilder installieren unter `YForm` > `Übersicht` > `Setup` > `YForm Formbuilder installieren` 8 | 9 | ## 2. Struktur vorbereiten 10 | 11 | Folgende Kategorien in der Struktur in oberster Ebene anlegen: 12 | 13 | * **Login**: Menüpunkt soll angezeigt werden, wenn der Besucher nicht eingeloggt ist. 14 | 15 | * **Logout**: Menüpunkt soll angezeigt werden, wenn der Besucher eingeloggt ist. 16 | 17 | * Unter `Logout` optional folgende Kategorien anlegen: 18 | 19 | * **Passwort ändern**: Dieser Artikel soll ein Formular zum Ändern des Passworts beinhalten. 20 | 21 | > Hinweis: Die hier vorgeschlagene ist nicht verpflichtend, wird jedoch für Anfänger empfohlen. 22 | 23 | ## Navigation vorbereiten 24 | 25 | Die Navigation muss angepasst werden, sodass Kategorien und Artikel, für die der Nutzer keine Berechtigung hat, nicht dargestellt werden. Sofern die Core-Funktion `rex_navigation::factory()` verwendet wird, folgende Zeile hinzufügen: `$nav->addCallback('rex_ycom_auth::articleIsPermitted');` 26 | 27 | > Hinweis: Wenn die Navigation nicht via `rex_navigation::factory()` 28 | erfolgt, werden ggf. alle Menüpunkte angezeigt. Die Navigation muss dann so erweitert werden, dass Nutzer ohne Berechtigungen die entsprechenden Menüpunkte nicht sehen kann. 29 | 30 | ## YCom konfigurieren 31 | 32 | YCom Einstellungsseite im REDAXO-Backend aufrufen unter `YCom` > `Einstellungen` und konfigurieren: 33 | 34 | * **Weiterleitungen**: 35 | * als `article_id_jump_ok` bspw. die Startseite verwenden. 36 | * als `article_id_jump_not_ok` (nur SAML-Authentifizierung) bspw. den Login-Artikel verwenden. 37 | * als `article_id_jump_logout` bspw. den Login-Artikel oder die Startseite verwenden. 38 | * als `article_id_jump_denied` bspw. den Login-Artikel verwenden oder einen eigenen Artikel anlegen. 39 | * **Allgemeine Seiten** 40 | * als Login-Seite den Login-Artikel auswählen. 41 | * **Login-Feld** 42 | * als Login-Feld die Option E-Mail-Adresse (`email`) auswählen. Alternativ `login`, wenn bspw. ein eigenes Pseudonym als Nutzername verwendet werden soll. 43 | 44 | > Hinweis: Dies sind die empfohlenen Minimal-Einstellungen für einen reibungslosen Betrieb. 45 | 46 | ## Weitere Schritte 47 | 48 | Anschließend können Module und Templates in Abhängigkeit des Login-Status (eingeloggt/ausgeloggt) dargestellt werden. 49 | 50 | ```php 51 | $ycom_user = rex_ycom_auth::getUser() // zu Beginn eines Moduls oder Templates 52 | 53 | if ($ycom_user) { 54 | // Benutzter besitzt die nötige Berechtigung 55 | } else { 56 | // Benutzter besitzt die nötige Berechtigung nicht 57 | } 58 | ``` 59 | > Siehe auch: [User und Gruppen auslesen](index.php?page=ycom/docs&mdfile=06_user_groups) 60 | -------------------------------------------------------------------------------- /lang/sv_se.lang: -------------------------------------------------------------------------------- 1 | ycom_perm = Community behörigheter 2 | 3 | ycom_page_perm = Community sidbehörigheter 4 | 5 | navigation_ycom = Community 6 | ycom_title = Community: inställningar 7 | ycom_navi_title = Inställningar 8 | 9 | ycom_main_title = Info 10 | ycom_info = Readme 11 | ycom_info_title = Nyttig infomation om hur du använder Community AddOn 12 | ycom_manual = Manual / exempel 13 | ycom_manual_title = Manual / exempel 14 | ycom_changelog = Change log 15 | ycom_changelog_title = Change log 16 | ycom_license = Licens 17 | ycom_license_title = Licens 18 | ycom_overview = Översikt 19 | ycom_user_management = Användar förvaltning 20 | ycom_field_management = Anpassa fälten 21 | 22 | ycom_user = Användare 23 | yform_ycom_user = Community användare 24 | rex_ycom_user = Community användare 25 | 26 | email = E-post 27 | status = Status 28 | firstname = Förnamn 29 | activation_key = Aktiveringsnyckel 30 | session_key = Session key 31 | last_login_time = Senaste inloggning 32 | last_action_time = Senaste aktion 33 | termination_time = Tidspunkt för uppsägning 34 | login_failed = Misslyckade inloggningar 35 | login_tries = Misslyckade försök att logga in 36 | 37 | ycom_login_tries_info = Efter 10 misslyckade försök att logga in kommer användaren att blockeras. 38 | 39 | ycom_account_inactive_termination = Tillträdet avslutades 40 | ycom_account_inactive_logins = Åtkomst har inaktiverats [Inloggning misslyckades] 41 | ycom_account_inactive = åtkomst är inaktiverad 42 | ycom_account_requested = åtkomst begärd 43 | ycom_account_confirm = Tillträde har bekräftats 44 | ycom_account_active = åtkomst är aktiverad 45 | 46 | ycom_status = Status 47 | 48 | ycom_perm_type = Sidbehörigheter 49 | 50 | ycom_perm_extends = Ärva från förälder kategori eller tiilgänglig för alla användare 51 | ycom_perm_only_logged_in = Tillgång för inloggade användare 52 | ycom_perm_only_not_logged_in = Tillgång för alla inte inloggade användare 53 | ycom_perm_all = Tillgång för alla användare 54 | 55 | ycom_this_login_exists_already = Användarnamnet finns redan 56 | ycom_this_email_exists_already = E-postadressen finns redan 57 | ycom_please_enter_login = Var god ange användarnamnet 58 | ycom_please_enter_email = Var god ange e-post adressen 59 | 60 | last_password_time = Senaste lösenordbyte 61 | termsofuse_accepted = villkoren bekräftades 62 | new_password_required = Du måste välja ett nytt lösenord 63 | 64 | ycom_docs_intro = Inrättning 65 | ycom_docs_navigations = Navigationen 66 | ycom_docs_passwords = Lösenord 67 | ycom_docs_settings = Inställningar 68 | ycom_docs_extern_auth = Extern autentisering 69 | ycom_docs_plugin_media_auth = MediaAuth-Plugin 70 | ycom_docs_login_logout_profile_register = Login/Logout/Profile/Registrering 71 | docs = Dokumentation 72 | ycom_docs_tricks = Tricks 73 | ycom_docs_user_groups = Läsa ut användare och grupper 74 | ycom_docs = Dokumentation 75 | -------------------------------------------------------------------------------- /plugins/auth/lib/yform/validate/ycom_auth.php: -------------------------------------------------------------------------------- 1 | getObjects() as $Object) { 13 | if ($this->isObject($Object)) { 14 | if ($Object->getName() == $this->getElement(2)) { 15 | $loginObject = $Object; 16 | $warningObjects[] = $Object; 17 | } 18 | if ($Object->getName() == $this->getElement(3)) { 19 | $passwordObject = $Object; 20 | $warningObjects[] = $Object; 21 | } 22 | if ($Object->getName() == $this->getElement(4)) { 23 | $stayObject = $Object; 24 | } 25 | } 26 | } 27 | 28 | if (!$loginObject || !$passwordObject || '' == $loginObject->getValue() || '' == $passwordObject->getValue()) { 29 | foreach ($warningObjects as $warningObject) { 30 | $this->params['warning'][$warningObject->getId()] = $this->params['error_class']; 31 | $this->params['warning_messages'][$warningObject->getId()] = $this->getElement(5); 32 | } 33 | rex_ycom_auth::clearUserSession(); 34 | return; 35 | } 36 | 37 | if (0 < count($this->params['warning'])) { 38 | return; 39 | } 40 | 41 | /* 42 | login_status 43 | 0: not logged in 44 | 1: logged in 45 | 2: has logged in 46 | 3: has logged out 47 | 4: login failed 48 | */ 49 | 50 | $params = []; 51 | $params['loginName'] = (string) $loginObject->getValue(); 52 | $params['loginPassword'] = (string) $passwordObject->getValue(); 53 | $params['loginStay'] = ($stayObject) ? (bool) $stayObject->getValue() : false; 54 | $params['ignorePassword'] = false; 55 | $params['filter'] = [ 56 | 'status > 0', 57 | ]; 58 | $status = rex_ycom_auth::login($params); 59 | 60 | if (rex_ycom_auth::STATUS_HAS_LOGGED_IN != $status) { 61 | foreach ($warningObjects as $warningObject) { 62 | $this->params['warning'][$warningObject->getId()] = $this->params['error_class']; 63 | $this->params['warning_messages'][$warningObject->getId()] = $this->getElement(6); 64 | } 65 | } 66 | } 67 | 68 | public function getDescription(): string 69 | { 70 | return 'ycom_auth -> prüft ob login und registriert user, beispiel: validate|ycom_auth|loginfield|passwordfield|stayfield|warning_message_enterloginpsw|warning_message_login_failed'; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/unit/user_status_test.php: -------------------------------------------------------------------------------- 1 | hier wird, wenn erfolgereich, auf die UserStartseite geleitet 25 | 26 | // -> wenn user enabled ist dann auf setup und es eventuell zu deaktivieren 27 | 28 | // Verify - Eingabe des Codes 29 | // -> hier wird, wenn erfolgereich, auf die UserStartseite geleitet 30 | 31 | $page = 'setup'; 32 | $SessionInstance = null; 33 | $config = rex_ycom_otp_password_config::forCurrentUser(); 34 | if ($config->enabled) { 35 | $SessionInstance = rex_ycom_user_session::getInstance()->getCurrentSession($user); 36 | if (1 != $SessionInstance['otp_verified']) { 37 | $page = 'verify'; 38 | } 39 | } 40 | 41 | switch ($page) { 42 | case 'verify': 43 | $this->params['form_output'][$this->getId()] = $this->parse( 44 | ['value.ycom_auth_otp_verify.tpl.php'], 45 | [ 46 | 'user' => $user, 47 | 'SessionInstance' => $SessionInstance, 48 | 'config' => $config, 49 | 'otp_article_id' => $otp_article_id, 50 | ], 51 | ); 52 | break; 53 | default: 54 | // setup 55 | $this->params['form_output'][$this->getId()] = $this->parse( 56 | ['value.ycom_auth_otp_setup.tpl.php'], 57 | [ 58 | 'user' => $user, 59 | 'SessionInstance' => $SessionInstance, 60 | 'config' => $config, 61 | 'otp_article_id' => $otp_article_id, 62 | ], 63 | ); 64 | } 65 | } 66 | 67 | public function getDescription(): string 68 | { 69 | return 'ycom_auth_otp -> Beispiel: ycom_auth_otp'; 70 | } 71 | 72 | /** 73 | * @return array 74 | */ 75 | public function getDefinitions(): array 76 | { 77 | return [ 78 | 'type' => 'value', 79 | 'name' => 'ycom_auth_otp', 80 | 'values' => [ 81 | 'name' => ['type' => 'name', 'label' => 'Feld'], 82 | 'label' => ['type' => 'text', 'label' => 'Bezeichnung'], 83 | ], 84 | 'description' => '', 85 | 'famous' => false, 86 | ]; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /plugins/auth/ytemplates/bootstrap/value.ycom_auth_otp_verify.tpl.php: -------------------------------------------------------------------------------- 1 | getMethod(); 13 | $blockTime = (int) ($OTPMethod::getPeriod() / 10); 14 | $loginTriesAllowed = $OTPMethod::getloginTries(); 15 | $loginTries = (int) $user->getValue('otp_tries'); 16 | $loginLastTry = (int) $user->getValue('otp_last_try_time'); 17 | 18 | $myOTP = rex_post('otp', 'string', null); 19 | 20 | if (null !== $myOTP) { 21 | if ($loginTries >= $loginTriesAllowed && ($loginLastTry > time() - $blockTime)) { 22 | $this->params['warning'][$this->getId()] = $this->params['error_class']; 23 | $this->params['warning_messages'][$this->getId()] = '{{ ycom_otp_code_error_blocked }}'; 24 | $countdownTime = $loginLastTry - time() + $blockTime; 25 | 26 | echo ''; 40 | } else { 41 | if ($OTPInstance->verify($myOTP)) { 42 | $user->resetOTPTries()->save(); 43 | rex_ycom_user_session::getInstance()->setOTPVerified($user); 44 | $article_jump_ok = (int) rex_ycom_config::get('article_id_jump_ok'); 45 | rex_response::sendRedirect(rex_getUrl($article_jump_ok, rex_clang::getCurrentId())); 46 | } else { 47 | $user->increaseOTPTries()->save(); 48 | $this->params['warning'][$this->getId()] = $this->params['error_class']; 49 | $this->params['warning_messages'][$this->getId()] = '{ ycom_otp_code_error }'; 50 | } 51 | } 52 | } else { 53 | if ('rex_ycom_otp_method_email' === $OTPMethod::class) { 54 | rex_ycom_otp_password::getInstance()->challenge(); 55 | } 56 | } 57 | 58 | ?> 59 | 60 |
61 |
62 |
63 |
64 |

{ ycom_otp_verify_title }

65 |
66 |
67 | 68 |
69 |
70 | 71 | 72 |
73 |
74 |
75 |
76 |
77 |
78 | -------------------------------------------------------------------------------- /docs/10_otp.md: -------------------------------------------------------------------------------- 1 | # OTP (OneTimePassword) Authentifizierung (2FA) 2 | 3 | ## Allgemeine Konfiguration 4 | 5 | Zunächst muss die Konfiguration in den YCom Einstellungen vorgenommen werden. Die entsprechenden Artikel für das OTP Setup und die OTP Überprüfung müssen angelegt werden und über die YCom Permission nur für eingeloggte User verfügbar gemacht werden. 6 | 7 | ## Benötigte Artikel 8 | 9 | Es wird 1 REDAXO Artikel benötigt. Dieser führt das initale SetUp und auch die Verifizierung durch. Dieser Artikel muss in der Einstellungseite verlinkt sein 10 | 11 | ### Artikel für das OTP Setup 12 | 13 | Im OTP Artikel das YForm Builder Modul verwenden und diesen YFormCode einsetzen. Rechte müssen auf "Zugriff für eingeloggte User" gesetzt sein. 14 | 15 | ``` 16 | ycom_auth_otp|setup 17 | ``` 18 | 19 | ## Einleitung 20 | 21 | Die 2-Faktor-Authentifizierung (2FA) ist eine zusätzliche Sicherheitsebene für Ihr Konto. Sie schützt Ihr Konto vor unbefugtem Zugriff, selbst wenn Ihr Passwort kompromittiert wurde. 22 | 23 | Die 2FA ist eine Authentifizierungsmethode, bei der zwei verschiedene Faktoren verwendet werden, um die Identität einer Person zu bestätigen. 24 | 25 | Hier werden 2 Möglichkeiten angeboten. 26 | 27 | * One Time Passwort über E-Mail. 28 | * One Time Passwort über (Google) Authenticator. 29 | 30 | Die 2FA ist eine der besten Möglichkeiten, um Ihr Konto zu schützen, da sie sicherstellt, dass nur Sie auf Ihr Konto zugreifen können, selbst wenn jemand Ihr Passwort kennt. 31 | 32 | ## Einrichtung 33 | 34 | Die 2FA kann in Ihrem Konto aktiviert werden. Dazu müssen Sie sich zunächst anmelden und die 2FA in den Einstellungen aktivieren. Anschließend müssen Sie einen Sicherheitsschlüssel hinzufügen, der zur Authentifizierung verwendet wird. 35 | 36 | Die 2FA kann auf verschiedene Arten aktiviert werden, z.B. durch die Eingabe eines Codes, der per SMS oder E-Mail gesendet wird, oder durch die Verwendung einer Authentifizierungs-App wie Google Authenticator oder Authy. 37 | 38 | ## Verwendung 39 | 40 | Nachdem die 2FA aktiviert wurde, müssen Sie sich bei jedem Anmeldeversuch zusätzlich zur Eingabe Ihres Passworts auch mit einem Sicherheitsschlüssel authentifizieren. Dies kann z.B. durch die Eingabe eines Codes aus der Authentifizierungs-App erfolgen. 41 | 42 | Die 2FA bietet eine zusätzliche Sicherheitsebene für Ihr Konto und schützt es vor unbefugtem Zugriff. Wir empfehlen daher, die 2FA zu aktivieren, um Ihr Konto zu schützen. 43 | 44 | ## Umsetzung in REDAXO 45 | 46 | In REDAXO kann die 2FA z.B. mit dem AddOn YCom umgesetzt werden. Dazu müssen Sie zunächst die 2FA in den Einstellungen von YCom aktivieren und einen Sicherheitsschlüssel hinzufügen. Anschließend müssen Sie sich bei jedem Anmeldeversuch zusätzlich zur Eingabe Ihres Passworts auch mit einem Sicherheitsschlüssel authentifizieren. 47 | 48 | Die 2FA bietet eine zusätzliche Sicherheitsebene für Ihr REDAXO-Konto und schützt es vor unbefugtem Zugriff. Wir empfehlen daher, die 2FA zu aktivieren, um Ihr Konto zu schützen. 49 | 50 | ## E-Mail Template 51 | 52 | Standardmäßig wird eine vorbereitete E-Mail mit festem Text verschickt. 53 | 54 | Es kann aber ein eigenes YForm-Template für die 2FA verwendet. Mit dem Key ```ycom_otp_code_template``` kann ein eigenes Template gesetzt wird. Folgende Werte sind verfügbar: ```name, email, firstname, code```. 55 | -------------------------------------------------------------------------------- /docs/07_plugin_media_auth.md: -------------------------------------------------------------------------------- 1 | # MediaAuth-Plugin 2 | 3 | Mit dem MediaAuth-Plugin werden Medien des Medienpools geschützt, sodass diese im Frontend nur unter bestimmten Vorraussetzungen aufgerufen werden können, bspw. nur im eingeloggten Zustand und nur für bestimmte YCom-Gruppen. 4 | 5 | Dazu ist das YRrewrite-Plugin `media_auth` nötig, das alle Medien auf ihre Berechtigung überprüft. 6 | 7 | > **Technische Erläuterung:** Anstelle eines direkten Aufrufs, bspw. `/media/meine_-_datei.pdf` werden die Aufrufe durch eine `.htaccess`-Regel über den **Media Manager** geleitet. Dort sorgt ein Authentifzierungs-Effekt dafür, dass die Berechtigungen des Besuchers überprüft werden und ggf die Auslieferung verweigert wird. 8 | 9 | > **Achtung:** Sofern man als Redakteur/Administrator im REDAXO-Backend eingeloggt ist, hat man immer Zugriff zu allen Medien. Daher empfehlen deswegen bei Tests immer darauf achten, dass man im Backend ausgeloggt ist. 10 | 11 | ## Einrichtung 12 | 13 | 1. Das Addon `YRewrite` installieren, aktivieren und das Setup ausführen 14 | 2. Das YCom-Plugin `media_auth` aktivieren 15 | 3. Unter YCom / Einstellungen unter dem Tab "Medien" Authentifizierung aktivieren 16 | 4. Im Medienpool den Dateien entsprechende Rechte geben. 17 | 18 | ## optional: Mediendateien nach Upload schützen 19 | 20 | Nutze den EP `MEDIA_ADDED`, um Medienuploads in eine bestimmte Kategorie standardmäßig zu schützen. Anbei ein Beispiel, das man an seine eigenen Anforderungen anpassen kann. 21 | 22 | In die `boot.php` deines Addons oder des Project-Addons notieren: 23 | 24 | ```php 25 | if (rex::isBackend() && rex::getUser()) { 26 | rex_extension::register('MEDIA_ADDED', ['project','media_added'], rex_extension::LATE); 27 | } 28 | ``` 29 | 30 | In deinem Addon-Code oder Project-Addon notieren, hier: `lib/project.php`: 31 | 32 | ```php 33 | getParams(); 39 | $media = rex_media::get($mediafile['filename']); 40 | 41 | if ($media->getCategory()->getId() == 1 || $media->getCategory()->getParent()->getId() == 1) { 42 | $sql = rex_sql::factory(); 43 | $sql->setWhere('filename="'.$mediafile['filename'].'"'); 44 | $sql->setTable(rex::getTablePrefix() . 'media'); 45 | $sql->setValue('ycom_auth_type', 1); 46 | $sql->update(); 47 | } 48 | $media = rex_media::get($mediafile['filename']); 49 | } 50 | } 51 | ``` 52 | 53 | ## Fehlerbehebung 54 | 55 | * **Das Schützen der Datei funktioniert nicht**: Sicherstellen, dass YRewrite installiert ist, aktiviert ist und die `.htaccess` im Hauptverzeichnis folgende Zeile enthält: 56 | `RewriteRule ^media/(.*) %{ENV:BASE}/index.php?rex_media_type=default&rex_media_file=$1&%{QUERY_STRING} [B]` 57 | 58 | * **Bestimmte Dateitypen werden im Frontend nicht mehr korrekt dargestellt / heruntergeladen**: Bitte hier melden: [YRewrite GitHub](https://github.com/yakamara/redaxo_yrewrite/issues/235) 59 | 60 | 61 | ## Überprüfung von Medien, welche im MediaManager einen eigene Pfad haben 62 | 63 | ``` 64 | rex_extension::register(['MEDIA_MANAGER_BEFORE_SEND'], function (rex_extension_point $ep) { 65 | /** @var rex_media_manager $mm */ 66 | $mm = $ep->getSubject(); 67 | $originalMediaPath = dirname($mm->getMedia()->getSourcePath()); 68 | $deinPfad = '.../testbilder/'; 69 | if ($originalMediaPath == $deinPfad) { 70 | // eigene überprüfung 71 | } 72 | }, rex_extension::EARLY); 73 | ``` 74 | -------------------------------------------------------------------------------- /docs/12_status_examples.md: -------------------------------------------------------------------------------- 1 | # YCom User Status - Beispiele 2 | 3 | ## Status-Konstanten verwenden 4 | 5 | ### Im PHP-Code 6 | 7 | ```php 8 | setValue('email', 'test@example.com'); 13 | $user->setValue('login', 'test@example.com'); 14 | $user->setValue('status', rex_ycom_user::STATUS_CONFIRMED); 15 | $user->save(); 16 | 17 | // Status prüfen 18 | $currentUser = rex_ycom_user::getMe(); 19 | if ($currentUser && $currentUser->getValue('status') >= rex_ycom_user::STATUS_CONFIRMED) { 20 | // User ist eingeloggt und bestätigt 21 | } 22 | 23 | // Status auf inaktiv setzen 24 | $user->setValue('status', rex_ycom_user::STATUS_INACTIVE); 25 | $user->save(); 26 | ``` 27 | 28 | ## Status-Optionen erweitern via Extension Point 29 | 30 | ### Beispiel 1: Eigenen Status hinzufügen 31 | 32 | ```php 33 | $statusOptions */ 37 | $statusOptions = $ep->getSubject(); 38 | 39 | // Premium-Status hinzufügen (Wert 3) 40 | $statusOptions[3] = 'translate:ycom_account_premium'; 41 | 42 | // VIP-Status hinzufügen (Wert 4) 43 | $statusOptions[4] = 'translate:ycom_account_vip'; 44 | 45 | return $statusOptions; 46 | }); 47 | ``` 48 | 49 | ### Beispiel 2: Status-Optionen anpassen und sortieren 50 | 51 | ```php 52 | $statusOptions */ 56 | $statusOptions = $ep->getSubject(); 57 | 58 | // Bestimmte Stati entfernen (optional) 59 | unset($statusOptions[rex_ycom_user::STATUS_INACTIVE_TERMINATION]); 60 | 61 | // Eigene Stati hinzufügen 62 | $statusOptions[10] = 'translate:ycom_account_special'; 63 | 64 | // Nach Schlüssel sortieren 65 | ksort($statusOptions); 66 | 67 | return $statusOptions; 68 | }); 69 | ``` 70 | 71 | ### Beispiel 3: Status-Labels projektspezifisch anpassen 72 | 73 | ```php 74 | $statusOptions */ 78 | $statusOptions = $ep->getSubject(); 79 | 80 | // Standard-Labels durch eigene ersetzen 81 | $statusOptions[rex_ycom_user::STATUS_ACTIVE] = 'Aktives Mitglied'; 82 | $statusOptions[rex_ycom_user::STATUS_CONFIRMED] = 'Bestätigtes Mitglied'; 83 | $statusOptions[rex_ycom_user::STATUS_REQUESTED] = 'Registrierung ausstehend'; 84 | 85 | return $statusOptions; 86 | }); 87 | ``` 88 | 89 | ## Verfügbare Status-Konstanten 90 | 91 | | Konstante | Wert | Standard-Label | 92 | |-----------|------|----------------| 93 | | `rex_ycom_user::STATUS_INACTIVE_TERMINATION` | -3 | Zugang wurde gekündigt | 94 | | `rex_ycom_user::STATUS_INACTIVE_LOGINS` | -2 | Zugang wurde deaktiviert [Loginfehlversuche] | 95 | | `rex_ycom_user::STATUS_INACTIVE` | -1 | Zugang ist inaktiv | 96 | | `rex_ycom_user::STATUS_REQUESTED` | 0 | Zugang wurde angefragt | 97 | | `rex_ycom_user::STATUS_CONFIRMED` | 1 | Zugang wurde bestätigt und ist aktiv | 98 | | `rex_ycom_user::STATUS_ACTIVE` | 2 | Zugang ist aktiv | 99 | 100 | ## Status-Logik 101 | 102 | - **Login erlaubt**: Status >= 1 (STATUS_CONFIRMED oder höher) 103 | - **Login nicht erlaubt**: Status <= -1 (STATUS_INACTIVE oder niedriger) 104 | - **Registriert, aber Nutzungsbedingungen fehlen**: Status = 0 (STATUS_REQUESTED) 105 | -------------------------------------------------------------------------------- /lang/de_de.lang: -------------------------------------------------------------------------------- 1 | ycom_perm = Community Rechte 2 | 3 | ycom_page_perm = Community Seitenrechte 4 | 5 | navigation_ycom = Community 6 | ycom_title = Community 7 | ycom_navi_title = Einstellungen 8 | 9 | ycom_main_title = Info 10 | ycom_info = Readme 11 | ycom_info_title = Nützliche Informationen zur Verwendung des Community Addons 12 | ycom_manual = Anleitung / Beispiele 13 | ycom_manual_title = Anleitung / Beispiele 14 | ycom_changelog = Changelog 15 | ycom_changelog_title = Changelog 16 | ycom_license = Lizenz 17 | ycom_license_title = Lizenz 18 | ycom_overview = Übersicht 19 | ycom_user_management = User Verwaltung 20 | ycom_field_management = Felder anpassen 21 | 22 | ycom_user = User 23 | yform_ycom_user = Community Benutzer 24 | rex_ycom_user = User 25 | 26 | email = E-Mail 27 | status = Status 28 | firstname = Vorname 29 | activation_key = Aktivierungsschlüssel 30 | session_key = Sessionschlüssel 31 | last_login_time = Letzter erfolgreicher Login 32 | last_login_try_time = Letzter versuchter Login 33 | last_action_time = Letzte Aktion 34 | termination_time = Kündigungszeitpunkt 35 | login_failed = Fehlgeschlagene Logins 36 | login_tries = Login Fehlversuche 37 | 38 | ycom_login_tries_info = Erlaubte Fehlversuche werden im Plugin YCom/Auth definiert. 39 | 40 | ycom_account_inactive_termination = Zugang wurde gekündigt 41 | ycom_account_inactive_logins = Zugang wurde deaktiviert [Loginfehlversuche] 42 | ycom_account_inactive = Zugang ist inaktiv 43 | ycom_account_requested = Zugang wurde angefragt 44 | ycom_account_confirm = Zugang wurde bestätigt und ist aktiv 45 | ycom_account_active = Zugang ist aktiv 46 | 47 | ycom_status = Status 48 | 49 | ycom_perm_type = Seitenberechtigungen 50 | 51 | ycom_perm_extends = Aus übergeordneter Kategorie übernehmen ansonsten Zugriff für alle User 52 | ycom_perm_only_logged_in = Zugriff für eingeloggte User 53 | ycom_perm_only_not_logged_in = Zugriff für alle nicht eingeloggten User 54 | ycom_perm_all = Zugriff für alle User 55 | 56 | ycom_this_login_exists_already = Dieser Login existiert bereits 57 | ycom_this_email_exists_already = Diese E-Mail existiert bereits 58 | ycom_please_enter_login = Bitte das Login eingeben 59 | ycom_please_enter_email = Bitte die E-mail eingeben 60 | ycom_this_activation_key_exists_already = Diese Activation-Key existiert bereits 61 | 62 | last_password_time = Letzter Passwortwechsel 63 | termsofuse_accepted = Nutzungsbedingungen bestätigt 64 | new_password_required = Neues Passwort muss gesetzt werden 65 | 66 | ycom_docs_intro = Einrichtung 67 | ycom_docs_navigations = Navigationen 68 | ycom_docs_passwords = Passwörter 69 | ycom_docs_settings = Einstellungen 70 | ycom_docs_extern_auth = Externe Authentifizierung 71 | ycom_docs_plugin_media_auth = MediaAuth-Plugin 72 | ycom_docs_login_logout_profile_register = Login/Logout/Profile/Registrierung 73 | docs = Dokumentation 74 | ycom_docs_tricks = Tricks 75 | ycom_docs_user_groups = User und Gruppen auslesen 76 | ycom_docs = Dokumentation 77 | ycom_docs_otp = 2FA (Zwei-Faktor-Authentifizierung) 78 | 79 | ycom_log = Log 80 | ycom_user_log = YCom-User-Log 81 | ycom_user_log_title = Log-Übersicht 82 | ycom_user_log_warning_logisinactive = YCom-User-Log ist inaktiv. Wir empfehlen es zu aktivieren. aktivieren 83 | ycom_user_log_warning_logisactive = YCom-User-Log ist aktiv. deaktivieren 84 | ycom_user_log_activated = YCom-User-Log wurde aktiviert 85 | ycom_user_log_deactivated = YCom-User-Log wurde deaktiviert 86 | ycom_user_log_time = Zeitstempel 87 | ycom_user_log_user_id = User-Id 88 | ycom_user_log_email = E-Mail 89 | ycom_user_log_type = Type 90 | ycom_user_log_params = Params 91 | ycom_user_log_ip = IP 92 | -------------------------------------------------------------------------------- /plugins/auth/lib/otp/method_email.php: -------------------------------------------------------------------------------- 1 | at(time()); 14 | 15 | if ($ycom_otp_code_template = rex_yform_email_template::getTemplate(self::$yform_email_template_key)) { 16 | $values = []; 17 | $values['email'] = $user->getValue('email'); 18 | $values['name'] = $user->getValue('name'); 19 | $values['firstname'] = $user->getValue('firstname'); 20 | $values['code'] = $otpCode; 21 | 22 | $yform_email_template = rex_yform_email_template::replaceVars($ycom_otp_code_template, $values); 23 | $yform_email_template['mail_to'] = $user->getValue('email'); 24 | $yform_email_template['mail_to_name'] = $user->getValue('name'); 25 | 26 | if (!rex_yform_email_template::sendMail($yform_email_template, self::$yform_email_template_key)) { 27 | throw new Exception('Unable to send email via Template. Make sure to setup the phpmailer AddOn.'); 28 | } 29 | } else { 30 | $mail = new rex_mailer(); 31 | $mail->addAddress($user->getValue('email')); 32 | $mail->Subject = 'OTP-Code: (' . $_SERVER['HTTP_HOST'] . ')'; 33 | $mail->isHTML(); 34 | $mail->Body = '

' . rex::getServerName() . ' Login verification


' . $otpCode . '


is your 2 factor authentication code.'; 35 | $mail->AltBody = " Login verification \r\n ------------------ \r\n" . $otpCode . "\r\n ------------------ \r\nis your 2 factor authentication code."; 36 | 37 | if (!$mail->send()) { 38 | throw new Exception('Unable to send email. Make sure to setup the phpmailer AddOn.'); 39 | } 40 | } 41 | } 42 | 43 | public static function getPeriod(): int 44 | { 45 | return (int) rex_addon::get('2factor_auth')->getConfig('email_period', 300); 46 | } 47 | 48 | public static function getloginTries(): int 49 | { 50 | return 10; 51 | } 52 | 53 | public function verify(string $provisioningUrl, string $otp): bool 54 | { 55 | $TOTP = Factory::loadFromProvisioningUri($provisioningUrl); 56 | 57 | // re-create from an existant uri 58 | if ($TOTP->verify($otp)) { 59 | return true; 60 | } 61 | 62 | $lastOTPCode = $TOTP->at(time() - self::getPeriod()); 63 | if ($lastOTPCode == $otp) { 64 | return Factory::loadFromProvisioningUri($provisioningUrl)->verify($TOTP->at(time())); 65 | } 66 | return false; 67 | } 68 | 69 | public function getProvisioningUri(rex_ycom_user $user): string 70 | { 71 | // create a uri with a random secret 72 | $otp = TOTP::create(null, self::getPeriod()); 73 | 74 | // the label rendered in "Google Authenticator" or similar app 75 | $label = $user->getValue('login') . '@' . rex::getServerName() . ' (' . $_SERVER['HTTP_HOST'] . ')'; 76 | $label = str_replace(':', '_', $label); // colon is forbidden 77 | $otp->setLabel($label); 78 | $otp->setParameter('period', self::getPeriod()); 79 | $otp->setIssuer(str_replace(':', '_', $user->getValue('login'))); 80 | 81 | return $otp->getProvisioningUri(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /plugins/media_auth/lib/ycom_media_auth_rules.php: -------------------------------------------------------------------------------- 1 | }> */ 10 | private array $rules; 11 | 12 | public function __construct() 13 | { 14 | $this->rules = []; 15 | $this->rules['redirect'] = [ 16 | 'info' => rex_i18n::msg('ycom_media_auth_failed_redirect_login'), 17 | 'action' => [ 18 | 'type' => 'redirect', 19 | 'article_id' => rex_ycom_config::get('article_id_login'), 20 | ], 21 | ]; 22 | $this->rules['redirect_with_errorpage'] = [ 23 | 'info' => rex_i18n::msg('ycom_media_auth_failed_redirect_login_with_error_page'), 24 | 'action' => [ 25 | 'type' => 'redirect', 26 | 'article_id' => rex_ycom_config::get('article_id_login'), 27 | 'error_article_id' => rex_ycom_config::get('article_id_jump_denied'), 28 | ], 29 | ]; 30 | $this->rules['header_notfound'] = [ 31 | 'info' => rex_i18n::msg('ycom_media_auth_failed_header_notfound'), 32 | 'action' => ['type' => 'header', 'header' => rex_response::HTTP_NOT_FOUND], 33 | ]; 34 | $this->rules['header_perm_denied'] = [ 35 | 'info' => rex_i18n::msg('ycom_media_auth_failed_header_perm_denied'), 36 | 'action' => ['type' => 'header', 'header' => rex_response::HTTP_UNAUTHORIZED], 37 | ]; 38 | } 39 | 40 | /** 41 | * @param string|null $rule_name 42 | * @throws rex_exception 43 | */ 44 | public function check($rule_name): void 45 | { 46 | if (!array_key_exists($rule_name ?? '', $this->rules)) { 47 | $rule_name = 'header_perm_denied'; 48 | } 49 | 50 | $rule = $this->rules[$rule_name]; 51 | 52 | switch ($rule['action']['type']) { 53 | case 'redirect': 54 | $me = rex_ycom_user::getMe(); 55 | if (null !== $me) { 56 | // logged in 57 | rex_response::setStatus(rex_response::HTTP_UNAUTHORIZED); 58 | if (isset($rule['action']['error_article_id'])) { 59 | rex_redirect($rule['action']['error_article_id']); 60 | } else { 61 | rex_response::sendContent(''); 62 | } 63 | exit; 64 | } 65 | rex_response::sendCacheControl(); 66 | rex_redirect($rule['action']['article_id'], '', ['returnTo' => $_SERVER['REQUEST_URI']]); 67 | 68 | // no break 69 | case 'redirect_wo_returnto': 70 | rex_response::sendCacheControl(); 71 | rex_redirect($rule['action']['article_id'], '', []); 72 | 73 | // no break 74 | case 'header': 75 | rex_response::setStatus($rule['action']['header']); 76 | rex_response::sendCacheControl(); 77 | rex_response::sendContent(''); 78 | break; 79 | 80 | default: 81 | throw new rex_exception(sprintf('Unknown auth_rule action key "%s".', $rule['action']['type'])); 82 | } 83 | exit; 84 | } 85 | 86 | /** 87 | * @return array 88 | */ 89 | public function getOptions(): array 90 | { 91 | $options = []; 92 | 93 | foreach ($this->rules as $key => $rule) { 94 | $options[$key] = $rule['info']; 95 | } 96 | 97 | return $options; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /plugins/auth/lang/sv_se.lang: -------------------------------------------------------------------------------- 1 | ycom_auth_title = Autentisering 2 | 3 | ycom_auth_settings = Inställningar 4 | ycom_auth_description = Förklaring 5 | 6 | ycom_auth_config = Konfiguration 7 | ycom_auth_config_save = Spara konfigurationen 8 | ycom_auth_config_status = Status 9 | ycom_auth_config_security = Säkerhet 10 | ycom_auth_config_forwarder = Omdirigerar till artikel 11 | ycom_auth_config_login_field = Login-fält 12 | 13 | ycom_auth_config_status_authactive = Autentisering aktiverad 14 | ycom_auth_config_status_stayactive = "Stanna inloggad" aktiverad 15 | ycom_auth_config_security_hashedpasswd = Krypterade lösenord (sha1) 16 | ycom_auth_config_id_jump_ok = ... om du har loggat in 17 | ycom_auth_config_id_jump_not_ok = ... om inte inloggad framgångsrikt 18 | ycom_auth_config_id_jump_logout = ... efter att ha loggat ut 19 | ycom_auth_config_id_jump_denied = ... när du begär åtkomst till en blockerad artikel 20 | ycom_auth_config_id_jump_denied_notice = Om du inte är inloggad kommer du att ledas till inloggningssidan. Först då kommer du att riktas till den nekade artikeln om du inte har några rättigheter till den här artikeln. 21 | 22 | ycom_auth_config_id_jump_activation_ok = ... efter framgångsrik aktivering 23 | ycom_auth_config_id_jump_activation_fail = .. efter misslyckad aktivering 24 | ycom_auth_config_id_jump_newpassword_ok = ... efter framgångsrik återställning av lösenord 25 | ycom_auth_config_id_jump_newpassword_fail = .. efter misslyckad återställning av lösenord 26 | ycom_auth_config_id_jump_password = ... om "sätt nytt nya lösenordet" (new_password_required field) är vald [valfritt] 27 | ycom_auth_config_id_jump_termsofuse = ... om användningsvillkoren ännu inte har bekräftats (termsofuse_accepterat fält inte är inställt) [valfritt] 28 | 29 | ycom_auth_settings_updated = Konfigurationen har sparats 30 | 31 | ycom_auth_info_title = Förklaring 32 | 33 | ycom_auth_perm = Åtkomsträttigheter 34 | ycom_auth_update_perm = Aktualisera 35 | ycom_auth_perm_updated = Rättigheterna aktualiserades 36 | 37 | ycom_auth_password_not_updated = Lösenordet har inte uppdaterats 38 | ycom_auth_password_updated = Lösenordet har uppdaterats 39 | ycom_auth_password_exists = Lösenordet finns 40 | ycom_auth_password_isempty = Lösenordet existerar inte 41 | 42 | ycom_auth_config_auth_rules = Autentiseringsregel 43 | ycom_auth_config_login_tries_deactivateafter = Användaren är inaktiverad efter {0} inloggningsförsök 44 | ycom_auth_config_login_tries = Maximal antal inloggningsförsök 45 | ycom_auth_config_login_tries_unlimited = Oändlig många försök 46 | ycom_auth_config_login_tries_pause = Användaren är låst i {1} minuter efter varje {0} inloggningsförsök 47 | ycom_auth_config_login_tries_always_pause = Användaren är låst i {1} minuter efter {0} inloggningsförsök 48 | 49 | perm_options_ycomArticlePermissions[] = YCom - att kunna ställa in sidrättigheter 50 | 51 | no_permission_to_edit = Inga redigeringsrättigheter 52 | 53 | ycom_validate_password_policy_rules_error = Vänligen observera standardreglerna minst 1 bokstav stor och liten, minst 8 tecken 54 | ycom_validate_password_policy_rules = Lösenordregler som JSON 55 | ycom_validate_password_policy_rules_error_message = Felmeddelande 56 | 57 | ycom_validate_password_policy_rules_script = Aktivera Password Creator 58 | 59 | ycom_auth_config_pages = Allmänna sidor 60 | ycom_auth_config_id_login = Login sida 61 | ycom_auth_config_id_logout = Logout-sida 62 | ycom_auth_config_id_register = Registreringssida 63 | ycom_auth_config_id_password = Återställ lösenord 64 | 65 | ycom_auth_config_auth_cookie_ttl = Cookie-varaktighet för "förbli inloggad" 66 | ycom_days = {0} dagar 67 | 68 | ycom_auth_config_extern = autentisering (SAML/CAS/OAUTH2) 69 | 70 | ycom_auth_howtoread_configvalues = Värden är via: rex_ycom_config :: get ('article_id_login', null); läsbara 71 | -------------------------------------------------------------------------------- /plugins/auth/lang/es_es.lang: -------------------------------------------------------------------------------- 1 | ycom_auth_title = Autenticación 2 | 3 | ycom_auth_settings = Ajustes 4 | ycom_auth_description = Descripción 5 | 6 | ycom_auth_config = Configuración 7 | ycom_auth_config_save = Guardar configuración 8 | ycom_auth_config_status = Estado 9 | ycom_auth_config_security = Seguridad 10 | ycom_auth_config_forwarder = Reenviar al artículo 11 | ycom_auth_config_login_field = botón Acceder 12 | 13 | ycom_auth_config_status_authactive = Autenticación activa 14 | ycom_auth_config_status_stayactive = "Mantenerse conectado" activo 15 | ycom_auth_config_security_hashedpasswd = Contraseñas cifradas (sha1) 16 | ycom_auth_config_id_jump_ok = ... si ha iniciado sesión correctamente 17 | ycom_auth_config_id_jump_not_ok = ... si no ha iniciado sesión correctamente 18 | ycom_auth_config_id_jump_logout = ...después de desconectarse 19 | ycom_auth_config_id_jump_denied = ...al acceder a un artículo bloqueado 20 | ycom_auth_config_id_jump_denied_notice = Si no ha iniciado sesión, se lo dirigirá a la página de inicio de sesión. Solo entonces se lo dirigirá al artículo denegado si no tiene derechos sobre este artículo. 21 | 22 | ycom_auth_config_id_jump_activation_ok = ...después de la activación exitosa 23 | ycom_auth_config_id_jump_activation_fail = .. después de la activación fallida 24 | ycom_auth_config_id_jump_newpassword_ok = ...después de restablecer con éxito la contraseña 25 | ycom_auth_config_id_jump_newpassword_fail = .. después de un restablecimiento de contraseña fallido 26 | ycom_auth_config_id_jump_password = ...si se establece una nueva contraseña (el campo new_password_required) se establece [opcional] 27 | ycom_auth_config_id_jump_termsofuse = ...si los términos de uso no se han confirmado (no se ha establecido el campo Termsofuse_accepted) [opcional] 28 | 29 | ycom_auth_settings_updated = Se ha guardado la configuración. 30 | 31 | ycom_auth_info_title = Información 32 | 33 | ycom_auth_perm = Permisos 34 | ycom_auth_update_perm = Actualizar 35 | ycom_auth_perm_updated = Permisos actualizados 36 | 37 | ycom_auth_password_not_updated = La contraseña no fue actualizada 38 | ycom_auth_password_updated = La contraseña fue actualizada 39 | ycom_auth_password_exists = La contraseña existe 40 | ycom_auth_password_isempty = La contraseña no existe 41 | 42 | ycom_auth_config_auth_rules = Reglas para la autenticación 43 | ycom_auth_config_login_tries_deactivateafter = El usuario está desactivado después de {0} intentos de inicio de sesión 44 | ycom_auth_config_login_tries = Intentos máximos de inicio de sesión 45 | ycom_auth_config_login_tries_unlimited = Intentos ilimitados 46 | ycom_auth_config_login_tries_pause = El usuario está bloqueado durante {1} minutos después de cada {0} intento de inicio de sesión 47 | ycom_auth_config_login_tries_always_pause = El usuario está bloqueado durante {1} minutos después de {0} intentos de inicio de sesión 48 | 49 | perm_options_ycomArticlePermissions[] = YCom - Derechos de acceso a la página 50 | 51 | no_permission_to_edit = No permiso para editar 52 | 53 | ycom_validate_password_policy_rules_error = Observe las reglas estándar de al menos 1 letra grande y pequeña, al menos 8 caracteres 54 | ycom_validate_password_policy_rules = Reglas de contraseña como JSON 55 | ycom_validate_password_policy_rules_error_message = Mensaje de error 56 | 57 | ycom_validate_password_policy_rules_script = Activar contraseña creador 58 | 59 | ycom_auth_config_pages = Páginas generales 60 | ycom_auth_config_id_login = página de login 61 | ycom_auth_config_id_logout = Página de cierre de sesión 62 | ycom_auth_config_id_register = Regístrese página 63 | ycom_auth_config_id_password = Restablecer contraseña 64 | 65 | ycom_auth_config_auth_cookie_ttl = Duración de la cookie para `permanecer conectado` 66 | ycom_days = {0} días 67 | 68 | ycom_auth_config_extern = Autenticación (SAML/CAS/OAUTH2) 69 | -------------------------------------------------------------------------------- /plugins/auth/lang/en_gb.lang: -------------------------------------------------------------------------------- 1 | ycom_auth_title = Authentication 2 | 3 | ycom_auth_settings = Settings 4 | ycom_auth_description = Explanation 5 | 6 | ycom_auth_config = Configuration 7 | ycom_auth_config_save = Save configuration 8 | ycom_auth_config_status = Status 9 | ycom_auth_config_security = Security 10 | ycom_auth_config_forwarder = Forward to article 11 | ycom_auth_config_login_field = Login field 12 | 13 | ycom_auth_config_status_authactive = Authentication active 14 | ycom_auth_config_status_stayactive = "Remain signed in" active 15 | ycom_auth_config_security_hashedpasswd = Encrypted passwords (sha1) 16 | ycom_auth_config_id_jump_ok = ... when successfully signed in 17 | ycom_auth_config_id_jump_not_ok = ... when sign in is unsuccessfull 18 | ycom_auth_config_id_jump_logout = ... when signed out 19 | ycom_auth_config_id_jump_denied = ... when attempting to access a restricted article 20 | ycom_auth_config_id_jump_denied_notice = When not already signed in visitors will be redirected to the sign in page. Afterwards visitors will be redirected to the access denied article if they have insufficient permissions to view this article. 21 | 22 | ycom_auth_config_id_jump_activation_ok = ... when successfully activated 23 | ycom_auth_config_id_jump_activation_fail = ... when activation was unsuccessfull 24 | ycom_auth_config_id_jump_newpassword_ok = ... when successfully reset password 25 | ycom_auth_config_id_jump_newpassword_fail = ... when password reset was unsuccessfull 26 | ycom_auth_config_id_jump_password = ... if setting a new password (new_password_required field) [optional] 27 | ycom_auth_config_id_jump_termsofuse = ... if terms of use have not been confirmed (termsofuse_accepted field not set) [optional] 28 | 29 | ycom_auth_settings_updated = Configuration has been saved 30 | 31 | ycom_auth_info_title = Explanation 32 | 33 | ycom_auth_perm = Permissions 34 | ycom_auth_update_perm = Update 35 | ycom_auth_perm_updated = Permissions updated 36 | 37 | ycom_auth_password_not_updated = Password was not updated 38 | ycom_auth_password_updated = Password was updated 39 | ycom_auth_password_exists = Password exists 40 | ycom_auth_password_isempty = Password does not exist 41 | 42 | ycom_auth_config_auth_rules = Rules for authentication 43 | ycom_auth_config_login_tries_deactivateafter = User will be deactived after {0} login-tries 44 | ycom_auth_config_login_tries = Maximum login tries 45 | ycom_auth_config_login_tries_unlimited = Unlimited tries 46 | ycom_auth_config_login_tries_pause = User will be blocked for {1} minutes after each {0} login attempts 47 | ycom_auth_config_login_tries_always_pause = User will be blocked for {1} minutes after {0} login attempts 48 | 49 | perm_options_ycomArticlePermissions[] = YCom - Page access rights 50 | 51 | no_permission_to_edit = No permission to edit 52 | 53 | ycom_validate_password_policy_rules_error = Please adhere to standard rules, at least 1 upper case and lower case letter, at least 8 characters 54 | ycom_validate_password_policy_rules = Password rules in JSON 55 | ycom_validate_password_policy_rules_error_message = Error message 56 | 57 | ycom_validate_password_policy_rules_script = Activate PasswordCreator 58 | 59 | ycom_auth_config_pages = General pages 60 | ycom_auth_config_id_login = Login page 61 | ycom_auth_config_id_logout = Logout page 62 | ycom_auth_config_id_register = Register page 63 | ycom_auth_config_id_password = Reset password 64 | 65 | ycom_auth_config_auth_cookie_ttl = Cookie lifespan for "remain signed in" 66 | ycom_days = {0} days 67 | 68 | ycom_auth_config_session_max_overall_duration = Session duration overall in seconds 69 | ycom_auth_config_session_duration = Session duration when inactive in seconds 70 | 71 | ycom_auth_config_extern = authentication (SAML/CAS/OAUTH2) 72 | 73 | ycom_auth_howtoread_configvalues = Values can be read via: rex_ycom_config::get('article_id_login', null); 74 | -------------------------------------------------------------------------------- /docs/06_user_groups.md: -------------------------------------------------------------------------------- 1 | # User und Gruppen auslesen 2 | 3 | Da YCom auf YForm basiert, können auf Details von YCom-Nutzern und Gruppen auf Basis von `rex_sql` oder `YOrm` ausgelesen werden. 4 | 5 | ## YOrm-Variante (empfohlen) 6 | 7 | Siehe auch 8 | * YForm-Dokumentation, Kapitel "YOrm": https://github.com/yakamara/redaxo_yform_docs/blob/master/de_de/yorm.md 9 | 10 | **Details des YCom-Nutzers auslesen** 11 | 12 | ```php 13 | getValue('login'); // Benutzer-Login 21 | echo $ycom_user->getValue('firstname'); // Vorname 22 | echo $ycom_user->getValue('name'); // Nachname 23 | echo $ycom_user->getValue('last_action_time'); // Letzte Aktion auf der Seite 24 | echo $ycom_user->getValue('last_login_time'); // Letzter Login 25 | echo $ycom_user->getValue('ycom_groups'); // IDs der zugeordneten Gruppen 26 | } 27 | 28 | ?> 29 | ``` 30 | 31 | > Hinweis: Die Felder lassen sich über den Table Manager in YForm um eigene Felder erweitern. 32 | 33 | **Details zur Gruppe eines YCom-Nutzers auslesen** 34 | 35 | ```php 36 | getRelatedCollection('ycom_groups'); 42 | foreach($ycom_groups as $ycom_group) { 43 | $ycom_group->getValue("name"); // Name der Gruppe 44 | dump($ycom_group); 45 | } 46 | } 47 | 48 | ?> 49 | ``` 50 | **Prüfen ob YCom-Nutzer in einer speziellen Gruppe ist** 51 | 52 | ```php 53 | isInGroup($group_id)) { 60 | // User ist in der Gruppe 61 | } else { 62 | // User ist nicht in der Gruppe 63 | }; 64 | } 65 | 66 | ?> 67 | ``` 68 | 69 | > Hinweis: Die Felder lassen sich über den Table Manager in YForm um eigene Felder erweitern. 70 | 71 | **Alle YCom-Gruppen auslesen** 72 | 73 | ```php 74 | $ycom_group_name) { 78 | dump($ycom_group_id); // ID der Gruppe 79 | dump($ycom_group_name); // Name der Gruppe 80 | } 81 | } 82 | 83 | ?> 84 | ``` 85 | 86 | 87 | **Alle Nutzer einer YCom-Gruppe** 88 | 89 | ```php 90 | getRelatedCollection('ycom_groups'); 93 | 94 | foreach($ycom_users as $ycom_user) { 95 | # dump($ycom_user); 96 | echo $ycom_user->getValue('firstname'); // Vorname 97 | echo $ycom_user->getValue('name'); // Nachname 98 | } 99 | } 100 | 101 | ?> 102 | ``` 103 | 104 | **Berechtigung eines Users an einer Kategorie an einem Artikel auslesen.:** 105 | 106 | ```php 107 | true oder false 111 | 112 | ?> 113 | ``` 114 | Weitere Methoden der rex_ycom_auth-Klasse: https://github.com/yakamara/redaxo_ycom/blob/master/plugins/auth/lib/ycom_auth.php 115 | 116 | ## SQL-Variante 117 | 118 | Siehe auch: 119 | * REDAXO API-Dokumentation: https://redaxo.org/api/master/class-rex_sql.html 120 | * Datenbank-Queries in REDAXO 5: https://redaxo.org/doku/master/datenbank-queries 121 | 122 | ```php 123 | getArray('SELECT id, name FROM rex_ycom_user WHERE id = :id', [$ycom_user->getId()]); 129 | # dump($user); // auskommentieren, um alle Schlüssel und Werte des Arrays auszugeben 130 | } 131 | 132 | ?> 133 | ``` 134 | -------------------------------------------------------------------------------- /plugins/auth/pages/tokens.php: -------------------------------------------------------------------------------- 1 | i18n('ycom_title')); 6 | 7 | rex_ycom_user_token::clearExpiredTokens(); 8 | 9 | $func = rex_request('func', 'string', ''); 10 | switch ($func) { 11 | case 'ycom_user_delete_tokens': 12 | $amount = (int) rex_ycom_user_token::deleteAllTokens(); 13 | echo rex_view::success($this->i18n('tokens_deleted', $amount)); 14 | break; 15 | case 'remove_token': 16 | $hash = (string) rex_request::get('hash', 'string', ''); 17 | rex_ycom_user_token::removeTokenByHash($hash); 18 | echo rex_view::success($this->i18n('token_removed')); 19 | break; 20 | } 21 | 22 | echo rex_view::warning(rex_i18n::rawMsg('ycom_user_delete_all_tokens_link', rex_url::currentBackendPage() . '&func=ycom_user_delete_tokens')); 23 | 24 | $list = rex_list::factory('SELECT * from ' . rex::getTable('ycom_user_token') . ' ORDER BY createdate DESC'); 25 | 26 | $list->addColumn('remove_token', '', 0, ['', '###VALUE###']); 27 | $list->setColumnParams('remove_token', ['func' => 'remove_token', 'hash' => '###hash###']); 28 | 29 | $list->setColumnLabel('user_id', rex_i18n::msg('ycom_user_id')); 30 | 31 | $list->setColumnLabel('token', rex_i18n::msg('ycom_token')); 32 | $list->setColumnLabel('hash', rex_i18n::msg('ycom_hash')); 33 | $list->setColumnLabel('type', rex_i18n::msg('ycom_token_type')); 34 | 35 | $list->setColumnLabel('createdate', rex_i18n::msg('createdate')); 36 | $list->setColumnLabel('expiredate', rex_i18n::msg('ycom_expiredate')); 37 | 38 | $list->removeColumn('selector'); 39 | 40 | $list->setColumnFormat('hash', 'custom', static function () use ($list) { 41 | return '' . rex_escape(rex_formatter::truncate($list->getValue('hash'), [ 42 | 'length' => 30, 43 | ])) . ''; 44 | }); 45 | 46 | $list->setColumnFormat('expiredate', 'custom', static function () use ($list) { 47 | return rex_formatter::intlDateTime((string) $list->getValue('expiredate'), IntlDateFormatter::SHORT); 48 | }); 49 | $list->setColumnFormat('createdate', 'custom', static function () use ($list) { 50 | return rex_formatter::intlDateTime((string) $list->getValue('createdate'), IntlDateFormatter::SHORT); 51 | }); 52 | 53 | $list->setColumnSortable('user_id'); 54 | $list->setColumnSortable('createdate'); 55 | $list->setColumnSortable('expiredate'); 56 | 57 | $content = $list->get(); 58 | 59 | $fragment = new rex_fragment(); 60 | $fragment->setVar('title', rex_i18n::msg('ycom_token_caption')); 61 | $fragment->setVar('content', $content, false); 62 | echo $fragment->parse('core/page/section.php'); 63 | 64 | // generate_key|activation_key 65 | // 66 | // text|email|E-Mail:| 67 | // 68 | // validate|type|email|email|Bitte geben Sie Ihre E-Mail-Adresse ein. 69 | // validate|empty|email|Bitte geben Sie Ihre E-Mail-Adresse ein. 70 | // validate|in_table|email|rex_ycom_user|email|Für die angegebene E-Mail-Adresse existiert kein Nutzer.| 71 | // 72 | // action|db_query|update rex_ycom_user set activation_key = ? where email = ?|activation_key,email 73 | // action|tpl2email|resetpassword_de|email| 74 | // 75 | // action|showtext|Sie erhalten eine E-Mail mit einem Link, über den Sie das Passwort zurücksetzen können.|

|

|1 76 | 77 | // Brauche User_ID, HASH 78 | // Passwort zurücksetzen, Register, email_change, Newsletter? 79 | // Ans Templatesystem andocken und eigene Kennung bauen REX_YCOM[tokenlink] 80 | 81 | // ycom_token|password_reset 82 | 83 | // UserToken=[Token][NonDBToken][id] 84 | // Tokenvalidierung 85 | // Form ausführbar, wenn Token valide und danach, wenn Formular ausgeführt wurde, Token löschen 86 | // ycom_token_validate (request und hidden value wenn abgeschickt) 87 | 88 | // REX_YCOM[id, token] 89 | 90 | // Passwort vergessen / email + id 91 | // Registrierung / email + id 92 | // E-Mail-Änderung / email + id 93 | -------------------------------------------------------------------------------- /lib/ycom_log.php: -------------------------------------------------------------------------------- 1 | isAvailable()) { 27 | $addon->setConfig('log', 1); 28 | self::$active = true; 29 | } 30 | } 31 | 32 | public static function deactivate(): void 33 | { 34 | $addon = rex_addon::get('ycom'); 35 | if ($addon->isAvailable()) { 36 | $addon->setConfig('log', 0); 37 | self::$active = false; 38 | } 39 | } 40 | 41 | public static function isActive(): bool 42 | { 43 | if (null === self::$active) { 44 | $addon = rex_addon::get('ycom'); 45 | if ($addon->isAvailable()) { 46 | self::$active = (1 === $addon->getConfig('log')) ? true : false; 47 | } else { 48 | self::$active = false; 49 | } 50 | } 51 | return (self::$active) ? true : false; 52 | } 53 | 54 | public static function logFolder(): string 55 | { 56 | return rex_path::addonData('ycom', 'log'); 57 | } 58 | 59 | public static function logFile(): string 60 | { 61 | return rex_path::log('ycom_user.log'); 62 | } 63 | 64 | public static function delete(): bool 65 | { 66 | return rex_log_file::delete(self::logFile()); 67 | } 68 | 69 | /** 70 | * @param rex_ycom_user|string|rex_yform_manager_dataset $user 71 | * @param array> $params 72 | */ 73 | public static function log($user, string $type = '', array $params = []): void 74 | { 75 | if (!self::isActive()) { 76 | return; 77 | } 78 | 79 | $ip = $_SERVER['REMOTE_ADDR'] ?? ''; 80 | if (isset($_SERVER['HTTP_CLIENT_IP'])) { 81 | $ip = (string) $_SERVER['HTTP_CLIENT_IP']; 82 | } elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { 83 | $ip = (string) $_SERVER['HTTP_X_FORWARDED_FOR']; 84 | } 85 | 86 | $id = ''; 87 | $login = ''; 88 | if (is_string($user)) { 89 | /** @var string $user */ 90 | $id = ''; 91 | $login = $user; 92 | } elseif ('rex_ycom_user' == $user::class) { 93 | /** @var rex_ycom_user $user */ 94 | $id = $user->getId(); 95 | $login = $user->getValue('login'); 96 | } 97 | 98 | $log = rex_log_file::factory(self::logFile(), self::$maxFileSize); 99 | $data = [ 100 | $ip, 101 | $id, 102 | $login, 103 | $type, 104 | (string) json_encode($params), 105 | ]; 106 | $log->add($data); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /plugins/auth/pages/system.log.ycom_user.php: -------------------------------------------------------------------------------- 1 | i18n('ycom_user_log_activated')); 12 | rex_ycom_log::activate(); 13 | break; 14 | case 'ycom_user_deactivate_log': 15 | echo rex_view::success($addon->i18n('ycom_user_log_deactivated')); 16 | rex_ycom_log::deactivate(); 17 | break; 18 | case 'ycom_user_delete_log': 19 | if (rex_ycom_log::delete()) { 20 | echo rex_view::success($addon->i18n('syslog_deleted')); 21 | } else { 22 | echo rex_view::error($addon->i18n('syslog_delete_error')); 23 | } 24 | } 25 | 26 | if (!rex_ycom_log::isActive()) { 27 | echo rex_view::warning(rex_i18n::rawMsg('ycom_user_log_warning_logisinactive', $activationLink)); 28 | } else { 29 | echo rex_view::warning(rex_i18n::rawMsg('ycom_user_log_warning_logisactive', $deactivationLink)); 30 | } 31 | 32 | $content = ' 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | '; 45 | 46 | $file = rex_log_file::factory($logFile); 47 | foreach (new LimitIterator($file, 0, 30) as $entry) { 48 | $data = $entry->getData(); 49 | $class = 'ERROR' == trim($data[0]) ? 'rex-state-error' : 'rex-mailer-log-ok'; 50 | $content .= ' 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | '; 59 | } 60 | 61 | $content .= ' 62 | 63 |
' . rex_i18n::msg('ycom_user_log_time') . '' . rex_i18n::msg('ycom_user_log_ip') . '' . rex_i18n::msg('ycom_user_log_user_id') . '' . rex_i18n::msg('ycom_auth_config_login_field') . '' . rex_i18n::msg('ycom_user_log_type') . '' . rex_i18n::msg('ycom_user_log_params') . '
' . rex_formatter::intlDateTime($entry->getTimestamp(), [IntlDateFormatter::SHORT, IntlDateFormatter::MEDIUM]) . '' . rex_escape($data[0]) . '' . rex_escape($data[1]) . '' . rex_escape($data[2]) . '' . rex_escape($data[3]) . '' . rex_escape($data[4] ?? '') . '
'; 64 | 65 | $formElements = []; 66 | $n = []; 67 | $n['field'] = ''; 68 | $formElements[] = $n; 69 | 70 | $fragment = new rex_fragment(); 71 | $fragment->setVar('elements', $formElements, false); 72 | $buttons = $fragment->parse('core/form/submit.php'); 73 | 74 | $fragment = new rex_fragment(); 75 | $fragment->setVar('title', rex_i18n::msg('ycom_user_log_title', $logFile), false); 76 | $fragment->setVar('content', $content, false); 77 | $fragment->setVar('buttons', $buttons, false); 78 | $content = $fragment->parse('core/page/section.php'); 79 | $content = ' 80 |
81 | 82 | ' . $content . ' 83 |
'; 84 | 85 | echo $content; 86 | -------------------------------------------------------------------------------- /plugins/auth/lib/ycom_auth_rules.php: -------------------------------------------------------------------------------- 1 | */ 6 | private array $rules; 7 | 8 | public function __construct() 9 | { 10 | $this->rules = []; 11 | $this->rules['login_try_unlimited'] = [ 12 | 'info' => rex_i18n::msg('ycom_auth_config_login_tries_unlimited'), 13 | 'trigger' => 'none', 14 | ]; 15 | $this->rules['login_try_5_deactivate'] = [ 16 | 'info' => rex_i18n::msg('ycom_auth_config_login_tries_deactivateafter', 5), 17 | 'trigger' => 'min', 18 | 'tries' => 5, 19 | 'action' => ['type' => 'deactivate'], 20 | ]; 21 | $this->rules['login_try_10_deactivate'] = [ 22 | 'info' => rex_i18n::msg('ycom_auth_config_login_tries_deactivateafter', 10), 23 | 'trigger' => 'min', 24 | 'tries' => 10, 25 | 'action' => ['type' => 'deactivate'], 26 | ]; 27 | $this->rules['login_try_20_deactivate'] = [ 28 | 'info' => rex_i18n::msg('ycom_auth_config_login_tries_deactivateafter', 20), 29 | 'trigger' => 'min', 30 | 'tries' => 20, 31 | 'action' => ['type' => 'deactivate'], 32 | ]; 33 | $this->rules['login_try_5_pause'] = [ 34 | 'info' => rex_i18n::msg('ycom_auth_config_login_tries_pause', 5, 15), 35 | 'trigger' => 'interval', 36 | 'tries' => 5, 37 | 'action' => ['type' => 'pause', 'time' => (15 * 60)], // 15 min pause 38 | ]; 39 | $this->rules['login_try_10_pause'] = [ 40 | 'info' => rex_i18n::msg('ycom_auth_config_login_tries_pause', 10, 15), 41 | 'trigger' => 'interval', 42 | 'tries' => 10, 43 | 'action' => ['type' => 'pause', 'time' => (15 * 60)], // 15 min pause 44 | ]; 45 | $this->rules['login_try_10_always_pause'] = [ 46 | 'info' => rex_i18n::msg('ycom_auth_config_login_tries_always_pause', 10, 5), 47 | 'trigger' => 'min', 48 | 'tries' => 10, 49 | 'action' => ['type' => 'pause', 'time' => (5 * 60)], // 5 min pause 50 | ]; 51 | } 52 | 53 | /** 54 | * @throws rex_exception 55 | */ 56 | public function check(rex_ycom_user $user, string $rule_name = 'login_try_5_pause'): bool 57 | { 58 | if (!array_key_exists($rule_name, $this->rules)) { 59 | $rule_name = 'login_try_5_pause'; 60 | } 61 | 62 | $rule = $this->rules[$rule_name]; 63 | $loginTries = $user->getValue('login_tries'); 64 | 65 | switch ($rule['trigger']) { 66 | case 'none': 67 | return true; 68 | case 'min': 69 | if ($rule['tries'] > $loginTries) { 70 | return true; 71 | } 72 | break; 73 | case 'interval': 74 | if (0 == $loginTries || 0 != $loginTries % $rule['tries']) { 75 | return true; 76 | } 77 | break; 78 | default: 79 | throw new rex_exception(sprintf('Unknown auth_rule trigger key "%s".', $rule['trigger'])); 80 | } 81 | 82 | switch ($rule['action']['type']) { 83 | case 'deactivate': 84 | $user->increaseLoginTries(); 85 | $user->setValue('status', -2); // to much login failures 86 | $user->save(); 87 | return false; 88 | case 'pause': 89 | $lastLoginTryDate = new DateTime($user->getValue('last_login_try_time')); 90 | $lastLoginTryDate->modify('+' . $rule['action']['time'] . ' seconds'); 91 | if (date('YmdHis') < $lastLoginTryDate->format('YmdHis')) { 92 | return false; 93 | } 94 | return true; 95 | default: 96 | throw new rex_exception(sprintf('Unknown auth_rule action key "%s".', $rule['action'])); 97 | } 98 | } 99 | 100 | /** 101 | * @return array 102 | */ 103 | public function getOptions(): array 104 | { 105 | $options = []; 106 | 107 | foreach ($this->rules as $key => $rule) { 108 | $options[$key] = $rule['info']; 109 | } 110 | 111 | return $options; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /plugins/auth/lib/yform/value/ycom_auth_password.php: -------------------------------------------------------------------------------- 1 | getElement('rules')) ? $this->getElement('rules') : json_decode($this->getElement('rules'), true)); 8 | if (!$rules || 0 == count($rules)) { 9 | $rules = json_decode(rex_yform_validate_password_policy::PASSWORD_POLICY_DEFAULT_RULES, true); 10 | } 11 | 12 | if ('1' == $this->params['send']) { 13 | $PasswordPolicy = new rex_password_policy($rules); 14 | 15 | if ('' != $this->getValue() && true !== $msg = $PasswordPolicy->check($this->getValue())) { 16 | $this->params['warning'][$this->getId()] = $this->params['error_class']; 17 | $this->params['warning_messages'][$this->getId()] = '' == trim($this->getElement('message')) ? $msg : $this->getElement('message'); 18 | } 19 | } 20 | 21 | if ($this->needsOutput() && $this->isViewable()) { 22 | if (!$this->isEditable()) { 23 | $this->params['form_output'][$this->getId()] = $this->parse(['value.ycom_password-view.tpl.php', 'value.password-view.tpl.php', 'value.text-view.tpl.php', 'value.view.tpl.php', 'value.text.tpl.php'], ['value' => '***********']); 24 | } else { 25 | $this->params['form_output'][$this->getId()] = $this->parse(['value.ycom_password.tpl.php', 'value.password.tpl.php', 'value.text.tpl.php'], ['type' => 'password', 'value' => '', 'script' => $this->getElement('script'), 'rules' => $rules]); 26 | } 27 | } 28 | } 29 | 30 | public function preAction(): void 31 | { 32 | $password = null; 33 | $hashed_value = null; 34 | 35 | if (isset($this->params['sql_object'])) { 36 | $hashed_value = $this->params['sql_object']->getValue($this->getName()); 37 | 38 | if ('' == $this->getValue()) { // kein neuer wert 39 | } elseif ($hashed_value == $this->getValue()) { 40 | } else { 41 | $password = $this->getValue(); 42 | } 43 | } elseif ('' != $this->getValue()) { 44 | $password = $this->getValue(); 45 | } 46 | if ('' != $password) { 47 | $hash_info = password_get_info($password); 48 | if (!isset($hash_info['algoName']) || 'bcrypt' != $hash_info['algoName']) { 49 | $hashed_value = rex_login::passwordHash($password); 50 | $password = ''; // empty in email pool 51 | } else { 52 | $hashed_value = $password; 53 | } 54 | } 55 | 56 | if ($password) { 57 | $this->params['value_pool']['email'][$this->getName()] = $password; 58 | } 59 | if ($hashed_value) { 60 | $this->params['value_pool']['sql'][$this->getName()] = $hashed_value; 61 | } 62 | } 63 | 64 | public function getDescription(): string 65 | { 66 | return 'ycom_auth_password -> Beispiel: ycom_auth_password|name|label|[password-rules-as-json]|message|[script 0/1]'; 67 | } 68 | 69 | /** 70 | * @return array 71 | */ 72 | public function getDefinitions(): array 73 | { 74 | return [ 75 | 'type' => 'value', 76 | 'name' => 'ycom_auth_password', 77 | 'values' => [ 78 | 'name' => ['type' => 'name', 'label' => 'Feld'], 79 | 'label' => ['type' => 'text', 'label' => 'Bezeichnung'], 80 | 'rules' => ['type' => 'text', 'label' => rex_i18n::msg('ycom_validate_password_policy_rules'), 'notice' => rex_i18n::msg('yform_validate_password_policy_rules_notice', rex_yform_validate_password_policy::PASSWORD_POLICY_DEFAULT_RULES)], 81 | 'message' => ['type' => 'text', 'label' => rex_i18n::msg('ycom_validate_password_policy_rules_error_message')], 82 | 'script' => ['type' => 'checkbox', 'label' => rex_i18n::msg('ycom_validate_password_policy_rules_script')], 83 | 'attributes' => ['type' => 'text', 'label' => rex_i18n::msg('yform_values_defaults_attributes'), 'notice' => rex_i18n::msg('yform_values_defaults_attributes_notice')], 84 | ], 85 | 'description' => 'Erzeugt den Hash-Wert des Passwortes', 86 | 'dbtype' => 'varchar(255)', 87 | 'famous' => false, 88 | ]; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /docs/08_extern_auth.md: -------------------------------------------------------------------------------- 1 | # Externe Authentifizierungen 2 | 3 | ## SAML-Authentifizierung 4 | 5 | Mit der SAML-Authentifizierung kann man sich über einen externen Identityprovider in der YCOM registrieren und einloggen. 6 | Dazu muss dieser Provider inkl. Metadaten entsprechend vorbereitet sein. 7 | 8 | ### Einrichtung 9 | 10 | Bei der Installation des Auth-Plugins wurde eine saml.php in den Data-Ordner des YCom-AddOns gelegt. Diesen muss man entsprechend anpassen. Die Identityprovider information müssen dort eingerichtet sein. 11 | 12 | Damit die Authentifizierung funktioniert, muss im Loginformular von YCOM ein Feld erweitert werden und es in der saml.php ergänzt werden. 13 | 14 | ```php 15 | ycom_auth_saml|samllabel|error_msg|[allowed returnTo domains: DomainA,DomainB]|default Userdata as Json{"ycom_groups": 3, "termsofuse_accepted": 1} 16 | ``` 17 | 18 | Mit diesem Feld erweitert man das Login um einen Loginbutton, der zum entsprechenden Authentifizierung führt. Wenn man diesen in der Loginmaske anklickt, wird diese Authentifizierung gestartet und eventuelle fehlende oder falsche Informationen werden angezeigt. 19 | 20 | **error_msg** ist die entsprechende Fehlermeldung die auttaucht, wenn die SAML Authentifizierung fehlschlägt 21 | **allowed returnTo domains** Optional: sollte man returnTo verwenden um eine Weiterleitung zu einer bestimmten Seite zu bekommen, kann man diese Weiterleitung filter, so dass nicht unerlaubte Domains genutzt werden können 22 | **default Userdata as Json** Optional: Will man bestimmte Userdaten eines Users bei der Anmeldung über SAML festlegen, so kann man dies hier über JSON festlegen. Einfach die Feldnamen der Usertabelle dazu nutzen 23 | 24 | > **Technische Erläuterung:** Sobald vom User der SAML-Auth-Login-Button geklickt wurde, wird man anhand der saml.php-Settings zum Identity Provider geschickt, welche den User eigenständig authentifiziert und dem User einen Token mit entsprechenden Usermetadaten zuweist. 25 | 26 | > Diese werden von YCom genutzt und der User wird, wenn nicht vorhanden, angelegt und den eventuell individuell festgelegten Userattributen angelegt, oder die Daten werden aktualisiert. 27 | 28 | > Danach ist der User auch in YCom authentifiziert und eine weiteres Login ist nicht nötig. Sofern eine Session weiterhin vorhanden ist, wird dieser User nur bei Ablauf der Session oder nach einem Logout wieder über dem Identityprovider authentifiziert. 29 | 30 | Sofern die automatische E-Mail Erkennung nicht klappt, oder/und man eigene Felder bei der Usergenerierung festlegen will, folgendes am besten in die `addons/project/boot.php` legen und anpassen. 31 | 32 | ```php 33 | rex_extension::register('YCOM_AUTH_SAML_MATCHING', function (rex_extension_point $ep) { 34 | 35 | $data = $ep->getSubject(); 36 | $params = $ep->getParams(); 37 | $Userdata = $params['Userdata']; 38 | 39 | echo '

Hier auslesen welche Parameter übergeben werden und eventuell übernehmen/auswerten

'; 40 | echo '

Danach diesen Block löschen

'; 41 | echo '

Das hier ist bereits erkannt worden

'; 42 | dump($data); 43 | echo '

Das hier kam über die SAML Schnittstelle

'; 44 | dump($Userdata); 45 | 46 | exit; 47 | 48 | // z.B. 49 | $emailKey = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'; 50 | $givennameKey = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname'; 51 | $surnameKey = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname'; 52 | 53 | $data['email'] = @$Userdata[$emailKey][0]; 54 | 55 | // Userdatensatz mit gewünschten Einstellungen anreichern. 56 | $data['termsofuse_accepted'] = 1; 57 | $data['login_tries'] = 0; 58 | 59 | return $data; 60 | 61 | }); 62 | ``` 63 | 64 | ## CAS 65 | 66 | ## OAuth2 67 | 68 | ## Allgemeines 69 | 70 | ### Programmatisches Einloggen an der Authentifizierung vorbei 71 | 72 | Hat man einen eigenen Authentifizierungsmechanismus implementiert, so genügt es, den gewünschten YCom-User über dessen ID einzuloggen: 73 | 74 | ```php 75 | $user = rex_ycom_auth::loginWithParams(['login' => $user_id]) 76 | ``` 77 | 78 | ### Loginseite 79 | 80 | Sofern man die externe Authentifikation nicht nutzt, wird die Loginseite meistens so eingestellt, dass nur nicht eingeloggte User diese sehen können. Das ist hier nicht zu empfehlen, da man sich nicht einloggen kann, wenn man über den IdentityProvider zur REDAXO Community kommt und bereits eingeloggt ist. Deswegen sollte die Loginseite verfühgbar, aber nicht sichtbar in der Navigation sein, wenn man eingeloggt ist. 81 | 82 | -------------------------------------------------------------------------------- /plugins/auth/install.php: -------------------------------------------------------------------------------- 1 | hasColumn('ycom_auth_type')) { 12 | $Column = $articleTable->getColumn('ycom_auth_type'); 13 | if (null !== $Column && 'enum' === substr($Column->getType(), 0, 4)) { 14 | $articleAuthTypeWasEnum = true; 15 | } 16 | } 17 | 18 | rex_sql_table::get(rex::getTable('article')) 19 | ->ensureColumn(new rex_sql_column('ycom_auth_type', 'int', false, '0')) 20 | ->alter(); 21 | 22 | rex_sql_table::get(rex::getTable('ycom_user_token')) 23 | ->ensureColumn(new rex_sql_column('hash', 'varchar(255)')) 24 | ->ensureColumn(new rex_sql_column('user_id', 'int(10) unsigned', true)) 25 | ->ensureColumn(new rex_sql_column('email', 'varchar(255)')) 26 | ->ensureColumn(new rex_sql_column('type', 'varchar(255)')) 27 | ->ensureColumn(new rex_sql_column('selector', 'varchar(255)')) 28 | ->ensureColumn(new rex_sql_column('createdate', 'datetime')) 29 | ->ensureColumn(new rex_sql_column('expiredate', 'datetime')) 30 | ->setPrimaryKey('hash') 31 | ->ensureIndex(new rex_sql_index('token', ['selector'], rex_sql_index::UNIQUE)) 32 | ->ensureForeignKey( 33 | new rex_sql_foreign_key( 34 | 'ycom_user_token_id', 35 | rex::getTable('ycom_user'), 36 | ['user_id' => 'id'], 37 | rex_sql_foreign_key::CASCADE, 38 | rex_sql_foreign_key::CASCADE, 39 | ), 40 | ) 41 | ->ensure(); 42 | 43 | rex_sql_table::get(rex::getTable('ycom_user_session')) 44 | ->ensureColumn(new rex_sql_column('session_id', 'varchar(255)')) 45 | ->ensureColumn(new rex_sql_column('user_id', 'int(10) unsigned')) 46 | ->ensureColumn(new rex_sql_column('ip', 'varchar(39)')) // max for ipv6 47 | ->ensureColumn(new rex_sql_column('useragent', 'varchar(255)')) 48 | ->ensureColumn(new rex_sql_column('starttime', 'datetime')) 49 | ->ensureColumn(new rex_sql_column('last_activity', 'datetime')) 50 | ->ensureColumn(new rex_sql_column('last_activity', 'datetime')) 51 | ->ensureColumn(new rex_sql_column('otp_verified', 'tinyint(1)', false, '0')) 52 | ->ensureColumn(new rex_sql_column('cookie_key', 'varchar(255)', true)) 53 | ->ensureIndex(new rex_sql_index('cookie_key', ['cookie_key'], rex_sql_index::UNIQUE)) 54 | ->setPrimaryKey('session_id') 55 | ->ensureForeignKey( 56 | new rex_sql_foreign_key( 57 | 'ycom_user_session_id', 58 | rex::getTable('ycom_user'), 59 | ['user_id' => 'id'], 60 | rex_sql_foreign_key::CASCADE, 61 | rex_sql_foreign_key::CASCADE, 62 | ), 63 | ) 64 | ->ensure(); 65 | 66 | // Update from Version < 4 67 | if ($articleAuthTypeWasEnum) { 68 | rex_sql::factory()->setQuery('UPDATE rex_article SET `ycom_auth_type` = `ycom_auth_type` -1'); 69 | } 70 | 71 | foreach (['saml', 'oauth2', 'cas'] as $settingType) { 72 | $pathFrom = __DIR__ . '/install/' . $settingType . '.php'; 73 | $pathTo = rex_addon::get('ycom')->getDataPath($settingType . '.php'); 74 | if (!file_exists($pathTo)) { 75 | rex_file::copy($pathFrom, $pathTo); 76 | } 77 | } 78 | 79 | // termofuse -> termsofuse. Version < 3.0 80 | try { 81 | rex_sql_table::get(rex::getTablePrefix() . 'ycom_user') 82 | ->ensureColumn(new rex_sql_column('termsofuse_accepted', 'tinyint(1)', false, '0')) 83 | ->removeColumn('session_key') 84 | ->alter(); 85 | 86 | rex_sql::factory() 87 | // ->setQuery('alter table `' . rex::getTablePrefix().'ycom_user' . '` drop if exists `termofuse_accepted`', []) 88 | ->setQuery('delete from `' . rex_yform_manager_field::table() . '` where `table_name`="rex_ycom_user" and `type_id`="value" and `type_name`="checkbox" and `name`="termofuse_accepted"', []) 89 | ->setQuery('UPDATE `rex_config` AS target LEFT JOIN `rex_config` AS existing ON existing.key = "article_id_jump_termsofuse" AND existing.namespace = "ycom/auth" SET target.key = "article_id_jump_termsofuse" WHERE target.key = "article_id_jump_termofuse" AND target.namespace = "ycom/auth" AND existing.key IS NULL', []) 90 | ->setQuery('delete from `' . rex_yform_manager_field::table() . '` where `table_name`="rex_ycom_user" and `type_id`="value" and `type_name`="generate_key" and `name`="session_key"'); 91 | } catch (rex_sql_exception $e) { 92 | dump($e); 93 | exit; 94 | } 95 | -------------------------------------------------------------------------------- /.github/workflows/rexstan.yml: -------------------------------------------------------------------------------- 1 | name: rexstan 2 | 3 | on: 4 | push: 5 | branches: [ master, main ] 6 | pull_request: 7 | branches: [ master, main ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | rexstan: 14 | env: 15 | ADDON_KEY: ${{ github.event.repository.name }} 16 | 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: write # for Git to git apply 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | 24 | # setup PHP v8, install some extensions 25 | - name: Setup PHP 26 | uses: shivammathur/setup-php@v2 27 | with: 28 | php-version: '8.2' 29 | extensions: gd, intl, pdo_mysql 30 | coverage: none # disable xdebug, pcov 31 | 32 | # download the latest REDAXO release and unzip it 33 | # credits https://blog.markvincze.com/download-artifacts-from-a-latest-github-release-in-sh-and-powershell/ 34 | - name: Download latest REDAXO release 35 | run: | 36 | LATEST_RELEASE=$(curl -L -s -H 'Accept: application/json' https://github.com/redaxo/redaxo/releases/latest) 37 | REDAXO_VERSION=$(echo $LATEST_RELEASE | sed -e 's/.*"tag_name":"\([^"]*\)".*/\1/') 38 | echo "Downloaded REDAXO $REDAXO_VERSION" 39 | curl -Ls -o redaxo.zip https://github.com/redaxo/redaxo/releases/download/$REDAXO_VERSION/redaxo_$REDAXO_VERSION.zip 40 | unzip -oq redaxo.zip -d redaxo_cms 41 | rm redaxo.zip 42 | 43 | # start mysql service, create a database called redaxo5, apply config patch 44 | - name: Init database 45 | run: | 46 | sudo /etc/init.d/mysql start 47 | mysql -uroot -h127.0.0.1 -proot -e 'create database redaxo5;' 48 | 49 | # run REDAXO setup with the following parameters 50 | # Language: de 51 | # DB password: root 52 | # Create DB: no 53 | # Admin username: admin 54 | # Admin password: adminpassword 55 | # Error E-mail: test@redaxo.invalid 56 | - name: Setup REDAXO 57 | run: | 58 | php redaxo_cms/redaxo/bin/console setup:run -n --lang=de_de --agree-license --db-host=127.0.0.1 --db-name=redaxo5 --db-password=root --db-createdb=no --db-setup=normal --admin-username=admin --admin-password=adminpassword --error-email=test@redaxo.invalid --ansi 59 | php redaxo_cms/redaxo/bin/console config:set --type boolean debug.enabled true 60 | php redaxo_cms/redaxo/bin/console config:set --type boolean debug.throw_always_exception true 61 | 62 | # copy Addon files, ignore some directories... 63 | # install the addon 64 | # if the addon name does not match the repository name, ${{ github.event.repository.name }} must be replaced with the addon name 65 | # install latest rexstan 66 | # if additional addons are needed, they can be installed via the console commands 67 | # see: https://www.redaxo.org/doku/main/basis-addons#console 68 | - name: Copy and install Addons 69 | run: | 70 | rsync -av --exclude='./vendor' --exclude='.github' --exclude='.git' --exclude='redaxo_cms' './' 'redaxo_cms/redaxo/src/addons/${{ github.event.repository.name }}' 71 | redaxo_cms/redaxo/bin/console install:download 'rexstan' '1.*' 72 | redaxo_cms/redaxo/bin/console package:install 'rexstan' 73 | redaxo_cms/redaxo/bin/console package:install 'cronjob' 74 | redaxo_cms/redaxo/bin/console package:install 'phpmailer' 75 | redaxo_cms/redaxo/bin/console install:download 'yform' '4.*' 76 | redaxo_cms/redaxo/bin/console package:install 'yform' 77 | redaxo_cms/redaxo/bin/console install:download 'yrewrite' '2.*' 78 | redaxo_cms/redaxo/bin/console package:install 'yrewrite' 79 | redaxo_cms/redaxo/bin/console package:install '${{ github.event.repository.name }}' 80 | 81 | # install dependencies from composer.json 82 | - name: Install test dependencies 83 | working-directory: redaxo_cms/redaxo/src/addons/${{ github.event.repository.name }} 84 | env: 85 | COMPOSER: composer.json 86 | run: composer install --prefer-dist --no-progress 87 | 88 | # execute rexstan.php to create the needed user-config.neon 89 | - name: Execute .tools/rexstan.php 90 | run: php -f redaxo/src/addons/${{ github.event.repository.name }}/.tools/rexstan.php 91 | working-directory: redaxo_cms 92 | 93 | # run rexstan 94 | - id: rexstan 95 | name: Run rexstan 96 | run: redaxo_cms/redaxo/bin/console rexstan:analyze 97 | -------------------------------------------------------------------------------- /plugins/auth/lib/otp/password_config.php: -------------------------------------------------------------------------------- 1 | user = $user; 20 | } 21 | 22 | public static function forCurrentUser(): self 23 | { 24 | $user = rex_ycom_auth::getUser(); 25 | return self::forUser($user); 26 | } 27 | 28 | public static function forUser(rex_ycom_user $user): self 29 | { 30 | return self::fromJson($user->getValue('otp_config'), $user); 31 | } 32 | 33 | public static function loadFromDb(rex_ycom_otp_method_interface $method, rex_ycom_user $user): self 34 | { 35 | // get non-cached values 36 | $userSql = rex_sql::factory(); 37 | $userSql->setTable(rex::getTablePrefix() . 'ycom_user'); 38 | $userSql->setWhere(['id' => $user->getId()]); 39 | $userSql->select(); 40 | 41 | $json = (string) $userSql->getValue('otp_config'); 42 | $config = self::fromJson($json, $user); 43 | $config->method = $method instanceof rex_ycom_otp_method_email ? 'email' : 'totp'; 44 | if (null === $config->getProvisioningUri()) { 45 | $config->setProvisioningUri($method->getProvisioningUri($user)); 46 | } 47 | return $config; 48 | } 49 | 50 | private static function fromJson(?string $json, rex_ycom_user $user): self 51 | { 52 | if (is_string($json)) { 53 | $configArr = json_decode($json, true); 54 | 55 | if (is_array($configArr)) { 56 | // compat with older versions, which did not yet define a method 57 | if (!array_key_exists('method', $configArr)) { 58 | $configArr['method'] = 'totp'; 59 | } 60 | 61 | $config = new self($user); 62 | $config->provisioningUri = $configArr['provisioningUri']; 63 | $config->enabled = $configArr['enabled']; 64 | $config->method = $configArr['method']; 65 | return $config; 66 | } 67 | } 68 | 69 | $method = new rex_ycom_otp_method_totp(); 70 | 71 | $default = new self($user); 72 | $default->method = $method instanceof rex_ycom_otp_method_email ? 'email' : 'totp'; 73 | $default->provisioningUri = $method->getProvisioningUri($user); 74 | 75 | return $default; 76 | } 77 | 78 | public function isEnabled(): bool 79 | { 80 | return $this->enabled ? true : false; 81 | } 82 | 83 | public function enable(): self 84 | { 85 | $this->enabled = true; 86 | return $this; 87 | } 88 | 89 | public function disable(): self 90 | { 91 | $this->enabled = false; 92 | $this->provisioningUri = null; 93 | return $this; 94 | } 95 | 96 | public function updateMethod(rex_ycom_otp_method_interface $method): self 97 | { 98 | $this->method = $method instanceof rex_ycom_otp_method_email ? 'email' : 'totp'; 99 | $this->provisioningUri = $method->getProvisioningUri($this->user); 100 | return $this; 101 | } 102 | 103 | public function getProvisioningUri() 104 | { 105 | return $this->provisioningUri; 106 | } 107 | 108 | public function setProvisioningUri($provisioningUri): self 109 | { 110 | $this->provisioningUri = $provisioningUri; 111 | return $this; 112 | } 113 | 114 | public function getMethod() 115 | { 116 | return $this->method; 117 | } 118 | 119 | public function save(): void 120 | { 121 | echo '
';
122 |         debug_print_backtrace();
123 |         echo '
'; 124 | 125 | $userSql = rex_sql::factory(); 126 | $userSql->setTable(rex::getTablePrefix() . 'ycom_user'); 127 | $userSql->setWhere(['id' => $this->user->getId()]); 128 | $userSql->setValue('otp_config', json_encode( 129 | [ 130 | 'provisioningUri' => $this->provisioningUri, 131 | 'method' => $this->method, 132 | 'enabled' => $this->enabled, 133 | ], 134 | )); 135 | $userSql->update(); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /.github/workflows/phpunit.yml: -------------------------------------------------------------------------------- 1 | name: PHPUnit 2 | 3 | on: 4 | push: 5 | branches: [ master, main ] 6 | pull_request: 7 | branches: [ master, main ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | phpunit: 14 | 15 | runs-on: ubuntu-latest 16 | permissions: 17 | contents: write # for Git to git apply 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | 22 | # setup PHP v8, install some extensions 23 | - name: Setup PHP 24 | uses: shivammathur/setup-php@v2 25 | with: 26 | php-version: '8.2' 27 | extensions: gd, intl, pdo_mysql 28 | coverage: none # disable xdebug, pcov 29 | 30 | # download the latest REDAXO release and unzip it 31 | # credits https://blog.markvincze.com/download-artifacts-from-a-latest-github-release-in-sh-and-powershell/ 32 | - name: Download latest REDAXO release 33 | run: | 34 | LATEST_RELEASE=$(curl -L -s -H 'Accept: application/json' https://github.com/redaxo/redaxo/releases/latest) 35 | REDAXO_VERSION=$(echo $LATEST_RELEASE | sed -e 's/.*"tag_name":"\([^"]*\)".*/\1/') 36 | echo "Downloaded REDAXO $REDAXO_VERSION" 37 | curl -Ls -o redaxo.zip https://github.com/redaxo/redaxo/releases/download/$REDAXO_VERSION/redaxo_$REDAXO_VERSION.zip 38 | unzip -oq redaxo.zip -d redaxo_cms 39 | rm redaxo.zip 40 | 41 | # start mysql service, create a database called redaxo5, apply config patch 42 | - name: Init database 43 | run: | 44 | sudo /etc/init.d/mysql start 45 | mysql -uroot -h127.0.0.1 -proot -e 'create database redaxo5;' 46 | 47 | # run REDAXO setup with the following parameters 48 | # Language: de 49 | # DB password: root 50 | # Create DB: no 51 | # Admin username: admin 52 | # Admin password: adminpassword 53 | # Error E-mail: test@redaxo.invalid 54 | - name: Setup REDAXO 55 | run: | 56 | php redaxo_cms/redaxo/bin/console setup:run -n --lang=de_de --agree-license --db-host=127.0.0.1 --db-name=redaxo5 --db-password=root --db-createdb=no --db-setup=normal --admin-username=admin --admin-password=adminpassword --error-email=test@redaxo.invalid --ansi 57 | php redaxo_cms/redaxo/bin/console config:set --type boolean debug.enabled true 58 | php redaxo_cms/redaxo/bin/console config:set --type boolean debug.throw_always_exception true 59 | 60 | # copy Addon files, ignore some directories... 61 | # install the addon 62 | # if the addon name does not match the repository name, ${{ github.event.repository.name }} must be replaced with the addon name 63 | # if additional addons are needed, they can be installed via the console commands 64 | # see: https://www.redaxo.org/doku/main/basis-addons#console 65 | - name: Copy and install Addons 66 | run: | 67 | rsync -av --exclude='./vendor' --exclude='.github' --exclude='.git' --exclude='redaxo_cms' './' 'redaxo_cms/redaxo/src/addons/${{ github.event.repository.name }}' 68 | redaxo_cms/redaxo/bin/console package:install 'cronjob' 69 | redaxo_cms/redaxo/bin/console package:install 'phpmailer' 70 | redaxo_cms/redaxo/bin/console install:download 'yform' '4.*' 71 | redaxo_cms/redaxo/bin/console package:install 'yform' 72 | redaxo_cms/redaxo/bin/console install:download 'yrewrite' '2.*' 73 | redaxo_cms/redaxo/bin/console package:install 'yrewrite' 74 | redaxo_cms/redaxo/bin/console package:install '${{ github.event.repository.name }}' 75 | 76 | # install dependencies from composer.json 77 | - name: Install test dependencies 78 | working-directory: redaxo_cms/redaxo/src/addons/${{ github.event.repository.name }} 79 | env: 80 | COMPOSER: composer.json 81 | run: composer install --prefer-dist --no-progress 82 | 83 | - name: Setup Problem Matchers for PHPUnit 84 | run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" 85 | 86 | # run unit tests, see composer.json 87 | - name: Run phpunit 88 | working-directory: redaxo_cms/redaxo/src/addons/${{ github.event.repository.name }} 89 | run: composer unit-test 90 | -------------------------------------------------------------------------------- /plugins/auth/pages/sessions.php: -------------------------------------------------------------------------------- 1 | i18n('ycom_title')); 6 | 7 | rex_ycom_user_session::clearExpiredSessions(); 8 | 9 | $func = rex_request('func', 'string', ''); 10 | switch ($func) { 11 | case 'ycom_user_delete_sessions': 12 | $amount = (int) rex_ycom_user_session::deleteAllSessions(); 13 | echo rex_view::success($this->i18n('sessions_deleted', $amount)); 14 | break; 15 | case 'remove_session': 16 | // delete session. 17 | $session_id = (string) rex_request('session_id', 'string', ''); 18 | $user_id = (string) rex_request::get('user_id', 'string', 0); 19 | rex_ycom_user_session::getInstance()->removeSession($session_id, $user_id); 20 | echo rex_view::success($this->i18n('session_removed')); 21 | break; 22 | case 'create_session': 23 | $user_id = (int) rex_request::get('user_id', 'int', 0); 24 | $ycom_user = rex_ycom_user::get($user_id); 25 | if (null !== $ycom_user) { 26 | if (1 > $ycom_user->getValue('status')) { 27 | echo rex_view::error(rex_i18n::rawMsg('ycom_session_could_not_been_added')); 28 | } else { 29 | $_SESSION[rex::getProperty('instname')][rex_ycom_auth::$sessionKey] = [ 30 | 'UID' => $ycom_user->getId(), 31 | ]; 32 | rex_ycom_user_session::getInstance()->storeCurrentSession($ycom_user); 33 | echo rex_view::success(rex_i18n::rawMsg('ycom_session_added_ready_to_login', rex_getUrl(rex_ycom_config::get('article_id_jump_ok')))); 34 | 35 | $be_user = rex::getUser(); 36 | if (null !== $be_user) { 37 | rex_ycom_log::log( 38 | $ycom_user, 39 | rex_ycom_log::TYPE_IMPERSONATE, 40 | [ 41 | 'be_user_id' => $be_user->getValue('id'), 42 | 'be_user_login' => $be_user->getValue('login'), 43 | 'be_user_name' => $be_user->getValue('name'), 44 | 'be_user_email' => $be_user->getValue('email'), 45 | ], 46 | ); 47 | } 48 | } 49 | } 50 | break; 51 | } 52 | 53 | echo rex_view::warning(rex_i18n::rawMsg('ycom_user_session_delete_all_sessions_link', rex_url::currentBackendPage() . '&func=ycom_user_delete_sessions')); 54 | 55 | $list = rex_list::factory('SELECT session_id, cookie_key, ip, user_id, useragent, starttime, last_activity from ' . rex::getTablePrefix() . 'ycom_user_session ORDER BY last_activity DESC'); 56 | 57 | $list->addColumn('remove_session', '', 0, ['', '###VALUE###']); 58 | $list->setColumnParams('remove_session', ['func' => 'remove_session', 'session_id' => '###session_id###', 'user_id' => '###user_id###']); 59 | $list->removeColumn('cookie_key'); 60 | $list->setColumnLabel('session_id', rex_i18n::msg('session_id')); 61 | $list->setColumnLabel('user_id', rex_i18n::msg('ycom_user_id')); 62 | $list->setColumnLabel('ip', rex_i18n::msg('ip')); 63 | $list->setColumnLabel('useragent', rex_i18n::msg('user_agent')); 64 | $list->setColumnLabel('starttime', rex_i18n::msg('starttime')); 65 | $list->setColumnLabel('last_activity', rex_i18n::msg('last_activity')); 66 | 67 | $list->setColumnFormat('session_id', 'custom', static function () use ($list) { 68 | return rex_escape((string) $list->getValue('session_id')) 69 | . ($list->getValue('cookie_key') ? ' ' . rex_i18n::msg('stay_logged_in') . '' : ''); 70 | }); 71 | 72 | $list->setColumnFormat('last_activity', 'custom', static function () use ($list) { 73 | if (session_id() === $list->getValue('session_id')) { 74 | return rex_formatter::intlDateTime((string) $list->getValue('last_activity'), IntlDateFormatter::SHORT) . '
' . rex_i18n::msg('active_session'); 75 | } 76 | return rex_formatter::intlDateTime((string) $list->getValue('last_activity'), IntlDateFormatter::SHORT); 77 | }); 78 | $list->setColumnFormat('starttime', 'custom', static function () use ($list) { 79 | return rex_formatter::intlDateTime((string) $list->getValue('starttime'), IntlDateFormatter::SHORT); 80 | }); 81 | 82 | $list->setColumnSortable('last_activity'); 83 | $list->setColumnSortable('active_session'); 84 | $list->setColumnSortable('ip'); 85 | $list->setColumnSortable('user_id'); 86 | $list->setColumnSortable('starttime'); 87 | $list->setColumnSortable('useragent'); 88 | 89 | $content = $list->get(); 90 | 91 | $fragment = new rex_fragment(); 92 | $fragment->setVar('title', rex_i18n::msg('session_caption')); 93 | $fragment->setVar('content', $content, false); 94 | echo $fragment->parse('core/page/section.php'); 95 | -------------------------------------------------------------------------------- /plugins/auth/boot.php: -------------------------------------------------------------------------------- 1 | getPath('ytemplates')); 15 | }); 16 | 17 | rex_extension::register('SESSION_REGENERATED', [rex_ycom_user_session::class, 'sessionRegenerated']); 18 | 19 | if (rex::isFrontend()) { 20 | rex_extension::register('PACKAGES_INCLUDED', static function (rex_extension_point $ep) { 21 | if ($redirect = rex_ycom_auth::init()) { 22 | rex_response::sendCacheControl(); 23 | rex_response::sendRedirect($redirect); 24 | } 25 | }); 26 | 27 | /* @deprecated use EP ART_IS_PERMITTED and CAT_IS_PERMITTED instead´ */ 28 | rex_extension::register('YREWRITE_ARTICLE_PERM', static function (rex_extension_point $ep) { 29 | $params = $ep->getParams(); 30 | return rex_ycom_auth::articleIsPermitted($params['article']); 31 | }); 32 | 33 | rex_extension::register(['ART_IS_PERMITTED', 'CAT_IS_PERMITTED'], static function (rex_extension_point $ep) { 34 | $params = $ep->getParams(); 35 | return rex_ycom_auth::articleIsPermitted($params['element'], $ep->getSubject() ? true : false); 36 | }); 37 | 38 | rex_extension::register('YCOM_AUTH_MATCHING', static function (rex_extension_point $ep) { 39 | $data = $ep->getSubject(); 40 | $params = $ep->getParams(); 41 | $Userdata = $params['Userdata']; 42 | $AuthType = $params['AuthType']; 43 | 44 | switch ($AuthType) { 45 | case 'oauth2': 46 | $data = rex_extension::registerPoint(new rex_extension_point('YCOM_AUTH_OAUTH2_MATCHING', $data, ['Userdata' => $Userdata])); 47 | break; 48 | case 'saml': 49 | $data = rex_extension::registerPoint(new rex_extension_point('YCOM_AUTH_SAML_MATCHING', $data, ['Userdata' => $Userdata])); 50 | break; 51 | case 'cas': 52 | $data = rex_extension::registerPoint(new rex_extension_point('YCOM_AUTH_CAS_MATCHING', $data, ['Userdata' => $Userdata])); 53 | break; 54 | } 55 | 56 | return $data; 57 | }, rex_extension::EARLY); 58 | } elseif (rex::isBackend()) { 59 | rex_view::addCssFile($this->getAssetsUrl('styles.css')); 60 | 61 | rex_extension::register('PACKAGES_INCLUDED', function (rex_extension_point $ep) { 62 | rex_extension::register('STRUCTURE_CONTENT_SIDEBAR', function (rex_extension_point $ep) { 63 | $params = $ep->getParams(); 64 | $subject = $ep->getSubject(); 65 | 66 | $panel = include rex_path::plugin('ycom', 'auth', 'pages/content.ycom_auth.php'); 67 | 68 | $fragment = new rex_fragment(); 69 | $fragment->setVar('title', ' ' . $this->i18n('ycom_page_perm'), false); 70 | $fragment->setVar('body', $panel, false); 71 | $fragment->setVar('article_id', $params['article_id'], false); 72 | $fragment->setVar('clang', $params['clang'], false); 73 | $fragment->setVar('ctype', $params['ctype'], false); 74 | $fragment->setVar('collapse', true); 75 | $fragment->setVar('collapsed', false); 76 | $content = $fragment->parse('core/page/section.php'); 77 | 78 | return $subject . $content; 79 | }); 80 | }, rex_extension::EARLY); 81 | 82 | rex_extension::register('YFORM_DATA_LIST_ACTION_BUTTONS', static function (rex_extension_point $ep) { 83 | $params = $ep->getParams(); 84 | /** @var rex_yform_manager_table $table */ 85 | $table = $params['table']; 86 | if (rex::getTablePrefix() . 'ycom_user' === $table->getTableName()) { 87 | if (rex::getUser() && rex::getUser()->isAdmin()) { 88 | $actionButtons = $ep->getSubject(); 89 | $actionButtons['ycom_impersonate'] = [ 90 | 'params' => [], 91 | 'content' => ' ' . rex_i18n::msg('ycom_impersonate'), 92 | 'attributes' => [ 93 | 'onclick' => "return confirm(' " . rex_i18n::msg('ycom_impersonate_alert') . "')", 94 | ], 95 | 'url' => rex_url::backendController(['page' => 'ycom/auth/sessions', 'user_id' => '___id___', 'func' => 'create_session']), 96 | ]; 97 | return $actionButtons; 98 | } 99 | } 100 | }); 101 | } 102 | -------------------------------------------------------------------------------- /lib/ycom_user.php: -------------------------------------------------------------------------------- 1 | 'translate:ycom_account_inactive_termination', 16 | self::STATUS_INACTIVE_LOGINS => 'translate:ycom_account_inactive_logins', 17 | self::STATUS_INACTIVE => 'translate:ycom_account_inactive', 18 | self::STATUS_REQUESTED => 'translate:ycom_account_requested', 19 | self::STATUS_CONFIRMED => 'translate:ycom_account_confirm', 20 | self::STATUS_ACTIVE => 'translate:ycom_account_active', 21 | ]; 22 | public string $password = ''; 23 | public int $login_tries = 0; 24 | 25 | /** 26 | * @return rex_ycom_user|null 27 | */ 28 | public static function getMe() 29 | { 30 | return rex_ycom_auth::getUser(); 31 | } 32 | 33 | public function isInGroup(int $group_id): bool 34 | { 35 | $ycom_groups = (string) $this->getValue('ycom_groups'); 36 | 37 | if (1 > $group_id) { 38 | return true; 39 | } 40 | if ('' !== $ycom_groups) { 41 | $ycom_groups_array = explode(',', $ycom_groups); 42 | if (in_array((string) $group_id, $ycom_groups_array, true)) { 43 | return true; 44 | } 45 | } 46 | 47 | return false; 48 | } 49 | 50 | public function getPassword(): string 51 | { 52 | return $this->getValue('password'); 53 | } 54 | 55 | /** 56 | * @return array|array 57 | */ 58 | public function getGroups(): array 59 | { 60 | if ('' === $this->getValue('ycom_groups')) { 61 | return []; 62 | } 63 | 64 | return explode(',', $this->getValue('ycom_groups')); 65 | } 66 | 67 | /** 68 | * @param array $data 69 | * @return rex_ycom_user|rex_yform_manager_dataset|null 70 | */ 71 | public static function createUserByEmail(array $data) 72 | { 73 | $data['status'] = self::STATUS_CONFIRMED; 74 | $data['password'] = str_shuffle('1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'); 75 | $data['login'] = $data['email']; 76 | $data['login_tries'] = 0; 77 | $data['termsofuse_accepted'] = 0; 78 | 79 | $data = rex_extension::registerPoint(new rex_extension_point('YCOM_USER_CREATE', $data, [])); 80 | 81 | $user = self::create(); 82 | foreach ($data as $k => $v) { 83 | $user->setValue((string) $k, (string) $v); 84 | } 85 | if ($user->save()) { 86 | return $user; 87 | } 88 | return null; 89 | } 90 | 91 | /** 92 | * @param array $data 93 | */ 94 | public static function updateUser(array $data): bool 95 | { 96 | $data = rex_extension::registerPoint(new rex_extension_point('YCOM_USER_UPDATE', $data, [])); 97 | $user = self::getMe(); 98 | 99 | if (null === $user) { 100 | return false; 101 | } 102 | 103 | foreach ($data as $k => $v) { 104 | $user->setValue((string) $k, (string) $v); 105 | } 106 | 107 | return $user 108 | ->save(); 109 | } 110 | 111 | public function increaseLoginTries(): self 112 | { 113 | $this->setValue('login_tries', $this->getValue('login_tries') + 1); 114 | return $this; 115 | } 116 | 117 | public function increaseOTPTries(): self 118 | { 119 | $otp_tries = (int) $this->getValue('otp_tries'); 120 | $this->setValue('otp_tries', $otp_tries + 1); 121 | $this->setValue('otp_last_try_time', time()); 122 | return $this; 123 | } 124 | 125 | public function resetOTPTries(): self 126 | { 127 | $this->setValue('otp_tries', 0); 128 | $this->setValue('otp_last_try_time', time()); 129 | return $this; 130 | } 131 | 132 | /** 133 | * Get status options with extension point for customization. 134 | * @return array 135 | */ 136 | public static function getStatusOptions(): array 137 | { 138 | // Allow additional status options via extension point 139 | $statusOptions = rex_extension::registerPoint(new rex_extension_point('YCOM_USER_STATUS_OPTIONS', self::DEFAULT_STATUS_OPTIONS)); 140 | if (!is_array($statusOptions)) { 141 | $statusOptions = self::DEFAULT_STATUS_OPTIONS; 142 | } 143 | return $statusOptions; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /docs/02_settings.md: -------------------------------------------------------------------------------- 1 | # Einstellungen 2 | 3 | Die Einstellungsseite befindet sich im REDAXO-Backend unter `YCom` > `Einstellungen`. 4 | 5 | > Hinweis: Nicht alle hier getätigen Einstellungen funktionieren von Haus aus und erfordern ggf. noch weitere Einstellungen in der Struktur, Tempaltes, Modulen oder E-Mail-Tempaltes. 6 | 7 | > Hinweis: Will man die Einstellungen individuell überschreiben, z.B. über eine settings-Datei, dann kann man den ExtensionPoint `YCOM_CONFIG` dazu nutzen 8 | 9 | | Weiterleitungen | Erläuterung | 10 | |----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 11 | | article_id_jump_ok | Ziel-Artikel, zu dem weitergeleitet wird, wenn der Login erfolgreich war. | 12 | | article_id_jump_not_ok | Ziel-Artikel, zu dem weitergeleitet wird, wenn der Login fehlgeschlagen ist. Dies kann auch der Login-Artikel sein. (nur für SAML-Authentifizierung) | 13 | | article_id_jump_logout | Ziel-Artikel, zu dem weitergeleitet wird, nachdem Logout erfolgt ist. Dies kann auch der Login-Artikel sein. | 14 | | article_id_jump_denied | Ziel-Artikel, zu dem weitergeleitet wird, wenn der Besucher nicht die passende Gruppen-Berechtigung hat. Z.B., wenn der Benutzer nicht eingeloggt ist oder keine passende Gruppenberechtigung hat. Dies kann auch der Login-Artikel sein. | 15 | | article_id_jump_password | (optional) Artikel, in dem das Passwort geändert wird. | 16 | | article_id_jump_termsofuse | (optional) Artikel, zu dem der Nutzer weitergeleitet wird, wenn der Besucher die Nutzungsbedingungen akzeptiert hat. | 17 | | otp_article_id | (optional) Ziel-Artikel, zu dem weitergeleitet wird, wenn der Nutzer nach dem Login noch ein OTP eingeben muss oder der Benutzer seine OTP-Einstellungen ändert. | 18 | 19 | 20 | | Allgemeine Seiten | Erläuterung | 21 | |-----------------------|---------------------------------------------------------------------------| 22 | | Login-Seite | Kategorie / Artikel, der das Login-Formular enthält. | 23 | | Register-Seite | (optional) Kategorie / Artikel, der das Registrier-Formular enthält. | 24 | | Passwort zurücksetzen | (optional) Kategorie / Artikel, der das Passwort-ändern-Formular enthält. | 25 | 26 | | Login | Erläuterung | 27 | |------------|-------------------------------------------------------------------------------------------------------------------------------------| 28 | | Login-Feld | Feld, das für den Login berücksichtigt werden soll, z.B. `email` (E-Mail-Adresse des Nutzers) oder `login` (Pseudonym des Nutzers). | 29 | 30 | | Sicherheit | Erläuterung | 31 | |-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------| 32 | | Authentifizierungsregel | Regel, mit der Bruteforce-Attacken unterbunden werden, z.B. um nach 5 fehlgeschlagenen Logins den entsprechenden Nutzer für 15 Minuten zu sperren. | 33 | 34 | | OTP-Einstellungen | Erläuterung | 35 | |------------------------------|------------------------------------------------------------------------| 36 | | Einschränkungen | Regel, ob OTP 2FA optional oder verpflichtend sein soll. | 37 | | Optionen | Regel, ob nur TOTPs, OTPs über E-Mail oder beides erlaubt sein sollen. | 38 | | Zeitintervall E-Mail | Nach wie vielen Minuten ein neuer E-Mail-Code generiert werden soll. | 39 | | Zeitintervall Authenticators | Nach wie vielen Minuten ein neuer TOTP-Code generiert werden soll. | 40 | | Fehlversuche | Anzahl der erlaubten Fehlversuche. | 41 | -------------------------------------------------------------------------------- /plugins/auth/lib/yform/value/ycom_user_token.php: -------------------------------------------------------------------------------- 1 | 'create', 7 | 'validate' => 'validate', 8 | ]; 9 | private ?string $ycom_user_token_action = null; 10 | private ?string $ycom_user_token_type = null; 11 | private ?rex_ycom_user_token $ycom_user_token = null; 12 | private int $ycom_user_token_duration = 60 * 60 * 24; 13 | 14 | public function enterObject(): void 15 | { 16 | $this->ycom_user_token_action = (string) $this->getElement(2); 17 | if (!in_array($this->ycom_user_token_action, self::$ycom_user_token_types)) { 18 | throw new rex_exception('ycom_user_token: action not defined. must be create or validate'); 19 | } 20 | 21 | $this->ycom_user_token_type = (string) $this->getElement(3); 22 | if ('' == $this->ycom_user_token_type) { 23 | throw new rex_exception('ycom_user_token: type not defined'); 24 | } 25 | 26 | switch ($this->ycom_user_token_action) { 27 | case 'create': 28 | // Wenn actions durchgelaufen sind, dann Token erstellen 29 | break; 30 | case 'validate': 31 | if (!$this->params['send']) { 32 | $value = rex_request($this->getName(), 'string', ''); 33 | $this->setValue($value); 34 | } else { 35 | try { 36 | $this->ycom_user_token = rex_ycom_user_token::getInstance()->validateToken($this->getValue(), $this->ycom_user_token_type); 37 | } catch (Exception $e) { 38 | $this->params['warning'][$this->getId()] = $this->params['error_class']; 39 | $this->params['warning_messages'][$this->getId()] = $this->getElement(4); 40 | } 41 | } 42 | 43 | if ($this->needsOutput()) { 44 | $this->params['form_output'][$this->getId()] = $this->parse('value.hidden.tpl.php'); 45 | } 46 | 47 | break; 48 | } 49 | } 50 | 51 | public function preAction(): void 52 | { 53 | switch ($this->ycom_user_token_action) { 54 | case 'create': 55 | // preActions, create Token 56 | $email = $this->params['value_pool']['sql']['email'] ?? ''; 57 | if ('' == $email) { 58 | throw new rex_exception('ycom_user_token: email not found'); 59 | } 60 | $user = rex_ycom_user::getMe(); 61 | $user_id = ($user instanceof rex_ycom_user) ? $user->getId() : null; 62 | 63 | $TokenData = rex_ycom_user_token::getInstance() 64 | ->createToken($user_id, $email, $this->ycom_user_token_type, $this->ycom_user_token_duration); 65 | 66 | $this->params['value_pool']['email'][$this->getName()] = $TokenData['token']; 67 | break; 68 | 69 | case 'validate': 70 | $user = null; 71 | 72 | if ($this->ycom_user_token->getId()) { 73 | $user = rex_ycom_user::query() 74 | ->where('id', $this->ycom_user_token->getId()) 75 | ->findOne(); 76 | } 77 | 78 | if (!$user && $this->ycom_user_token->getEmail()) { 79 | $user = rex_ycom_user::query() 80 | ->where('email', $this->ycom_user_token->getEmail()) 81 | ->findOne(); 82 | } 83 | 84 | if ($user && count($this->params['value_pool']['sql']) > 0) { 85 | foreach ($this->params['value_pool']['sql'] as $key => $value) { 86 | $user->setValue($key, $value); 87 | } 88 | $user->save(); 89 | } 90 | 91 | $user = rex_ycom_auth::loginWithParams([ 92 | 'email' => $user->getValue('email'), 93 | ]); 94 | 95 | $this->ycom_user_token->delete(); 96 | 97 | if ($user) { 98 | $redirect = false; 99 | foreach ($this->params['fields']['actions'] as $action) { 100 | if (rex_yform_action_redirect::class === $action::class) { 101 | $redirect = true; 102 | break; 103 | } 104 | } 105 | 106 | if (!$redirect) { 107 | rex_response::sendRedirect(rex_getUrl(rex_ycom_config::get('article_id_login'))); 108 | } 109 | } 110 | } 111 | } 112 | 113 | public function getDescription(): string 114 | { 115 | return 'ycom_user_token|create|[type]|[email_template] 116 | ycom_user_token|validate|[type]|[login/only_update]|[error_message]'; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /plugins/auth/assets/clipboard-copy-element.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 3 | typeof define === 'function' && define.amd ? define(factory) : 4 | (global = global || self, global.ClipboardCopyElement = factory()); 5 | }(this, function () { 'use strict'; 6 | 7 | function createNode(text) { 8 | const node = document.createElement('pre'); 9 | node.style.width = '1px'; 10 | node.style.height = '1px'; 11 | node.style.position = 'fixed'; 12 | node.style.top = '5px'; 13 | node.textContent = text; 14 | return node; 15 | } 16 | 17 | function copyNode(node) { 18 | if ('clipboard' in navigator) { 19 | // eslint-disable-next-line flowtype/no-flow-fix-me-comments 20 | // $FlowFixMe Clipboard is not defined in Flow yet. 21 | return navigator.clipboard.writeText(node.textContent); 22 | } 23 | 24 | const selection = getSelection(); 25 | 26 | if (selection == null) { 27 | return Promise.reject(new Error()); 28 | } 29 | 30 | selection.removeAllRanges(); 31 | const range = document.createRange(); 32 | range.selectNodeContents(node); 33 | selection.addRange(range); 34 | document.execCommand('copy'); 35 | selection.removeAllRanges(); 36 | return Promise.resolve(); 37 | } 38 | function copyText(text) { 39 | if ('clipboard' in navigator) { 40 | // eslint-disable-next-line flowtype/no-flow-fix-me-comments 41 | // $FlowFixMe Clipboard is not defined in Flow yet. 42 | return navigator.clipboard.writeText(text); 43 | } 44 | 45 | const body = document.body; 46 | 47 | if (!body) { 48 | return Promise.reject(new Error()); 49 | } 50 | 51 | const node = createNode(text); 52 | body.appendChild(node); 53 | copyNode(node); 54 | body.removeChild(node); 55 | return Promise.resolve(); 56 | } 57 | 58 | function copy(button) { 59 | const id = button.getAttribute('for'); 60 | const text = button.getAttribute('value'); 61 | 62 | function trigger() { 63 | button.dispatchEvent(new CustomEvent('clipboard-copy', { 64 | bubbles: true 65 | })); 66 | } 67 | 68 | if (text) { 69 | copyText(text).then(trigger); 70 | } else if (id) { 71 | const root = 'getRootNode' in Element.prototype ? button.getRootNode() : button.ownerDocument; 72 | if (!(root instanceof Document || 'ShadowRoot' in window && root instanceof ShadowRoot)) return; 73 | const node = root.getElementById(id); 74 | if (node) copyTarget(node).then(trigger); 75 | } 76 | } 77 | 78 | function copyTarget(content) { 79 | if (content instanceof HTMLInputElement || content instanceof HTMLTextAreaElement) { 80 | return copyText(content.value); 81 | } else if (content instanceof HTMLAnchorElement && content.hasAttribute('href')) { 82 | return copyText(content.href); 83 | } else { 84 | return copyNode(content); 85 | } 86 | } 87 | 88 | function clicked(event) { 89 | const button = event.currentTarget; 90 | 91 | if (button instanceof HTMLElement) { 92 | copy(button); 93 | } 94 | } 95 | 96 | function keydown(event) { 97 | if (event.key === ' ' || event.key === 'Enter') { 98 | const button = event.currentTarget; 99 | 100 | if (button instanceof HTMLElement) { 101 | event.preventDefault(); 102 | copy(button); 103 | } 104 | } 105 | } 106 | 107 | function focused(event) { 108 | event.currentTarget.addEventListener('keydown', keydown); 109 | } 110 | 111 | function blurred(event) { 112 | event.currentTarget.removeEventListener('keydown', keydown); 113 | } 114 | 115 | class ClipboardCopyElement extends HTMLElement { 116 | constructor() { 117 | super(); 118 | this.addEventListener('click', clicked); 119 | this.addEventListener('focus', focused); 120 | this.addEventListener('blur', blurred); 121 | } 122 | 123 | connectedCallback() { 124 | if (!this.hasAttribute('tabindex')) { 125 | this.setAttribute('tabindex', '0'); 126 | } 127 | 128 | if (!this.hasAttribute('role')) { 129 | this.setAttribute('role', 'button'); 130 | } 131 | } 132 | 133 | get value() { 134 | return this.getAttribute('value') || ''; 135 | } 136 | 137 | set value(text) { 138 | this.setAttribute('value', text); 139 | } 140 | 141 | } 142 | 143 | if (!window.customElements.get('clipboard-copy')) { 144 | window.ClipboardCopyElement = ClipboardCopyElement; 145 | window.customElements.define('clipboard-copy', ClipboardCopyElement); 146 | } 147 | 148 | return ClipboardCopyElement; 149 | 150 | })); 151 | -------------------------------------------------------------------------------- /docs/09_tricks.md: -------------------------------------------------------------------------------- 1 | # Tricks 2 | 3 | ## Nach dem Aktivierungs-Link direkt wieder ausloggen 4 | 5 | Nach der Zeile `action|ycom_auth_db|update` diese Zeile einfügen: `action|ycom_auth_db|logout` 6 | 7 | [Diskussion auf Github](https://github.com/yakamara/redaxo_ycom/issues/221), vielen Dank an @godsdog 8 | 9 | ## Unterschiedliche Zielseiten für verschiedene Nutzer 10 | 11 | Wenn nach dem Login Nutzer zu unterschiedlichen Seiten weitergeleitet werden sollen, muss YCom um die entsprechende Funktion erweitert werden. 12 | 13 | ### Variante A: Unterschiedliche Zielseiten je YCom-Nutzer 14 | 15 | Voraussetzung ist, dass mindestens ein Benutzer vorhanden ist. 16 | 17 | **Vorbereitung** 18 | 19 | Zunächst muss die Tabelle `User` um ein Feld erweitert werden, das die gewünschte ID zum Zielartikel enthält. 20 | 21 | 1. im REDAXO-Backend `YCom` -> `User` aufrufen 22 | 2. In der Tabelle User auf `Felder editieren` klicken. 23 | 3. Auf das `+` Symbol klicken, um ein neues YForm-Feld hinzuzufügen. 24 | 4. Als Feldtyp `be_link` auswählen und z.B. folgende Werte eingeben: 25 | * Name: `target_id` 26 | * Bezeichnung: `Ziel-Artikel` 27 | 5. Das Feld mit `Speichern` hinzufügen. 28 | 29 | **Zuweisung der Zielseiten** 30 | 31 | Als nächstes wird jedem Benutzer ein Zielartikel zugewiesen. 32 | 33 | 1. Im REDAXO-Backend `YCom` -> `User` aufrufen 34 | 2. Benutzer editieren, um im neuen Feld `Ziel-Artikel` einen Artikel aus der Struktur auszuwählen und speichern. Diesen Schritt für jeden Benutzer wiederholen 35 | 36 | **Weiterleitung einrichten** 37 | 38 | Zu guter Letzt werden REDAXO und YCom so konfiguriert, dass nach dem Login automatisch auf den richtigen Artikel weitergeleitet wird. 39 | 40 | 1. Im REDAXO-Backend ein neues Template `Login-Weiterleitung` anlegen und folgenden Code kopieren: 41 | 42 | ```php 43 | getValue("target_id"); 50 | 51 | if($target_id) { 52 | rex_redirect($target_id); 53 | } 54 | 55 | } else { 56 | // System-Start-Artikel 57 | rex_redirect(rex_article::getSiteStartArticleId()); 58 | // Alternativ: YRewrite-Domain-Startartikel 59 | # rex_redirect(rex_yrewrite::getCurrentDomain()->getStartId()); 60 | } 61 | ?> 62 | ``` 63 | 64 | > Hinweis: Wenn ein Nutzer auf den Artikel zugreift, ohne eingeloggt zu sein, wird er automatisch auf die Startseite weitergeleitet. 65 | 66 | 2. Unter Struktur einen neuen Artikel `Login-Weiterleitung` anlegen und das Template `Login-Weiterleitung` zuweisen. 67 | 3. Im REDAXO-Backend `YCom` -> `Einstellungen` aufrufen und unter "...wenn erfolgreich eingeloggt [target_id_jump_ok]" den Artikel `Login-Weiterleitung` auswählen. 68 | 69 | ### Variante B: Unterschiedliche Zielseiten je YCom-Gruppe 70 | 71 | Voraussetzung ist, dass mindestens ein Benutzer mindestens einer Gruppe zugewiesen ist. 72 | 73 | **Vorbereitung** 74 | 75 | Zunächst muss die Tabelle `Gruppen` um ein Feld erweitert werden, das die gewünschte ID zum Zielartikel enthält. 76 | 77 | 1. im REDAXO-Backend `YCom` -> `Gruppen` aufrufen 78 | 2. In der Tabelle Gruppen auf `Felder editieren` klicken. 79 | 3. Auf das `+` Symbol klicken, um ein neues YForm-Feld hinzuzufügen. 80 | 4. Als Feldtyp `be_link` auswählen und z.B. folgende Werte eingeben: 81 | * Name: `target_id` 82 | * Bezeichnung: `Ziel-Artikel` 83 | 5. Das Feld mit `Speichern` hinzufügen. 84 | 85 | **Zuweisung der Zielseiten** 86 | 87 | Als nächstes wird jeder angelegten Gruppe ein Zielartikel zugewiesen. 88 | 89 | 1. Im REDAXO-Backend `YCom` -> `Gruppen` aufrufen 90 | 2. Gruppe editieren, um im neuen Feld `Ziel-Artikel` einen Artikel aus der Struktur auszuwählen und speichern. Diesen Schritt für jede Gruppe wiederholen 91 | 92 | **Weiterleitung einrichten** 93 | 94 | Zu guter Letzt werden REDAXO und YCom so konfiguriert, dass nach dem Login automatisch auf den richtigen Artikel weitergeleitet wird. 95 | 96 | 1. Im REDAXO-Backend ein neues Template `Login-Weiterleitung` anlegen und folgenden Code kopieren: 97 | 98 | ```php 99 | getRelatedDataset('ycom_groups')->getValue("target_id"); 106 | 107 | if($target_id) { 108 | rex_redirect($target_id); 109 | } 110 | 111 | } else { 112 | // System-Start-Artikel 113 | rex_redirect(rex_article::getSiteStartArticleId()); 114 | // Alternativ: YRewrite-Domain-Startartikel 115 | # rex_redirect(rex_yrewrite::getCurrentDomain()->getStartId()); 116 | } 117 | ?> 118 | ``` 119 | 120 | > Hinweis: Wenn ein Nutzer auf den Artikel zugreift, ohne eingeloggt zu sein, wird er automatisch auf die Startseite weitergeleitet. 121 | 122 | 2. Unter Struktur einen neuen Artikel `Login-Weiterleitung` anlegen und das Template `Login-Weiterleitung` zuweisen. 123 | 3. Im REDAXO-Backend `YCom` -> `Einstellungen` aufrufen und unter "...wenn erfolgreich eingeloggt [target_id_jump_ok]" den Artikel `Login-Weiterleitung` auswählen. 124 | 125 | ## Du hast weitere Tricks auf Lager? 126 | 127 | Dann [ergänze die YCom-Doku](https://github.com/yakamara/redaxo_ycom/tree/master/) oder schreibe ein [Issue auf GitHub](https://github.com/yakamara/redaxo_ycom/issues). 128 | -------------------------------------------------------------------------------- /plugins/auth/lib/ycom_user_token.php: -------------------------------------------------------------------------------- 1 | getArray( 28 | 'SELECT * FROM ' . rex::getTable('ycom_user_token') . ' WHERE hash = ? and type = ? and expiredate > ?', 29 | [ 30 | $user_hash, 31 | $type, 32 | rex_sql::datetime(time()), 33 | ]); 34 | 35 | if (1 !== count($TokenData)) { 36 | throw new Exception('Token not found'); 37 | } 38 | 39 | // check hashes 40 | $this->hash = $this->generateHash($TokenData[0]['selector'], $user_verifier); 41 | 42 | if (!hash_equals($this->hash, $user_hash)) { 43 | throw new Exception('Token is invalid'); 44 | } 45 | 46 | $this->user_id = $TokenData[0]['user_id']; 47 | $this->email = $TokenData[0]['email']; 48 | $this->type = $TokenData[0]['type']; 49 | $this->selector = $TokenData[0]['selector']; 50 | $this->createdate = $TokenData[0]['createdate']; 51 | $this->expiredate = $TokenData[0]['expiredate']; 52 | 53 | return $this; 54 | } 55 | 56 | public function getId(): ?int 57 | { 58 | return $this->user_id; 59 | } 60 | 61 | public function getEmail(): string 62 | { 63 | return $this->email; 64 | } 65 | 66 | public function delete(): bool 67 | { 68 | $sql = rex_sql::factory() 69 | ->setTable(rex::getTable('ycom_user_token')) 70 | ->setWhere('hash = ?', [$this->hash]) 71 | ->delete(); 72 | return $sql->getRows() > 0; 73 | } 74 | 75 | public function generateHash($selector, $verifier): string 76 | { 77 | return base64_encode(hash_hmac('sha256', $selector, $verifier, false)); 78 | } 79 | 80 | public function createToken($user_id, $email, $type, $duration): array 81 | { 82 | $this->user_id = $user_id; 83 | $this->email = $email; 84 | $this->type = $type; 85 | $this->duration = $duration; 86 | 87 | $this->selector = ByteString::fromRandom(20)->toString(); 88 | $this->verifier = ByteString::fromRandom(20)->toString(); // do not store in database! 89 | 90 | $this->hash = $this->generateHash($this->selector, $this->verifier); // base64_encode(hash_hmac('sha256', $this->selector, $this->verifier, false)); 91 | $this->createdate = rex_sql::datetime(time()); 92 | $this->expiredate = rex_sql::datetime(time() + $this->duration); 93 | $this->token = $this->verifier . $this->hash; 94 | 95 | try { 96 | rex_sql::factory() 97 | ->setDebug(false) 98 | ->setTable(rex::getTable('ycom_user_token')) 99 | ->setValue('hash', $this->hash) 100 | ->setValue('user_id', $this->user_id) 101 | ->setValue('email', $this->email) 102 | ->setValue('type', $this->type) 103 | ->setValue('selector', $this->selector) 104 | ->setValue('createdate', $this->createdate) 105 | ->setValue('expiredate', $this->expiredate) 106 | ->insert(); 107 | } catch (Exception $e) { 108 | throw new Exception('Token could not be created'); 109 | } 110 | 111 | return [ 112 | 'hash' => $this->hash, 113 | 'user_id' => $this->user_id, 114 | 'email' => $this->email, 115 | 'type' => $this->type, 116 | 'selector' => $this->selector, 117 | 'verifier' => $this->verifier, 118 | 'token' => $this->token, 119 | 'createdate' => $this->createdate, 120 | 'expiredate' => $this->expiredate, 121 | ]; 122 | } 123 | 124 | public function getTokenByHash(string $hash): array 125 | { 126 | $sql = rex_sql::factory() 127 | ->setTable(rex::getTable('ycom_user_token')) 128 | ->setWhere('hash = ?', [$hash]) 129 | ->select(); 130 | return $sql->getArray(); 131 | } 132 | 133 | public static function clearExpiredTokens(): void 134 | { 135 | rex_sql::factory() 136 | ->setTable(rex::getTable('ycom_user_token')) 137 | ->setWhere('expiredate < :current_datetime', [ 138 | ':current_datetime' => rex_sql::datetime(time()), 139 | ]) 140 | ->delete(); 141 | } 142 | 143 | public static function removeTokenByHash(string $hash): bool 144 | { 145 | $sql = rex_sql::factory() 146 | ->setTable(rex::getTable('ycom_user_token')) 147 | ->setWhere('hash = ?', [$hash]) 148 | ->delete(); 149 | return $sql->getRows() > 0; 150 | } 151 | 152 | public static function deleteAllTokens(): bool 153 | { 154 | $sql = rex_sql::factory() 155 | ->setTable(rex::getTable('ycom_user_token')) 156 | ->delete(); 157 | return $sql->getRows() > 0; 158 | } 159 | } 160 | --------------------------------------------------------------------------------