Skip to content

Commit

Permalink
feat: initial commit (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
sverrehu authored Oct 31, 2023
1 parent b1a6e11 commit a43d317
Show file tree
Hide file tree
Showing 12 changed files with 384 additions and 2 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @statnett/k3a
4 changes: 4 additions & 0 deletions .github/renovate.json5
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>statnett/renovate-config"],
}
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
name: CI
on:
pull_request: {}

permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- run: mvn --batch-mode verify
17 changes: 17 additions & 0 deletions .github/workflows/lint-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
name: Lint PR
on:
pull_request_target:
types:
- opened
- edited
- synchronize

permissions:
contents: read
jobs:
trigger:
uses: statnett/github-workflows/.github/workflows/lint-pr.yaml@main
permissions:
pull-requests: write
statuses: write
19 changes: 19 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
name: Release Please
on:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read
jobs:
trigger:
uses: statnett/github-workflows/.github/workflows/release-please.yaml@main
with:
release-type: maven
secrets: inherit
permissions:
contents: write
pull-requests: write
21 changes: 21 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Publish package
on:
release:
types: [created]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
- name: Build package
run: mvn --batch-mode verify
- name: Push GitHub release artifact
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
with:
files: target/*.jar
20 changes: 20 additions & 0 deletions .github/workflows/scorecard.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: Scorecard supply-chain security
on:
branch_protection_rule:
schedule:
- cron: "20 7 * * 2"
push:
branches:
- main

permissions:
contents: read
jobs:
trigger:
uses: statnett/github-workflows/.github/workflows/scorecard.yaml@main
permissions:
security-events: write
id-token: write
contents: read
actions: read
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*~
.idea/
target/
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## 1.0.0 (2021-12-01)

* Initial, internal version.
52 changes: 50 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,50 @@
# k3a-prompt-login-module
A JAAS LoginModule intended for use with Kafka clients, asking for the user's password
# prompt-login-module

A JAAS `LoginModule` intended for use with Kafka clients. When using
this module, it is not necessary to embed/store the password in the
config. Instead, the user will be prompted for the password (and
optionally, the username, if not present in the config).

## How to use it

Make the jar file available to the Kafka tools by installing it in the
shared jar directory of the tools. This will typically be a
`share/java/kafka` sub-directory under the base directory of the tools
installation. If tools are installed in `/opt/kafka`, this command
will install the jar:

```shell
KAFKA_BASEDIR=/opt/kafka
PROMPT_LOGIN_MODULE_VERSION=1.1.0
curl -sSfo ${KAFKA_BASEDIR}/share/java/kafka/prompt-login-module.jar \
https://github.com/statnett/k3a-prompt-login-module/releases/download/v${PROMPT_LOGIN_MODULE_VERSION}/k3a-prompt-login-module-${PROMPT_LOGIN_MODULE_VERSION}.jar
```

The latest version may be found [on GitHub](https://github.com/statnett/k3a-prompt-login-module).

Reference the `io.statnett.k3a.authz.PromptLoginModule` class in
the JAAS section of your Kafka properties file:

```properties
sasl.mechanism=PLAIN
security.protocol=SASL_SSL
sasl.jaas.config=io.statnett.k3a.authz.PromptLoginModule required \
username="my.user.name";
```

Run a Kafka command, referencing the Kafka properties, and note how it
now asks for a password from the console (If the JAAS config lacks a
username, it will be prompted for too):

```shell
$ kafka-topics --bootstrap-server broker:9092 --command-config kafka.properties --list
Password:
__consumer_offsets
__transaction_state
app_strm_demo_topic
:
```

If input or output is redirected, the password will not be read from
the console, but instead a dialog window will open asking for the
credentials.
60 changes: 60 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?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>io.statnett.k3a</groupId>
<artifactId>k3a-prompt-login-module</artifactId>
<packaging>jar</packaging>
<version>1.0.5-SNAPSHOT</version>

<name>${project.groupId}:${project.artifactId}</name>
<description>A JAAS LoginModule intended for use with Kafka clients, asking for the user's password</description>
<url>https://github.com/statnett/k3a-prompt-login-module</url>

<licenses>
<license>
<name>MIT License</name>
<url>https://opensource.org/license/mit/</url>
</license>
</licenses>

<scm>
<connection>scm:git:git://github.com/statnett/k3a-prompt-login-module.git</connection>
<developerConnection>scm:git:ssh://github.com:statnett/k3a-prompt-login-module.git</developerConnection>
<url>https://github.com/statnett/k3a-prompt-login-module/tree/main</url>
</scm>

<properties>
<java.version>1.8</java.version>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kafka.version>3.6.0</kafka.version>
</properties>

<dependencies>

<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>${kafka.version}</version>
<scope>provided</scope>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>

</project>
166 changes: 166 additions & 0 deletions src/main/java/io/statnett/k3a/authz/PromptLoginModule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package io.statnett.k3a.authz;

import org.apache.kafka.common.security.plain.internals.PlainSaslServerProvider;

import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.spi.LoginModule;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.Console;
import java.util.Map;

/**
* A JAAS LoginModule for Kafka that will look for username and password configuration values,
* and prompt the user for any value that is missing.
*
* If a Java "Console" is available, ie. stdin/stdout is not redirected, the input
* will be read from the console. Otherwise, this class will try to disable
* "headless mode", and display a Swing dialog.
*/
public final class PromptLoginModule
implements LoginModule {

private static final String USERNAME_CONFIG = "username";
private static final String PASSWORD_CONFIG = "password";
private Subject subject;
private String username;
private String password;

static {
System.setProperty("java.awt.headless", "false");
PlainSaslServerProvider.initialize();
}

@Override
public void initialize(final Subject subject, final CallbackHandler callbackHandler, final Map<String, ?> sharedState, final Map<String, ?> options) {
this.subject = subject;
username = (String) options.get(USERNAME_CONFIG);
password = (String) options.get(PASSWORD_CONFIG);
promoteNonNulls();
}

@Override
public boolean login() {
final Console console = System.console();
if (console != null) {
inputFromConsole(console);
} else {
inputFromWindow();
}
promoteNonNulls();
return true;
}

private void inputFromConsole(final Console console) {
if (username == null) {
username = console.readLine("Username: ");
}
if (password == null) {
final char[] chars = console.readPassword("Password: ");
if (chars != null) {
password = new String(chars);
}
}
}

private void inputFromWindow() {
new UsernamePasswordDialog();
}

@Override
public boolean logout() {
return true;
}

@Override
public boolean commit() {
return true;
}

@Override
public boolean abort() {
return false;
}

private void promoteNonNulls() {
if (username != null) {
subject.getPublicCredentials().add(username);
}
if (password != null) {
subject.getPrivateCredentials().add(password);
}
}

private final class UsernamePasswordDialog
extends JDialog {

UsernamePasswordDialog() {
super((Frame) null, "JAAS Login", true);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
final JPanel panel = new JPanel(new GridBagLayout());
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
final GridBagConstraints constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.insets = new Insets(2, 5, 2, 5);
final JTextField usernameInput = new JTextField(username == null ? "" : username, 20);
addInput(panel, constraints, 0, "Username", usernameInput);
final JPasswordField passwordInput = new JPasswordField(password == null ? "" : password, 20);
addInput(panel, constraints, 1, "Password", passwordInput);

final JButton submitButton = new JButton("Submit");
submitButton.addActionListener(e -> {
username = usernameInput.getText();
password = new String(passwordInput.getPassword());
dispose();
});
getRootPane().setDefaultButton(submitButton);
final JPanel buttonPanel = new JPanel();
buttonPanel.add(submitButton);

getContentPane().add(panel, BorderLayout.CENTER);
getContentPane().add(buttonPanel, BorderLayout.PAGE_END);

pack();
setResizable(false);
setLocation((Toolkit.getDefaultToolkit().getScreenSize().width - getWidth()) / 2,
(Toolkit.getDefaultToolkit().getScreenSize().height - getHeight()) / 2);
addWindowFocusListener(new WindowAdapter() {
@Override
public void windowGainedFocus(final WindowEvent e) {
if (username != null) {
passwordInput.requestFocusInWindow();
}
}
});
setVisible(true);
}

private void addInput(final JPanel panel, final GridBagConstraints constraints, final int y, final String label, final Component input) {
constraints.gridy = y;
constraints.gridx = 0;
constraints.gridwidth = 1;
panel.add(new JLabel(label), constraints);
constraints.gridx = 1;
constraints.gridwidth = 2;
panel.add(input, constraints);
}

}

}

0 comments on commit a43d317

Please sign in to comment.