-
-
Notifications
You must be signed in to change notification settings - Fork 107
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added example on principal mapper usage
- Loading branch information
Showing
10 changed files
with
253 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
...es-parent/services-examples/src/main/java/io/scalecube/services/examples/auth/ApiKey.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package io.scalecube.services.examples.auth; | ||
|
||
import java.util.StringJoiner; | ||
|
||
public class ApiKey { | ||
|
||
private final String id; | ||
private final String permissions; | ||
|
||
public ApiKey(String id, String permissions) { | ||
this.id = id; | ||
this.permissions = permissions; | ||
} | ||
|
||
public String id() { | ||
return id; | ||
} | ||
|
||
public String permissions() { | ||
return permissions; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return new StringJoiner(", ", ApiKey.class.getSimpleName() + "[", "]") | ||
.add("id='" + id + "'") | ||
.add("permissions='" + permissions + "'") | ||
.toString(); | ||
} | ||
} |
156 changes: 156 additions & 0 deletions
156
...xamples/src/main/java/io/scalecube/services/examples/auth/PrincipalMapperAuthExample.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package io.scalecube.services.examples.auth; | ||
|
||
import io.scalecube.services.Microservices; | ||
import io.scalecube.services.ServiceEndpoint; | ||
import io.scalecube.services.ServiceInfo; | ||
import io.scalecube.services.auth.Authenticator; | ||
import io.scalecube.services.auth.CredentialsSupplier; | ||
import io.scalecube.services.discovery.ScalecubeServiceDiscovery; | ||
import io.scalecube.services.exceptions.UnauthorizedException; | ||
import io.scalecube.services.transport.rsocket.RSocketServiceTransport; | ||
import java.time.Duration; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.UUID; | ||
import reactor.core.publisher.Mono; | ||
|
||
public class PrincipalMapperAuthExample { | ||
|
||
/** | ||
* Main program. | ||
* | ||
* @param args arguments | ||
*/ | ||
public static void main(String[] args) { | ||
Microservices service = | ||
Microservices.builder() | ||
.discovery("service", ScalecubeServiceDiscovery::new) | ||
.transport(() -> new RSocketServiceTransport().authenticator(authenticator())) | ||
.services( | ||
ServiceInfo.fromServiceInstance(new SecuredServiceByApiKeyImpl()) | ||
.principalMapper(PrincipalMapperAuthExample::apiKeyPrincipalMapper) | ||
.build()) | ||
.services( | ||
ServiceInfo.fromServiceInstance(new SecuredServiceByUserProfileImpl()) | ||
.principalMapper(PrincipalMapperAuthExample::userProfilePrincipalMapper) | ||
.build()) | ||
.startAwait(); | ||
|
||
Microservices userProfileCaller = | ||
Microservices.builder() | ||
.discovery("caller", endpoint -> discovery(service, endpoint)) | ||
.transport(() -> new RSocketServiceTransport().credentialsSupplier(credsSupplier())) | ||
.startAwait(); | ||
|
||
String responseByUserProfile = | ||
userProfileCaller | ||
.call() | ||
.api(SecuredServiceByUserProfile.class) | ||
.hello(UUID.randomUUID().toString()) | ||
.block(Duration.ofSeconds(3)); | ||
|
||
System.err.println("### Received 'userProfileCaller' response: " + responseByUserProfile); | ||
|
||
Microservices apiKeyCaller = | ||
Microservices.builder() | ||
.discovery("caller", endpoint -> discovery(service, endpoint)) | ||
.transport(() -> new RSocketServiceTransport().credentialsSupplier(credsSupplier())) | ||
.startAwait(); | ||
|
||
String responseByApiKey = | ||
apiKeyCaller | ||
.call() | ||
.api(SecuredServiceByApiKey.class) | ||
.hello(UUID.randomUUID().toString()) | ||
.block(Duration.ofSeconds(3)); | ||
|
||
System.err.println("### Received 'apiKeyCaller' response: " + responseByApiKey); | ||
} | ||
|
||
private static Authenticator<Map<String, String>> authenticator() { | ||
return headers -> { | ||
String credsType = headers.get("type"); // credentials type | ||
|
||
if (SecuredServiceByUserProfile.class.getName().equals(credsType)) { | ||
return authenticateUserProfile(headers); | ||
} | ||
if (SecuredServiceByApiKey.class.getName().equals(credsType)) { | ||
return authenticateApiKey(headers); | ||
} | ||
throw new IllegalArgumentException( | ||
"[authenticator] not expected namespace: '" + credsType + "'"); | ||
}; | ||
} | ||
|
||
private static CredentialsSupplier credsSupplier() { | ||
return service -> { | ||
String namespace = service.namespace(); // decide by service | ||
|
||
if (SecuredServiceByUserProfile.class.getName().equals(namespace)) { | ||
return userProfileCredentials(); | ||
} | ||
if (SecuredServiceByApiKey.class.getName().equals(namespace)) { | ||
return apiKeyCredentials(); | ||
} | ||
throw new IllegalArgumentException( | ||
"[credentialsSupplier] not expected namespace: '" + namespace + "'"); | ||
}; | ||
} | ||
|
||
private static Mono<Map<String, String>> authenticateUserProfile(Map<String, String> headers) { | ||
String username = headers.get("username"); | ||
String password = headers.get("password"); | ||
|
||
if ("Alice".equals(username) && "qwerty".equals(password)) { | ||
HashMap<String, String> authData = new HashMap<>(); | ||
authData.put("name", "Alice"); | ||
authData.put("role", "ADMIN"); | ||
return Mono.just(authData); | ||
} | ||
|
||
return Mono.error( | ||
new UnauthorizedException("Authentication failed (username or password incorrect)")); | ||
} | ||
|
||
private static Mono<Map<String, String>> authenticateApiKey(Map<String, String> headers) { | ||
String apiKey = headers.get("apiKey"); | ||
|
||
if ("jasds8fjasdfjasd89fa4k9rjn7ahdfasduf".equals(apiKey)) { | ||
HashMap<String, String> authData = new HashMap<>(); | ||
authData.put("id", "jasds8fjasdfjasd89fa4k9rjn7ahdfasduf"); | ||
authData.put("permissions", "OPERATIONS:EVENTS:ACTIONS"); | ||
return Mono.just(authData); | ||
} | ||
|
||
return Mono.error(new UnauthorizedException("Authentication failed (apiKey incorrect)")); | ||
} | ||
|
||
private static Mono<Map<String, String>> userProfileCredentials() { | ||
HashMap<String, String> creds = new HashMap<>(); | ||
creds.put("type", SecuredServiceByUserProfile.class.getName()); | ||
creds.put("username", "Alice"); | ||
creds.put("password", "qwerty"); | ||
return Mono.just(creds); | ||
} | ||
|
||
private static Mono<Map<String, String>> apiKeyCredentials() { | ||
HashMap<String, String> creds = new HashMap<>(); | ||
creds.put("type", SecuredServiceByApiKey.class.getName()); | ||
creds.put("apiKey", "jasds8fjasdfjasd89fa4k9rjn7ahdfasduf"); | ||
return Mono.just(creds); | ||
} | ||
|
||
private static UserProfile userProfilePrincipalMapper(Map<String, String> authData) { | ||
return new UserProfile(authData.get("name"), authData.get("role")); | ||
} | ||
|
||
private static ApiKey apiKeyPrincipalMapper(Map<String, String> authData) { | ||
return new ApiKey(authData.get("id"), authData.get("permissions")); | ||
} | ||
|
||
private static ScalecubeServiceDiscovery discovery( | ||
Microservices service, ServiceEndpoint endpoint) { | ||
return new ScalecubeServiceDiscovery(endpoint) | ||
.membership(opts -> opts.seedMembers(service.discovery("service").address())); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
...xamples/src/main/java/io/scalecube/services/examples/auth/SecuredServiceByApiKeyImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package io.scalecube.services.examples.auth; | ||
|
||
import io.scalecube.services.auth.MonoAuthUtil; | ||
import io.scalecube.services.exceptions.ForbiddenException; | ||
import reactor.core.publisher.Mono; | ||
|
||
public class SecuredServiceByApiKeyImpl implements SecuredServiceByApiKey { | ||
|
||
@Override | ||
public Mono<String> hello(String name) { | ||
return MonoAuthUtil.deferWithPrincipal(ApiKey.class) | ||
.flatMap( | ||
apiKey -> { | ||
checkPermissions(apiKey); | ||
return Mono.just("Hello, name=" + name + " (apiKey=" + apiKey + ")"); | ||
}); | ||
} | ||
|
||
private void checkPermissions(ApiKey apiKey) { | ||
if (!apiKey.permissions().equals("OPERATIONS:EVENTS:ACTIONS")) { | ||
throw new ForbiddenException("Forbidden"); | ||
} | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
...amples/src/main/java/io/scalecube/services/examples/auth/SecuredServiceByUserProfile.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package io.scalecube.services.examples.auth; | ||
|
||
import io.scalecube.services.annotations.Service; | ||
import io.scalecube.services.annotations.ServiceMethod; | ||
import io.scalecube.services.auth.Secured; | ||
import reactor.core.publisher.Mono; | ||
|
||
@Secured | ||
@Service | ||
public interface SecuredServiceByUserProfile { | ||
|
||
@ServiceMethod | ||
Mono<String> hello(String name); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters