Which keycloak endpoints should I use? - keycloak-rest-api

I'd like to use keycloak to role base access control. I don't understand very well how key cloak works so far.
I can get an access_token by following command.
curl \
-d "username=admin" \
-d "password=MY_PASSWORD" \
-d "client_id=MY_CLIENT" \
-d "grant_type=password" \
"https://MY_DOMAIN/auth/realms/MY_REALM/protocol/openid-connect/token"
My WebAPI is made by rocket (rust). So there is no prepared keycloak adapter.
I think I need check the given token by client on each access to my WebAPI.
This is my handler.
///handler
#[get("/")]
pub fn myhandler(_key:ApiKey,conn: DbConn) -> Option<JsonValue>
pub struct ApiKey(String);
impl<'a, 'r> FromRequest<'a, 'r> for ApiKey {
type Error = ApiKeyError;
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
let keys: Vec<_> = request.headers().get("x-api-key").collect();
//ask keycloak server that keys[0] is valid access_token??
}
}
I found there are some endpoints on keycloak. Which should I use?
{
// some claims are expected here
// these are the main claims in the discovery document about Authorization Services endpoints location
"token_endpoint": "http://${host}:${port}/auth/realms/${realm}/protocol/openid-connect/token",
"token_introspection_endpoint": "http://${host}:${port}/auth/realms/${realm}/protocol/openid-connect/token/introspect",
"resource_registration_endpoint": "http://${host}:${port}/auth/realms/${realm}/authz/protection/resource_set",
"permission_endpoint": "http://${host}:${port}/auth/realms/${realm}/authz/protection/permission",
"policy_endpoint": "http://${host}:${port}/auth/realms/${realm}/authz/protection/uma-policy"
}

Related

How to create an overload API or function in Actix-web?

Background
I need to create a couple of endpoints for an API service project. These API can accept encrypted and un-encrypted one (for development). Both parameters then are passed into a same function. For example:
/api/movie/get with encrypted parameter (for production)
/dev/movie/get with un-encrypted parameter (for development)
Current implementation in actix_web
I use actix_web and routes (not macro) to provide path routing. Modified from the sample code below
use actix_web::{web, App, HttpServer, Responder};
async fn index() -> impl Responder {
"Hello world!"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new().service(
// prefixes all resources and routes attached to it...
web::scope("/app")
// ...so this handles requests for `GET /app/index.html`
.route("/index.html", web::get().to(index)),
)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
For each API endpoint, I had to create 2 functions, such as:
/// for encrypted one
/// PATH: /api/movie/get
pub async fn handle_get_movie(
app_data: Data<AppState>,
Json(payload): Json<EncryptedPayload>,
) -> Result<impl Responder, Error> {
// decrypt the content
// this is the only difference between un-encrypted and encrypted endpoint
let params = payload
.unload::<GetMovieInf>()
.map_err(|_| MainError::Malformatted)?;
// other codes here....
Ok(Json(ReplyInf {ok: true}))
}
/// for un-encrypted one
/// PATH: /dev/movie/get
pub async fn handle_get_movie(
app_data: Data<AppState>,
Json(payload): Json<UnencryptedPayload>,
) -> Result<impl Responder, Error> {
// other codes here. These codes are exactly identical with the above function...
Ok(Json(ReplyInf {ok: true}))
}
Questions
Since both functions are similar, does it possible to combine them both into a single function ? a function "overload" maybe ?
The problem is this line Json(payload): Json<UnencryptedPayload> in the parameter function. I tried to use generics like Json<T>. this doesn't work.
I can use the environment variable to control which should be active (EncryptedPayload or UnencryptedPayload). I can use one path for each endpoint (eg: /api/movie/get) and don't have to write the same functionality twice.

Having a Solana program mint tokens while a user pays the fee

I'm trying to have a Solana Anchor program mint tokens to a user's token account (so the program or its PDA should be the authority for the mint), while the owner of the token account that's receiving the tokens should pay the fee.
This is what I have right now:
use anchor_lang::prelude::*;
use anchor_spl::token;
use anchor_spl::token::{Token, MintTo};
declare_id!("EcFTDXxknt3vRBi1pVZYN7SjZLcbHjJRAmCmjZ7Js3fd");
//token HKTPz1skkSNAC8TXJiYvPyYtMCQDHtAheCL4FrBArCDJ
//program EcFTDXxknt3vRBi1pVZYN7SjZLcbHjJRAmCmjZ7Js3fd
//account GHQMHrt4j8i6bBaVhpMCLP8uoFfWUrLZsQtWCWqSJKA6
#[program]
pub mod solana_ubi {
use super::*;
pub fn mint_token(ctx: Context<MintToken>,) -> Result<()> {
// Create the MintTo struct for our context
let cpi_accounts = MintTo {
mint: ctx.accounts.mint.to_account_info(),
to: ctx.accounts.token_account.to_account_info(),
authority: ctx.accounts.owner.to_account_info(),
};
//need to make pda. see cookbook: sign a transaction with a pda.
let cpi_program = ctx.accounts.token_program.to_account_info();
// Create the CpiContext we need for the request
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
// Execute anchor's helper function to mint tokens
token::mint_to(cpi_ctx, 10_000_000_000)?;
Ok(())
}
}
#[derive(Accounts)]
pub struct MintToken<'info> {
/// CHECK: This is the token that we want to mint
#[account()]
pub mint: UncheckedAccount<'info>,
pub token_program: Program<'info, Token>,
/// CHECK: This is the token account that we want to mint tokens to
#[account(mut)]
pub token_account: UncheckedAccount<'info>,
/// CHECK: ugh
#[account(signer)]
pub owner: AccountInfo<'info>,
}
this does mint the tokens, if I pass the mint's authority (any account holding SOL) as owner. Again for clarity: I want this program to be the mint authority for the token, but the user receiving the tokens to pay the fee.
how should I change the code to make it work as described above? Please provide a code answer.

How to design an Axum server with test-friendliness in mind?

When I tried to build an application with axum, I failed to separate the framework from my handler. With Go, the classic way is define an Interface, implement it and register the handler to framework. In this way, it's easy to provide a mock handler to test with. However, I couldn't make it work with Axum. I defined a trait just like above, but it wouldn't compile:
use std::net::ToSocketAddrs;
use std::sync::{Arc, Mutex};
use serde_derive::{Serialize, Deserialize};
use serde_json::json;
use axum::{Server, Router, Json};
use axum::extract::Extension;
use axum::routing::BoxRoute;
use axum::handler::get;
#[tokio::main]
async fn main() {
let app = new_router(
Foo{}
);
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
trait Handler {
fn get(&self, get: GetRequest) -> Result<GetResponse, String>;
}
struct Foo {}
impl Handler for Foo {
fn get(&self, req: GetRequest) -> Result<GetResponse, String> {
Ok(GetResponse{ message: "It works.".to_owned()})
}
}
fn new_router<T:Handler>(handler: T) -> Router<BoxRoute> {
Router::new()
.route("/", get(helper))
.boxed()
}
fn helper<T:Handler>(
Extension(mut handler): Extension<T>,
Json(req): Json<GetRequest>
) -> Json<GetResponse> {
Json(handler.get(req).unwrap())
}
#[derive(Debug, Serialize, Deserialize)]
struct GetRequest {
// omited
}
#[derive(Debug, Serialize, Deserialize)]
struct GetResponse {
message: String
// omited
}
error[E0599]: the method `boxed` exists for struct `Router<axum::routing::Layered<Trace<axum::routing::Layered<AddExtension<Nested<Router<BoxRoute>, Route<axum::handler::OnMethod<fn() -> impl Future {direct}, _, (), EmptyRouter>, EmptyRouter<_>>>, T>>, SharedClassifier<ServerErrorsAsFailures>>>>`, but its trait bounds were not satisfied
--> src/router.rs:25:10
|
25 | .boxed()
| ^^^^^ method cannot be called on `Router<axum::routing::Layered<Trace<axum::routing::Layered<AddExtension<Nested<Router<BoxRoute>, Route<axum::handler::OnMethod<fn() -> impl Future {direct}, _, (), EmptyRouter>, EmptyRouter<_>>>, T>>, SharedClassifier<ServerErrorsAsFailures>>>>` due to unsatisfied trait bounds
|
::: /Users/lebrancebw/.cargo/registry/src/github.com-1ecc6299db9ec823/axum-0.2.5/src/routing/mod.rs:876:1
|
876 | pub struct Layered<S> {
| --------------------- doesn't satisfy `<_ as tower_service::Service<Request<_>>>::Error = _`
|
= note: the following trait bounds were not satisfied:
`<axum::routing::Layered<Trace<axum::routing::Layered<AddExtension<Nested<Router<BoxRoute>, Route<axum::handler::OnMethod<fn() -> impl Future {direct}, _, (), EmptyRouter>, EmptyRouter<_>>>, T>>, SharedClassifier<ServerErrorsAsFailures>>> as tower_service::Service<Request<_>>>::Error = _`
I guess the key point is my design is apparently not "rustic". Is there a way to structure an Axum project that lends itself to testing easily?
The question is what you want to test. I will assume that you have some core logic and an HTTP layer. And you want to make sure that:
requests are routed correctly;
requests are parsed correctly;
the core logic is called with the expected parameters;
and return values from the core are correctly formatted into HTTP responses.
To test it you want to spawn an instance of the server with the core logic mocked out.
#lukemathwalker in his blog and book "Zero To Production In Rust" has a very nice description of how to spawn an app for testing through the actual TCP port. It is written for Actix-Web, but the idea applies to Axum as well.
You should not use axum::Server::bind, but rather use axum::Server::from_tcp to pass it a std::net::TcpListner which allows you to spawn a test server on any available port using `TcpListener::bind("127.0.0.1:0").
To make the core logic injectable (and mockable) I declare it as a struct and implement all the business methods on it. Like this:
pub struct Core {
public_url: Url,
secret_key: String,
storage: Storage,
client: SomeThirdPartyClient,
}
impl Core {
pub async fn create_something(
&self,
new_something: NewSomething,
) -> Result<Something, BusinessErr> {
...
}
With all these pieces you can write a function to start the server:
pub async fn run(listener: TcpListener, core: Core)
This function should encapsulate things like routing configuration, server logging configuration and so on.
Core can be provided to handlers using Extension Layer mechanism like this:
...
let shared_core = Arc::new(core);
...
let app = Router::new()
.route("/something", post(post_something))
...
.layer(AddExtensionLayer::new(shared_core));
Which in a handler can be declared in parameter list using extension extractor:
async fn post_something(
Extension(core): Extension<Arc<Core>>,
Json(new_something): Json<NewSomething>,
) -> impl IntoResponse {
core
.create_something(new_something)
.await
}
Axum examples contain one on error handling and dependency injection. You can check it here.
Last but not least, now you can mock Core out with a library like mockall, write spawn_app function that would return host and port where the server is run, run some requests against it and do assertions.
The video from Bogdan at Let's Get Rusty channel provides a good start with mockall.
I will be happy to provide more details if you feel something is missing from the answer.

How can I return a configured App in Actix-Web?

I'm using actix-web to create a httpserver with state/data embedded in it. But vscode show me that the create_app function has wrong arguments in its return value type definition App<AppState>:
pub struct App<T, B>
wrong number of type arguments: expected 2, found 1
expected 2 type argumentsrustc(E0107)
app.rs:
use crate::api;
use crate::model::DbExecutor;
use actix::prelude::Addr;
use actix_web::{error, http::Method, middleware::Logger, web, App, HttpResponse};
pub struct AppState {
pub db: Addr<DbExecutor>,
}
pub fn create_app(db: Addr<DbExecutor>) -> App<AppState> {
App::new().data(AppState { db }).service(
web::resource("/notes/").route(web::get().to(api::notes))
);
}
main.rs:
fn main() {
HttpServer::new(move || app::create_app(addr.clone()))
.bind("127.0.0.1:3000")
.expect("Can not bind to '127.0.0.1:3000'")
.start();
}
As return type of "service" method is "Self" which is type actix_web::App, I tried modify return type to App (without generic parameter) but still got error, what should I do?
First, App takes two generic type arguments, App<AppEntry, Body>, you've only given one.
Second, AppState is not AppEntry.
Third, instantiating App outside actix-web is hard, if not impossible, as the types you need from actix-web are not public.
Instead, you should use configure to achieve the same, here is a simplified example:
use actix_web::web::{Data, ServiceConfig};
use actix_web::{web, App, HttpResponse, HttpServer};
fn main() {
let db = String::from("simplified example");
HttpServer::new(move || App::new().configure(config_app(db.clone())))
.bind("127.0.0.1:3000")
.expect("Can not bind to '127.0.0.1:3000'")
.run()
.unwrap();
}
fn config_app(db: String) -> Box<Fn(&mut ServiceConfig)> {
Box::new(move |cfg: &mut ServiceConfig| {
cfg.data(db.clone())
.service(web::resource("/notes").route(web::get().to(notes)));
})
}
fn notes(db: Data<String>) -> HttpResponse {
HttpResponse::Ok().body(["notes from ", &db].concat())
}
Read more about ServiceConfig in the api documentation.

Application state access from an Actix web application middleware

I have a simple middleware intended to access the application's global state to perform validation of the authentication token:
use actix_web;
use actix_web::HttpMessage;
pub struct Authenticator;
impl<S> actix_web::middleware::Middleware<S> for Authenticator {
fn start(
&self,
request: &mut actix_web::HttpRequest<S>,
) -> actix_web::Result<actix_web::middleware::Started> {
//let _state = request.state() as &::application::State;
match request.headers().get("Authentication") {
Some(_) => Ok(actix_web::middleware::Started::Done),
None => Err(::view::error(
"No authentication header provided",
actix_web::http::StatusCode::FORBIDDEN,
)),
}
}
}
The commented string shows how I tried to get the state. I have tried many ways actually. What is the best way of doing such stuff?
I thought about adding a reference to needed data (e.g. Arc'd RwLock) to the Authenticator structure and constructing it with the reference when I register my middlewares.
I am still not good with trait stuff, but there must be a clean way of casting the S type to my application-defined State structure:
pub struct State {
pub database: actix::Addr<actix::Syn, ::database::Actor>,
pub cache: ::std::sync::Arc<::cache::Cache>,
pub sessions: ::std::sync::Arc<::session::Storage>,
}
Use your state instead of S:
impl actix_web::middleware::Middleware<::Application::State> for Authenticator {
}
By the way, middleware can have state too.

Resources