What is the proper way to register policy-enforcer configuration in ktor - keycloak

In spring boot, adding keycloak adaptor and "keycloak.policy-enforcer-config.claimInformationPointConfig.claims[claim-from-uri]={ request.uri }" to application.properties
file, I am able to receive "claim-from-uri" in keycloak javascript policy. But using similar settings in ktor does not work.
I have added "policy-enforcer" to keyclaok.json but "claim-from-uri" property is always null in javascript policy in keycloak.
// js policy in keycloak
var context = $evaluation.getContext();
var attributes = context.getAttributes();
var realm = $evaluation.getRealm();
var httpUri = attributes.getValue('http.uri');
var claimFromUri = attributes.getValue('claim-from-uri');
My usecase is to get the claim from the URI and then use it to get the policy from the keycloak server.
Below is my keycloak.json file.
{
"realm": "test-realm",
"auth-server-url": "https://localhost:8080/auth",
"ssl-required": "none",
"resource": "api-resource",
"public-client": true,
"policy-enforcer": {
"enforcement-mode": "ENFORCING",
"paths": [
{
"path": "/api/*",
"claim-information-point": {
"claims": {
"claim-from-uri": "{request.uri}"
}
},
"methods": [
{
"method": "GET",
"scopes": ["get", "GET"]
},
{
"method": "POST",
"scopes": ["post", "POST"]
}
]
}
]
}
}
val keycloakProvider = OAuthServerSettings.OAuth2ServerSettings(
name = "keycloak",
authorizeUrl = "https://localhost:8082/auth/realms/test-realm/protocol/openid-connect/auth",
accessTokenUrl = "https://localhost:8082/auth/realms/test-realm/protocol/openid-connect/token",
clientId = "test-realm-backend",
clientSecret = "client-secret",
accessTokenRequiresBasicAuth = false,
requestMethod = HttpMethod.Post,
)
//application setup
install(Authentication) {
oauth("keycloak") {
client = HttpClient(Apache)
providerLookup = { keycloakProvider }
urlProvider = { "http://localhost:8080/callback" }
}
}
// routing
authenticate("keycloak") {
get("/api/{name}") {
val principal: OAuthAccessTokenResponse.OAuth2? = call.authentication.principal()
call.sessions.set(UserSession("Bearer $principal?.accessToken.toString()"))
val name = call.parameters["name"] ?: "name missing in parameter"
val user = User(name)
call.respond(user)
}
}

Related

why mongoose populate() request does not work?

I try to populate some data from other collection to an other collection.i had googled the search and also i follow the tutorial step by step but the population had fail.any help is appreciate friends. this is the code:
router.get("/", passport.authenticate("jwt", {session: false}), (req, res)=> {
const errors = {};
Profile.findOne({user: req.user.id})
.then(profile => {
if (!profile) {
errors.noprofile = "there is no profile for this user"
return res.status(404).json(errors);
}
res.json(profile);
}).catch(err=> res.status(404).json(err))
});
// #route POST api/profile
//#desc Create or edit user profile
//#access Private
router.get("/", passport.authenticate("jwt", {session: false}), (req, res)=> {
const {errors, isValid} = validateProfileInput(req.body);
//Check validation
if(!isValid) {
return res.status(400).json(errors);
}
// Get profile data
const profileData = {};
profileData.user = req.user.id;
if(req.body.handle) {
profileData.handle = req.body.handle
};
if(req.body.company) {
profileData.company = req.body.company
};
if(req.body.website) {
profileData.website = req.body.website
};
if(req.body.location) {
profileData.location = req.body.location
};
if(req.body.status) {
profileData.status = req.body.status
};
if(typeof req.body.skills !== 'undefined') {
profileData.skills = req.body.skills.split(',');
}
//social
profileData.social = {};
if(req.body.youtube) {
profileData.social.youtube = req.body.youtube
};
if(req.body.twitter) {
profileData.social.twitter = req.body.twitter
};
if(req.body.facebook) {
profileData.social.facebook = req.body.facebook
};
if(req.body.instagram) {
profileData.social.instagram = req.body.instagram
};
Profile.findOne({user: req.user.id})
.populate(
"user",
["name, avatar"]
)
this is the result that I get from the postman :
"_id": "62ee1058ceb295ccdfedffce",
"user": "62e6825958870d3db69d2da5",
"handle": "pablo",
"status": "developper",
"skills": [
"design web"
],
and the correct result must be :
"_id": "62ee1058ceb295ccdfedffce",
"user": {"_id": "62e6825958870d3db69d2da5",
"name": "pablo",
"avatar": "//www.gravatar.com/avatar/1ffsrenbdgeajks-ghsdereys1dkkdhddbc"
}
"handle": "pablo",
"status": "developper",
"skills": [
"design web"
],

401 error accessing Cloudant from IBM Cloud Function

I have created a Cloudant service and credentials (1) along with a database and a couple of documents. I want to access that database from an IBM Cloud Function so created a function to do that. I tested the function by adding the copied and pasted credentials from (1) into the "Invoke with credentials" box (along with the and the db name and a valid docid). The function seems to take all that info correctly but I get a 401 error... any help would be massively appreciated!
{
"error": {
"description": "couch returned 401",
"errid": "non_200",
"error": "unauthorized",
"headers": {
"cache-control": "must-revalidate",
"content-type": "application/json",
"date": "Sat, 30 May 2020 16:37:25 GMT",
"statusCode": 401,
"strict-transport-security": "max-age=31536000",
"uri": "xxxxxxx",
"via": "1.1 lb1.bm-cc-eu-gb-04 (Glum/1.89.6)",
"www-authenticate": "Basic realm=\"Cloudant Private Database\"",
"x-cloudant-action": "cloudantnosqldb.any-document.read",
"x-cloudant-backend": "bm-cc-eu-gb-04",
"x-cloudant-request-class": "lookup",
"x-content-type-options": "nosniff",
"x-couch-request-id": "03e7fe91bb"
},
"message": "_reader access is required for this request",
"name": "Error",
"reason": "_reader access is required for this request",
"request": {
"headers": {
"accept": "application/json",
"content-type": "application/json"
},
"method": "GET",
"uri": "xxxxx"
},
"scope": "couch",
"stack": "Error: _reader access is required for this request\n at Object.clientCallback (/node_modules/#cloudant/cloudant/node_modules/nano/lib/nano.js:151:15)\n at Request._callback (/node_modules/#cloudant/cloudant/lib/clientutils.js:162:11)\n at Request.self.callback (/node_modules/request/request.js:185:22)\n at Request.emit (events.js:198:13)\n at Request.self._source.emit (/node_modules/#cloudant/cloudant/lib/eventrelay.js:78:21)\n at Request.<anonymous> (/node_modules/request/request.js:1161:10)\n at Request.emit (events.js:198:13)\n at Request.self._source.emit (/node_modules/#cloudant/cloudant/lib/eventrelay.js:78:21)\n at IncomingMessage.<anonymous> (/node_modules/request/request.js:1083:12)",
"statusCode": 401
}
}
The function code is as follows:
/**
* Read a document in Cloudant database:
* https://docs.cloudant.com/document.html#read
**/
function main(message) {
var cloudantOrError = getCloudantAccount(message);
if (typeof cloudantOrError !== 'object') {
return Promise.reject(cloudantOrError);
}
var cloudant = cloudantOrError;
var dbName = message.dbname;
var docId = message.docid || message.id;
var params = {};
if (!dbName) {
return Promise.reject('dbname is required.');
}
if (!docId) {
return Promise.reject('docid is required.');
}
var cloudantDb = cloudant.use(dbName);
if (typeof message.params === 'object') {
params = message.params;
} else if (typeof message.params === 'string') {
try {
params = JSON.parse(message.params);
} catch (e) {
return Promise.reject('params field cannot be parsed. Ensure it is valid JSON.');
}
}
return readDocument(cloudantDb, docId, params);
}
function readDocument(cloudantDb, docId, params) {
return new Promise(function (resolve, reject) {
cloudantDb.get(docId, params, function (error, response) {
if (!error) {
resolve(response);
} else {
console.error('error', error);
reject(error);
}
});
});
}
function getCloudantAccount(params) {
var Cloudant = require('#cloudant/cloudant');
var cloudant;
if (!params.iamApiKey && params.url) {
cloudant = Cloudant(params.url);
} else {
checkForBXCreds(params);
if (!params.host) {
return 'Cloudant account host is required.';
}
if (!params.iamApiKey) {
if (!params.username || !params.password) {
return 'You must specify parameter/s of iamApiKey or username/password';
}
}
var protocol = params.protocol || 'https';
if (params.iamApiKey) {
var dbURL = `${protocol}://${params.host}`;
if (params.port) {
dbURL += ':' + params.port;
}
cloudant = new Cloudant({
url: dbURL,
plugins: {iamauth: {iamApiKey: params.iamApiKey, iamTokenUrl: params.iamUrl}}
});
} else {
var url = `${protocol}://${params.username}:${params.password}#${params.host}`;
if (params.port) {
url += ':' + params.port;
}
cloudant = Cloudant(url);
}
}
return cloudant;
}
function checkForBXCreds(params) {
if (params.__bx_creds && (params.__bx_creds.cloudantnosqldb || params.__bx_creds.cloudantNoSQLDB)) {
var cloudantCreds = params.__bx_creds.cloudantnosqldb || params.__bx_creds.cloudantNoSQLDB;
if (!params.host) {
params.host = cloudantCreds.host || (cloudantCreds.username + '.cloudant.com');
}
if (!params.iamApiKey && !cloudantCreds.apikey) {
if (!params.username) {
params.username = cloudantCreds.username;
}
if (!params.password) {
params.password = cloudantCreds.password;
}
} else if (!params.iamApiKey) {
params.iamApiKey = cloudantCreds.apikey;
}
}
}
Basically, copying and pasting those credentials led to it not working. Not sure why. To get a test invocation working I added url, docid, dbname, host, url and iamApiKey values to the parameters section of the function. That worked.

IdentityServer 4 with Multiple Bindings

I have 2 sites (www.dogsite.com and www.catsite.com). They are pointed to login.identityserverdemo.com as my authority server. This allows the SSO to work between the two sites. Now if I add two more bindings to my login.identityserverdemo.com site called login.dogsite.com and login.catsite.com, I will lose the SSO. I have tried adding the www.dogsite.com and www.catsite.com to the client's redirectUrls with no improvement.
Here is my client config:
new Client
{
ClientId = "mvc-dog",
ClientName = "MVC Client For Dog Site",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RequireConsent = false,
ClientSecrets =
{
new Secret("woofwoof123".Sha256())
},
Claims = new List<Claim>
{ },
RedirectUris = { "http://www.catsite.com/signin-oidc", "http://www.dogsite.com/signin-oidc" },
PostLogoutRedirectUris = { "http://www.catsite.com/signout-callback-oidc","http://www.dogsite.com/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
},
AllowOfflineAccess = true
},
new Client
{
ClientId = "mvc-cat",
ClientName = "MVC Client For Cat Site",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RequireConsent = false,
ClientSecrets =
{
new Secret("MeowMeow456".Sha256())
},
Claims = new List<Claim>
{},
RedirectUris = { "http://www.catsite.com/signin-oidc", "http://www.dogsite.com/signin-oidc" },
PostLogoutRedirectUris = { "http://www.catsite.com/signout-callback-oidc","http://www.dogsite.com/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
},
AllowOfflineAccess = true
}
Please help??
You lose SSO because login.dogsite.com and login.catsite.com cannot get access to the same idSrv cookies.
Check out Cross domain cookie option. It's for ID3 but is equally applicable to ID4.

AuthenticateResult: Why are some claims missing?

I'm trying out a self-hosted IdentityServer3 solution and have come across an issue that I haven't found an answer to.
This is my IdentityServer setup:
var factory = new IdentityServerServiceFactory();
factory.UseInMemoryClients(Config.GetClients())
.UseInMemoryScopes(Config.GetScopes());
factory.UserService = new Registration<IUserService>(resolver => new LocalRegistrationUserService());
var options = new IdentityServerOptions
{
SiteName = "Demo IdP",
SigningCertificate = Certificate.Get(),
Factory = factory,
RequireSsl = Convert.ToBoolean(ConfigurationManager.AppSettings["RequireSsl"]),
};
app.UseIdentityServer(options);
Scope(s) and client(s):
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "resourceowner.client",
ClientSecrets =
{
new Secret("3FE8FB45-627A-4C44-BBE3-63281C6CA910".Sha256())
},
AllowedScopes = { "demo", "openid", "profile" },
Flow = Flows.ResourceOwner,
}
};
}
public static IEnumerable<Scope> GetScopes()
{
return new List<Scope>
{
new Scope
{
Name = "demo",
DisplayName = "Demo",
},
StandardScopes.OpenId,
StandardScopes.Profile,
};
}
And in LocalRegistrationUserService.AuthenticateLocalAsync(LocalAuthenticationContext context) I've got:
var loginResult = new AccountManagementService().Login(context.UserName, context.Password);
if (loginResult.LoginOk)
{
context.AuthenticateResult = new AuthenticateResult(loginResult.Subject, loginResult.UserName);
}
else
{
....
}
My understanding of the following paragraph from the documentation
*To fully log the user in the authentication API must produce a subject and a name that represent the user. The subject is the user service’s unique identifier for the user and the name is a display name for the user that will be displayed in the user interface.*
is that the subject and username will be present in the token returned by IdentityServer. However, when I decode the token this is what I get:
Access Token (decoded):
{
"typ": "JWT",
"alg": "RS256",
"x5t": "a3rMUgMFv9tPclLa6yF3zAkfquE",
"kid": "a3rMUgMFv9tPclLa6yF3zAkfquE"
}
{
"iss": "http://localhost:44333/core",
"aud": "http://localhost:44333/core/resources",
"exp": 1478524845,
"nbf": 1478521245,
"client_id": "resourceowner.client",
"scope": [
"demo",
"openid",
"profile"
],
"sub": "6ace8b2e-ce20-41e9-8d4e-382168e4ce05",
"auth_time": 1478521245,
"idp": "idsrv",
"amr": [
"password"
]
}
As is evident, no name claim is present. I've tried adding the claim explicitly when instantiating AuthenticateResult, but to no avail. I'm clearly missing something here, but can't for the best of it figure out what I'm doing wrong so tips, pointers and/or a helpful example would be much appreciated.
TIA

How to get all versions of an object in Google cloud storage bucket?

In a web page hosted in Google cloud storage, I will like to show revision history, which require listing all versions of the object.
Sending GET request to the bucket with ?versions parameter return list versions all objects. Is there any way to list all versions of a single object, as in gsutil ls -la, in javascript?
There is not. The closest you can do is to use versions=true and prefix=YOUR_OBJECT_NAME.
GCS will respond with a listing of objects beginning with all of the versions of your object and continuing on to any other objects that begin with YOUR_OBJECT_NAME. You'll have to check those items to see when the listing runs out of versions of your object and moves on to other objects.
If it so happens that only one object begins with YOUR_OBJECT_NAME (for example, your object is "foo.txt" and there are no files named, say, "foo.txt.backup", you will get exactly the files you want. You probably don't want to rely on this as a general practice, though.
Brondon's answer work with XML, but not with gapi client.
/**
* Get versions meta data of the object.
* #return {goog.async.Deferred} return history of the object.
*/
mbi.data.Object.prototype.history = function() {
var df = new goog.async.Deferred();
var use_gapi = true;
var name = this.getName();
if (use_gapi) {
// GAPI does not return result for versions request.
var params = {
'bucket': this.getBucketName(),
'versions': true,
'prefix': name
};
// console.log(params);
var req = gapi.client.rpcRequest('storage.buckets.get',
mbi.app.base.GAPI_STORAGE_VERSION, params);
req.execute(function(json, row) {
if (json) {
df.callback(json);
} else {
df.errback();
throw new Error(row);
}
});
} else {
var xm = mbi.data.Object.getXhr();
var uri = new goog.Uri(this.getUrl());
uri.setParameterValue('versions', 'true');
uri.setParameterValue('max-keys', '25');
uri.setParameterValue('prefix', name);
var url = uri.setPath('').setFragment('').toString();
xm.send(url, url, 'GET', null, {}, 1, function(e) {
var xhr = /** #type {goog.net.XhrIo} */ (e.target);
if (xhr.isSuccess()) {
var xml = xhr.getResponseXml();
// console.log(xml);
var json = mbi.utils.xml.xml2json(xml);
var items = json['ListBucketResult']['Version'];
var versions = goog.isArray(items) ? items : items ? [items] : [];
versions = versions.filter(function(x) {
return x['Key'] == name;
});
df.callback(versions);
} else {
df.errback(xhr.getStatus() + ' ' + xhr.getResponseText());
}
});
}
return df;
};
GAPI return as follow without version meta:
[
{
"id": "gapiRpc",
"result": {
"kind": "storage#bucket",
"id": "mbiwiki-test",
"name": "mbiwiki-test",
"timeCreated": "2013-08-20T01:18:46.957Z",
"metageneration": "9",
"owner": {
"entity": "group-00b4903a97262a358b97b95b39df60893ece79605b60280ad389c889abf70645",
"entityId": "00b4903a97262a358b97b95b39df60893ece79605b60280ad389c889abf70645"
},
"location": "US",
"website": {
"mainPageSuffix": "index.html",
"notFoundPage": "error404.html"
},
"versioning": {
"enabled": true
},
"cors": [
{
"origin": [
"http://static.mechanobio.info",
"http://mbinfo-backend.appspot.com",
"https://mbinfo-backend.appspot.com",
"http://localhost",
"chrome-extension://pbcpfkkhmlbicomenogobbagaaenlnpd",
"chrome-extension://mhigmmbegkpdlhjaphlffclbgkgelnbe",
"chrome-extension://jhmklemcneaienackijjhdikoicmoepp"
],
"method": [
"GET",
"HEAD",
"POST",
"PUT",
"DELETE",
"PATCH"
],
"responseHeader": [
"content-type",
"Authorization",
"Cache-Control",
"x-goog-meta-reviewer"
]
}
],
"storageClass": "STANDARD",
"etag": "CAk="
}
}
]