Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package io.theurl.framework.core;

import java.util.PriorityQueue;
import java.util.function.Predicate;

/**
* Utility class for finding a value in a priority queue based on a filter predicate.
* This class provides a method to search through a priority queue and return the first value that matches the given filter.
* If no matching value is found, it returns a specified default value.
*/
public class PriorityValueFinder {

/**
* Finds the first value in the priority queue that matches the given filter.
* If no matching value is found, returns the default value.
*
* @param values the priority queue to search
* @param filter the filter predicate to apply to each value
* @param defaultValue the value to return if no matching value is found
* @param <T> the type of values in the priority queue
* @return the first matching value or the default value if none is found
*/
public static <T> T find(PriorityQueue<T> values, Predicate<T> filter, T defaultValue) {

if (values == null || values.isEmpty()) {
throw new IllegalArgumentException("Values queue cannot be null or empty");
}

if (filter == null) {
throw new IllegalArgumentException("Filter queue cannot be null");
}

while (!values.isEmpty()) {
T value = values.poll();
if (filter.test(value)) {
return value;
}
}
return defaultValue;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.theurl.framework.security;

import java.util.Collections;
import java.util.Map;

/**
* Exception thrown for account-related errors during authentication or authorization processes, such as account not found, account locked, etc.
* This exception can be extended to provide more specific error types and include additional details as needed.
*/
@SuppressWarnings("unused")
public class AccountException extends RuntimeException {
private final String identity;

private final Map<String, Object> details = Collections.emptyMap();

public AccountException(String identity) {
this.identity = identity;
}

public AccountException(String identity, String message) {
super(message);
this.identity = identity;
}

public AccountException(String identity, String message, Throwable cause) {
super(message, cause);
this.identity = identity;
}

public String getIdentity() {
return identity;
}


public Map<String, Object> getDetails() {
return details;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.theurl.framework.security;

/**
* Exception thrown when an account is locked ant cannot be used for authentication or authorization processes. This typically occurs after multiple failed login attempts or due to administrative actions.
* The exception includes the identity of the locked account and can be extended to include additional details as needed.
*/
@SuppressWarnings("unused")
public class AccountLockedException extends AccountException {
public AccountLockedException(String identity) {
super(identity);
}

public AccountLockedException(String identity, String message) {
super(identity, message);
}

public AccountLockedException(String identity, String message, Throwable cause) {
super(identity, message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.theurl.framework.security;

import java.util.Collections;
import java.util.Map;

/**
* Base exception for credential-related errors, such as invalid credentials, expired credentials, etc.
* This exception can be extended to provide more specific error types and include additional details as needed.
*/
@SuppressWarnings("unused")
public class CredentialException extends RuntimeException {
private final String credential;
private final Map<String, Object> details = Collections.emptyMap();


public CredentialException(String credential) {
this.credential = credential;
}

public CredentialException(String credential, String message) {
super(message);
this.credential = credential;
}

public CredentialException(String credential, String message, Throwable cause) {
super(message, cause);
this.credential = credential;
}

public String getCredential() {
return credential;
}

public Map<String, Object> getDetails() {
return details;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.theurl.framework.security;

/**
* The UnauthorizedAccessException is thrown when an attempt is made to access a resource or perform an action without proper authentication or authorization.
* This exception indicates that the user does not have the necessary credentials or permissions to access the requested resource.
* It is typically used in scenarios where authentication is required but has not been provided, or when the provided credentials are invalid.
*/
@SuppressWarnings("unused")
public class UnauthorizedAccessException extends RuntimeException {
public UnauthorizedAccessException(String message) {
super(message);
}

public UnauthorizedAccessException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.theurl.framework.core;

import org.junit.jupiter.api.Test;

import java.util.PriorityQueue;

class PriorityValueFinderTests {
@Test
void testFind() {
// Add test cases for PriorityValueFinder.find method
PriorityQueue<Integer> values = new PriorityQueue<>();
values.offer(1);
values.offer(2);
values.offer(3);
values.offer(4);
values.offer(5);

Integer result = PriorityValueFinder.find(values, value -> value % 2 == 0, -1);
assert result == 2 : "Expected 2, but got " + result;
}
}
5 changes: 5 additions & 0 deletions identity/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@
<artifactId>spring-boot-starter-webflux-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.2.15</version>
</dependency>
</dependencies>

<build>
Expand Down
34 changes: 34 additions & 0 deletions identity/src/main/java/io/theurl/identity/SpringUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.theurl.identity;

import lombok.Getter;
import org.jspecify.annotations.NonNull;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@SuppressWarnings("unused")
@Component
public class SpringUtil implements ApplicationContextAware {
@Getter
private static ApplicationContext applicationContext;

@Override
public void setApplicationContext(@NonNull ApplicationContext context) throws BeansException {
if (applicationContext == null) {
applicationContext = context;
}
}

public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}

public static <T> T getBean(String beanName, Class<T> clazz) {
return applicationContext.getBean(beanName, clazz);
}

public static Object getBean(String beanName) {
return applicationContext.getBean(beanName);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package io.theurl.identity.external;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.stream.Collectors;

/**
* Provides common methods for external authentication providers, such as fetching user info and access tokens.
* Concrete providers (e.g., Google, Microsoft, GitHub) can extend this class to implement their specific authentication logic while reusing these common methods.
*/
@Slf4j
public abstract class BaseAuthProvider implements ExternalAuthProvider {

protected JsonNode getUserInfo(String token, String url) {
var request = HttpRequest.newBuilder()
.uri(java.net.URI.create(url))
.header("Accept", "application/json")
.header("User-Agent", "Linkyou")
.header("Authorization", "Bearer " + token)
.GET()
.build();

try (HttpClient client = HttpClient.newHttpClient()) {
HttpResponse<String> response = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.join();
if (response.statusCode() == 200) {
return readJson(response.body());
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}

protected String getToken(String url, Map<String, String> params, String paramsType) {
var form = params.entrySet().stream()
.map(e -> URLEncoder.encode(e.getKey(), StandardCharsets.UTF_8) + "=" + URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));

var builder = HttpRequest.newBuilder()
.header("Accept", "application/json")
.header("User-Agent", "Linkyou");

switch (paramsType) {
case "form":
builder.uri(java.net.URI.create(url))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(form));
break;
case "query":
builder.uri(URI.create(url + "?" + form))
.POST(HttpRequest.BodyPublishers.noBody());
break;
default:
throw new IllegalArgumentException("Unsupported params type: " + paramsType);
}

var request = builder.build();


try (HttpClient client = HttpClient.newHttpClient()) {
HttpResponse<String> response = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.join();
if (response.statusCode() == 200) {
var json = readJson(response.body());
return json.findValue("access_token").asText();
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}

protected JsonNode readJson(String json) {
try {
var objectMapper = new ObjectMapper();
return objectMapper.readTree(json);
} catch (Exception exception) {
throw new RuntimeException(exception);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.theurl.identity.external;

/**
* Defines the contract for external authentication providers.
* Implementations of this interface should handle the authentication process with external services.
*/
public interface ExternalAuthProvider {
/**
* Authenticates a user using the provided authentication code.
* @param authCode The authentication code received from the external service.
* @return The result of the authentication process encapsulated in an ExternalAuthResult object.
*/
ExternalAuthResult authenticate(String authCode);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.theurl.identity.external;

import lombok.Data;

@Data
public class ExternalAuthResult {
private String id;
private String username;
private String nickname;
private String email;
private String phone;
private String avatarUrl;
}
Loading
Loading