Skip to content
This repository has been archived by the owner on Apr 23, 2024. It is now read-only.

Commit

Permalink
Add code
Browse files Browse the repository at this point in the history
  • Loading branch information
aolle committed Nov 26, 2023
1 parent e5b0ab3 commit 356b5f2
Show file tree
Hide file tree
Showing 101 changed files with 9,482 additions and 0 deletions.
40 changes: 40 additions & 0 deletions code/authentication-spi/telegram-authentication-spi/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
release.properties
.flattened-pom.xml

# Eclipse
.project
.classpath
.settings/
bin/

# IntelliJ
.idea
*.ipr
*.iml
*.iws

# NetBeans
nb-configuration.xml

# Visual Studio Code
.vscode
.factorypath

# OSX
.DS_Store

# Vim
*.swp
*.swo

# patch
*.orig
*.rej

# Local environment
.env
10 changes: 10 additions & 0 deletions code/authentication-spi/telegram-authentication-spi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
rh-sso-custom-authentication-spi
---

Expected variable as host:[port] default value: localhost:8081 => USER_STORAGE_CUSTOM_SPI_TARGET_HOST

Must be setted (no default value):
Telegram bot token variable: TELEGRAM_TOKEN
Telegram bot name: TELEGRAM_BOT_USERNAME

The Authenticator has been developed with a rolling action for obtaining the telegram ID instead of the @username for simplification; this avoids the use of the telegram api_id, phone_registered and api_hash. If desired, the authenticator can be modified for doing a lookup of the Telegram ID from the @username calling https://core.telegram.org/method/users.getFullUser
103 changes: 103 additions & 0 deletions code/authentication-spi/telegram-authentication-spi/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.olleb</groupId>
<artifactId>telegram-authentication-spi</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<!--
RH-SSO 7.6 version => Wildfly 15
-->
<version.server.bom>15.0.1.Final</version.server.bom>
<!--
https://access.redhat.com/articles/2342881
RH-SSO 7.6 version => Keycloak 18.0
-->
<keycloak.version>18.0.2</keycloak.version>
</properties>

<dependencyManagement>
<dependencies>
<!-- JBoss distributes a complete set of Java EE APIs including a Bill
of Materials (BOM). A BOM specifies the versions of a "stack" (or a collection)
of artifacts. We use this here so that we always get the correct versions
of artifacts. Here we use the jboss-eap-javaee8-with-tools stack (you can
read this as the JBoss stack of the Java EE APIs, with some extras tools
for your project, such as Arquillian for testing) -->
<dependency>
<groupId>org.wildfly.bom</groupId>
<artifactId>wildfly-javaee8-with-tools</artifactId>
<version>${version.server.bom}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<scope>provided</scope>
</dependency>
<!--
Upstream note (latest upstream version). It doesn apply on this concrete example, as we are aligned with RH-SSO equivalent version.
This functionality depends on APIs bundled in the keycloak-model-legacy module.
It will soon be replaced with the new map storage API which provides a uniform way to access both local and external information
about users and other entities, and the old APIs will be removed eventually.
-->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
<version>${keycloak.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi-private</artifactId>
<version>${keycloak.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${keycloak.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<version>${keycloak.version}</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* This file is part of Red Hat Single Sign-On workshop
* Copyright (C) 2022 Àngel Ollé Blázquez
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.olleb.action;

import java.lang.invoke.MethodHandles;
import java.util.Locale;
import java.util.function.Predicate;

import javax.ws.rs.core.Response;

import org.jboss.logging.Logger;
import org.keycloak.authentication.RequiredActionContext;
import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.common.util.SecretGenerator;
import org.keycloak.theme.Theme;

import com.olleb.service.TelegramMessage;
import com.olleb.service.TelegramService;

/**
* @author Àngel Ollé Blázquez
*/
public class TelegramRequiredAction implements RequiredActionProvider {

private static final Logger LOGGER = Logger.getLogger(MethodHandles.lookup().lookupClass());

private static final TelegramService TELEGRAM_SERVICE = TelegramService.getInstance();

private static final Predicate<String> BLANK = s -> s == null || s.isBlank();

@Override
public void evaluateTriggers(RequiredActionContext context) {
String telegramId = context.getUser().getFirstAttribute(TelegramRequiredActionConstants.TELEGRAM_ID_ATTRIBUTE);
if (BLANK.test(telegramId)) {
context.getUser().addRequiredAction(TelegramRequiredActionConstants.PROVIDER_ID);
}
}

@Override
public void requiredActionChallenge(RequiredActionContext context) {
try {
Theme theme = context.getSession().theme().getTheme(Theme.Type.LOGIN);
String msg = theme.getMessages(Locale.ENGLISH)
.getProperty(TelegramRequiredActionConstants.TELEGRAM_ID_PROPERTY);
String code = SecretGenerator.getInstance().randomString(
TelegramRequiredActionConstants.TELEGRAM_ROLLING_CODE_LENGTH,
SecretGenerator.DIGITS);

context.getAuthenticationSession()
.setAuthNote(TelegramRequiredActionConstants.TELEGRAM_SECURITY_CODE_ATTRIBUTE, code);

msg = String.format(msg, code, TelegramService.getTelegramBotUsername());

Response response = context.form()
.setAttribute(TelegramRequiredActionConstants.TELEGRAM_MESSAGE_SECURITY_CODE_ATTRIBUTE, msg)
.createForm(TelegramRequiredActionConstants.TEMPLATE);

context.challenge(response);
} catch (Exception e) {
LOGGER.error(e);
}
}

@Override
public void processAction(RequiredActionContext context) {
String subscriptionCode = context.getHttpRequest().getDecodedFormParameters()
.getFirst(TelegramRequiredActionConstants.TELEGRAM_ROLLING_CODE_ATTRIBUTE);

if (BLANK.test(subscriptionCode)) {
requiredActionChallenge(context);
return;
}

String code = context.getAuthenticationSession()
.getAuthNote(TelegramRequiredActionConstants.TELEGRAM_SECURITY_CODE_ATTRIBUTE);
TelegramMessage message = TELEGRAM_SERVICE.getMessageFromSecurityCode(code);

if (message != null && message.getSubscriptionCode().equals(subscriptionCode)) {
String userId = message.getId();
String username = message.getUsername();

context.getUser().setSingleAttribute(TelegramRequiredActionConstants.TELEGRAM_ID_ATTRIBUTE, userId);
context.getUser().setSingleAttribute(TelegramRequiredActionConstants.TELEGRAM_USERNAME_ATTRIBUTE, username);
context.getUser().removeRequiredAction(TelegramRequiredActionConstants.PROVIDER_ID);

context.success();
return;
}

requiredActionChallenge(context);
}

@Override
public void close() {
// no-op
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* This file is part of Red Hat Single Sign-On workshop
* Copyright (C) 2022 Àngel Ollé Blázquez
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.olleb.action;

public final class TelegramRequiredActionConstants {

static final String PROVIDER_ID = "telegram-otp-required-action";
static final String DISPLAY_TEXT = "Telegram ID";

static final String TEMPLATE = "telegram-id-config.ftl";
static final String TELEGRAM_ID_ATTRIBUTE = "telegram_id";
static final String TELEGRAM_ID_PROPERTY = "telegram-id-label";
static final String TELEGRAM_USERNAME_ATTRIBUTE = "telegram_username";
static final String TELEGRAM_ROLLING_CODE_ATTRIBUTE = "telegram_code";
static final String TELEGRAM_SECURITY_CODE_ATTRIBUTE = "scode";
static final String TELEGRAM_MESSAGE_SECURITY_CODE_ATTRIBUTE = "msgcode";
static final int TELEGRAM_ROLLING_CODE_LENGTH = 6;

private TelegramRequiredActionConstants() {
// no-op constructor
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* This file is part of Red Hat Single Sign-On workshop
* Copyright (C) 2022 Àngel Ollé Blázquez
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.olleb.action;

import org.keycloak.Config.Scope;
import org.keycloak.authentication.RequiredActionFactory;
import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;

/**
* @author Àngel Ollé Blázquez
*/
public class TelegramRequiredActionFactory implements RequiredActionFactory {

private static final TelegramRequiredAction TELEGRAM_REQUIRED_ACTION = new TelegramRequiredAction();

@Override
public RequiredActionProvider create(KeycloakSession session) {
return TELEGRAM_REQUIRED_ACTION;
}

@Override
public String getId() {
return TelegramRequiredActionConstants.PROVIDER_ID;
}

@Override
public String getDisplayText() {
return TelegramRequiredActionConstants.DISPLAY_TEXT;
}

@Override
public void init(Scope config) {
// no-op
}

@Override
public void postInit(KeycloakSessionFactory factory) {
// no-op
}

@Override
public void close() {
// no-op
}

}
Loading

0 comments on commit 356b5f2

Please sign in to comment.