I am working on a project in which I need to expose some of its method over web service and that I am doing using Mule by following RAML and to implement security, OAuth 2.0 has been proposed. I am able to validate the client for its 'client_id' and 'client_secret' but for that I have given the credentials in configuration file only. Now I need to fetch the client details from database. I tried using 'preFlow-ref' and another flow is being called where I am able to retrieve 'client_id' and 'client_secret' but don't know how to give callback to OAuth.
<spring:beans>
<spring:bean id="oauthTokenStore" name="oauthTokenStore" class="database_connection.MyClientStore"/>
</spring:beans>
<oauth2-provider:config
name="oauth2Provider"
providerName="TweetBook"
supportedGrantTypes="CLIENT_CREDENTIALS"
preFlow-ref="apiFlow1"
host="localhost"
port="8083"
authorizationEndpointPath="tweetbook/api/authorize"
accessTokenEndpointPath="tweetbook/api/token"
scopes="READ_PROFILE WRITE_PROFILE READ_BOOKSHELF WRITE_BOOKSHELF">
</oauth2-provider:config>
<apikit:config name="Router" raml="src/main/api/api.raml" consoleEnabled="true" consolePath="console" doc:name="Router"/>
<flow name="apiFlow1">
<set-variable variableName="client_id" value="1234567890abcd" doc:name="Client id"/>
<logger message="entered DB flow" level="INFO" doc:name="Logger"/>
<db:select config-ref="Generic_Database_Configuration" doc:name="Database">
<db:dynamic-query><![CDATA[select client_secret from table1 where client_id = #[flowVars.client_Id]]]></db:dynamic-query>
</db:select>
<logger message="exit db flow" level="INFO" doc:name="Logger"/>
</flow>
Related
I am trying to use APIM to send a request through to a back-end App Service which requires the client to be authorised with roles. When I connect directly to the App Service with an App Registration with the roles this works as expected or when I send via APIM without using Managed Identity for the authorisation.
I would like to be able to have the Athorisation header replaced using the System Assinged Managed Identity but when it adds the new bearer token it is missing the roles section when I inspect the JWT Token in jwt.io.
The policy looks like this:
`
<policies>
<inbound>
<base />
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Missing Token or Invalid one yo!">
<openid-config url="https://login.microsoftonline.com/{my_tenant}/.well-known/openid-configuration" />
<required-claims>
<claim name="aud">
<value>api://{my_backend_app_reg}</value>
</claim>
<claim name="roles" match="any" separator=",">
<value>{my_role}</value>
</claim>
</required-claims>
</validate-jwt>
<set-header name="ClientAppRegId" exists-action="override">
<value>#(context.Request.Headers.GetValueOrDefault("Authorization").AsJwt()?.Claims.GetValueOrDefault("appid"))</value>
</set-header>
<set-header name="Ocp-Apim-Subscriptionkey" exists-action="delete" />
<set-header name="Authorization" exists-action="delete" />
<authentication-managed-identity resource="api://{my_backend_app_reg}" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
`
Have ensured the App Registration that seems to be tied to the APIM Managed Identity has the roles set within the API permissions section and confirmed when sending via Postman/Insomnia that the roles are in the returned token when manually requested.
Have checked the token used in the trace and it looks almost the same but missing the roles section.
My back-end service then authenticates as expected but then fails at the authorisation on the controllers as the roles are not present in the token
I am storing the output from DB in a flowvar #[flowvars.test] after which I am iterating the payload to remove few key, value pair. when the payload is modified inside the for loop the value which is stored in flowvars also getting modified.I am using mule 3.9 runtime.
<db:parameterized-query><![CDATA[select MachineName,TransactionId,SourceAPIName,Source,Target,ErrorCode,Severity,MailContent,ExceptionType,ExceptionMessage from Notification
where Notify='Y' and IsNotify='N']]>
</db:select>
</poll>
<expression-filter expression="#[payload.size()>0]" doc:name="Stop If No Records"/>
<set-variable variableName="test" value="#[message.payload]" doc:name="Variable"/>
<set-variable variableName="validatedEntries" value="#[[]]"
doc:name="ValidatedEntries" />
<logger level="INFO" doc:name="Logger"/>
<foreach collection="#[flowVars.test]" doc:name="For Each">
<logger level="INFO" doc:name="Logger"/>
<set-variable variableName="tempNotificationTable" value="#[payload.remove('TransactionID')]" doc:name="Temp NotificationTable"/>
<expression-component doc:name="Expression"><![CDATA[#[flowVars.validatedEntries.add(payload)]]]></expression-component>
</foreach>
For-Each will not create a deep copy of the object. Because of this, your original payload is getting changed.
I have the below flow which returns a list of values from DB with in mule.
I want to be able to get only the value part of the result from DB instead of a full linked list in the format of "column=value". I want to use the value part as part of a URL in the next http block. Please see my flow below. Any feedback will be helpful please.
<http:request-config name="HTTP_Request_Configuration" protocol="HTTPS" host="jsonplaceholder.typicode.com/#[flowVars.custID]" port="443" doc:name="HTTP Request Configuration"/>
<http:listener-config name="HTTP_Listener_Configuration1" host="0.0.0.0" port="8081" doc:name="HTTP Listener Configuration"/>
<flow name="dbcconnectorFlow">
<http:listener config-ref="HTTP_Listener_Configuration2" path="/lockStatus" allowedMethods="PUT" doc:name="HTTP"/>
<db:select config-ref="Oracle_Configuration" doc:name="Database">
<db:dynamic-query><![CDATA[select CUST_NO FROM CUSTOMERS WHERE LOCKED='N']]></db:dynamic-query>
</db:select>
<logger message="#[message.payload]"" level="INFO" doc:name="Logger"/>
<set-variable variableName="custID" value="#[message.payload[0]]" doc:name="Variable"/>
<http:request config-ref="HTTP_Request_Configuration" path="/" method="PUT" doc:name="HTTP"/>
</flow>
Ekow
One way to get only the value part of the result from DB is to put a transform message (Dataweave) component after the DB component and configure it as shown in this example:
<flow name="myprojectFlow">
<http:listener config-ref="HTTP_Listener_Configuration" path="/" doc:name="HTTP"/>
<db:select config-ref="MySQL_Configuration" doc:name="Database">
<db:parameterized-query><![CDATA[select CompanyName from Customers limit 10]]></db:parameterized-query>
</db:select>
<response>
<dw:transform-message doc:name="Transform Message">
<dw:set-payload><![CDATA[%dw 1.0
%output application/json
---
payload.CompanyName[1]]]></dw:set-payload>
</dw:transform-message>
</response>
</flow>
The output is the first company name string in the array. The column name is not included.
Found a way around this. I had to convert the db results which was an object into xml using the object to xml transformer. Then use the splitter to split the results since I was expecting multiple output as below
[xpath3('//root/data/"xml_tag_of_required_data"', payload, 'NODESET')]. I then set the output as a variable and referenced the variable in the http endpoint URL.
I am calling one mule flow from another using HTTP with basic authentication using the Spring Security Manager. I am using Mule 3.7 and configured everything according to the documentation at:
https://docs.mulesoft.com/mule-user-guide/v/3.7/configuring-the-spring-security-manager
<spring:beans>
<ss:authentication-manager alias="authenticationManager">
<ss:authentication-provider>
<ss:user-service id="userService">
<ss:user name="${security.user.id}" password="${security.user.password}" authorities="ROLE_ADMIN" />
</ss:user-service>
</ss:authentication-provider>
</ss:authentication-manager>
</spring:beans>
<mule-ss:security-manager>
<mule-ss:delegate-security-provider name="memory-dao" delegate-ref="authenticationManager" />
</mule-ss:security-manager>
<http:listener-config name="httpLocalListener" host="${local.host}" port="${local.port}"
basePath="${local.path}" doc:name="HTTP Local Listener" connectionIdleTimeout="${local.timeout}"/>
<http:request-config name="httpLocalRequest" doc:name="HTTP Local Configuration" responseTimeout="${local.timeout}"
basePath="${local.path}" host="${local.host}" port="${local.port}">
<http:basic-authentication username="${security.user.id}" password="${security.user.password}"/>
</http:request-config>
<flow name="ServiceFlow1" processingStrategy="synchronous">
<http:listener config-ref="httpLocalListener" path="/status/*" doc:name="HTTP" allowedMethods="GET"/>
<http:basic-security-filter realm="${security.filter.realm}"/>
<!-- Omitted code -->
<http:request config-ref="httpLocalRequest" path="/ping/txt?siteId=#[sessionVars['siteId']]" method="GET" doc:name="HTTP" parseResponse="false">
<http:success-status-code-validator values="0..599"/>
</http:request>
</flow>
<flow name="ServiceFlow2" processingStrategy="synchronous">
<http:listener config-ref="httpLocalListener" path="/ping/txt" doc:name="HTTP" allowedMethods="GET"/>
<http:basic-security-filter realm="${security.filter.realm}"/>
<!-- Omitted code -->
</flow>
I get the following error (I removed '//' from http links due to stackoverflow requirements):
ERROR 2016-08-19 10:28:09,539 [[Service].httpLocalListener.worker.02]
org.mule.exception.DefaultMessagingExceptionStrategy:
Message : Registered authentication is set to org.mule.transport.http.filters.HttpBasicAuthenticationFilter but there was no security context on the session. Authentication denied on endpoint http:0.0.0.0:8081/services/ping/txt. Message payload is of type: NullPayload
Type : org.mule.api.security.UnauthorisedException
Code : MULE_ERROR--2
JavaDoc : http:www.mulesoft.org/docs/site/current3/apidocs/org/mule/api/security/UnauthorisedException.html
Payload : {NullPayload}
Exception stack is:
1. Registered authentication is set to org.mule.transport.http.filters.HttpBasicAuthenticationFilter but there was no security context on the session. Authentication denied on endpoint http:0.0.0.0:8081/services/ping/txt. Message payload is of type: NullPayload (org.mule.api.security.UnauthorisedException)
org.mule.transport.http.filters.HttpBasicAuthenticationFilter:156 (http:www.mulesoft.org/docs/site/current3/apidocs/org/mule/api/security/UnauthorisedException.html)
Any help would be appreciated!
Thanks,
Dennis
I had the same kind of issue once, but that issue disappeared when I invoked the same URL from Postman where we hit the service along with credentials for basic authentication. The same doesn't work with a normal browser based test because when you invoke the service, it expects the credentials for the basic authentication and then given a pop-up for the same in next instance.
HTTPs basic auth using Postman Client
AM not sure whether this helps or not because my explanation is a bit immature, but might help you get a better idea on the implementation. only thing I can say is, it will throw an error but will work as desired.
I would like to iterate through an array and use the value taken from the array to put it within an http inbound endpoint. How would I be able to iterate through this array and take the value from the array to place it as a variable within the http inbound endpoint?
The code that I used to try was:
<flow name="foreachFlow1" doc:name="foreachFlow1">
<poll frequency="2000">
<foreach collection="#[groovy:['localhost:8082', 'localhost:8083']]"
doc:name="For Each">
<http:outbound-endpoint exchange-pattern="request-response"
address="http://#[payload]" method="GET" doc:name="HTTP" />
</foreach>
</poll>
</flow>
and I get the error
Invalid content was found starting with element 'poll'
Inbound endpoints are message sources and can not be parametrized the way you're describing.
To achieve your goal, trying a <poll> message source to wrap a foreach that uses http:outbound-endpoint to perform GET (#method) request-response (#exchange-apttern) interactions.
The trick is to bring the results for the HTTP calls back through the foreach, which by default do not do it. The following illustrate a potential approach:
<flow name="foreachFlow1">
<poll frequency="2000">
<processor-chain>
<set-variable variableName="httpResponses" value="#[[]]" />
<foreach collection="#[groovy:['localhost:8082', 'localhost:8083']]">
<http:outbound-endpoint
exchange-pattern="request-response" address="http://#[payload]"
method="GET" />
<expression-component>httpResponses.add(message.payloadAs(java.lang.String))
</expression-component>
</foreach>
</processor-chain>
</poll>
<logger level="INFO" message="#[httpResponses]" />
</flow>
<!-- Test server stubs -->
<flow name="server8082">
<http:inbound-endpoint exchange-pattern="request-response"
address="http://localhost:8082" />
<set-payload value="This is 8082" />
</flow>
<flow name="server8083">
<http:inbound-endpoint exchange-pattern="request-response"
address="http://localhost:8083" />
<set-payload value="This is 8083" />
</flow>