In the API Manager 1.10 release, the API Manager team unveild a new
REST API for store and publisher operations. This article will discuss
the details of the REST API design, how it was developed, and how you
can use the API to generate customized applications that can design,
develop, manage, and consume APIs.
Table of Contents
- Introduction
- REST API Design and Implementation Details
- Security Mechanisms
- OAuth Application Registration
- API Invocation
- How to Write a Simple Java Client to Invoke Rest API and Get all APIs
- Conclusion
Introduction
WSO2 API Manager is a complete solution for publishing APIs, creating
and managing a developer community, and for scalably routing API
traffic. It leverages proven, production-ready, integration, security
and governance components from
WSO2 Enterprise Service Bus,
WSO2 Identity Server, and
WSO2 Governance Registry.
Moreover, it is powered by WSO2 Business Activity Monitor, thereby
making WSO2 API Manager ready for any large-scale deployments right
away.
As part of its latest release, the REST API was developed as a CXF
REST web application running on WSO2 API Manager. This API comes with a
pluggable security mechanism. API security is implemented as a CXF
handler, hence if users need to plug custom security mechanism then they
can write their own handler and add it to web service. This REST API is
implemented based on REST best practices and specifications. The API
development started with a swagger specification for store and publisher
operations.
In today’s connected world, it’s important to have proper REST APIs
for all available software products. Almost all organizations,
platforms, and products are connected to each other via APIs to enable a
better service. For instance, if you consider a solution like API
Management, then a complete API is a must-have feature given that most
times users will develop their own API store publisher using APIs that
are available in the platform.
Prior to API manager 1.10, we had a Jaggery-based REST API for API
Management related common operations; however, it was not fully
compliant with REST specifications. Therefore, in the 1.10 version, we
decided to implement a complete REST API that complies with all
REST-related best practices. We also decided to follow the Richardson
maturity model for REST API when we progressed with implementation.
In WSO2 API Manager, you can perform all operations available in the
API store and publisher via REST API. From API Manager 1.10 onwards, the
product will come with REST API store and publisher features. The API
store and publisher Jaggery applications will still support the old REST
API. However, if you’re using the WSO2 API Manager 1.10 version or
above, it’s highly recommended to use the new REST API to all store and
publisher operations.
The contract first approach is not a new design methodology to anyone
in the software industry, and in this implementation too we’ve used
this approach. We started with a swagger definition for REST APIs and
then progressed to design a complete REST API using swagger API
description language. When designing the API, we identified resources,
resource URLs, and multiple parameters to be pass APIs. All input/output
request format response codes, response body, etc. were designed using
swagger. For the initial implementation, we’ve incorporated REST API for
API store and publisher functionalities. In the future, we will
introduce a new API for all management and other operations available in
the product.
In the swagger definition, we defined data models for each and every
resource object. Hence, each resource would be represented with JSON
payload and we can then map them to a Java object. This article will
discuss the basics in REST API design, development details, and how you
can invoke it. To invoke this REST API, you can use any web service
client available in the industry, such as Curl, REST Client, SoapUI, or
any others.
With this API, you can make a developer’s’ life easy as it’s a
well-defined API. Since we have a complete swagger definition, users can
generate client applications using a swagger document or code
generation clients based on WADL, e.g. a WADL to Java client.
REST API Design and Implementation Details
These two web applications are implemented as CXF web applications.
For this, we used swagger to CXF client and generated a code for web
application. As we are generating CXF server side skeleton code directly
from the swagger definition, there is a minimal amount of gap between
the swagger definition and the actual implementation. In addition, when
we make a change to the specification, this can easily be brought into
the implementation code. These are major advantages of using a code
generator. Once the initial code was generated, we implemented a data
access layer.
WSO2 API Manager store and publisher REST APIs are developed as 2 separate CXF web applications named as follows:
- Applications are named as
- api#am#store#v1.war and
- api#am#publisher#v1.war.
- Applications will be deployed in contexts
- api/am/store/v1 and
- api/am/publisher/v1.
These 2 APIs are developed based on swagger definitions for WSO2 API
Manager store and publisher. This app development first started with the
swagger API definition after identifying all required operations for
the API management story. Then the swagger to CXF code generator was
used to generate code for web applications. The underlying data access
layer will directly call the API manager consumer and provider
implementations.
For more details about all supporting methods in this rest API you can use the
swagger console
and add WSO2 API Manager store and publisher REST API definitions. Then
it will list all operations available in store and publisher (you can
find store and publisher swagger definitions for API Magere 1.10 in
this git location.
Security Mechanisms
WSO2 API Manager REST API has complex resource paths for different
operations. These resources should be accessible based on user
permissions. In real production systems, you would need to be able to
manage these permissions as extensively as possible as requirements can
change based on user scenarios. Moreover, you should be able to easily
add/update permissions per resource/operations and associated
users/roles, etc.
For example, let’s consider the following requirement:
API update/edit can be performed only by a special user with API
create/update permission; API lifecycle state changes should be carried
out by a user who has API publish permission; and tier edit/update
should be performed by users with administrative privileges.
Here, you can see that different resources are defined with different
permissions and, depending on deployment, these permissions can be
changed. As a solution for that requirement, API manager REST APIs will
comprise extendable security mechanisms.
According to the current implementation, API manager REST API web
applications contain CXF interceptors to handle security. By default, it
will have 3 security related interceptors and users can change
configuration and select the required mechanism. In summary, we will
support the following security mechanisms for REST API:
- Basic authentication
- XACML for fine-grained permission validation
- OAuth with scopes support
If users are willing to have another security mechanism they need to
write CXF handler and plug it to web applications. It’s always
recommended to use one or more security mechanisms with this API as
users can do almost all critical operations using these APIs.
You can change the CXF interceptor by editing WEB-INF/beans.xml
files. You can see the following entry for the security interceptor.
As you can see above, by default, REST APIs come with OAuth
authentication; if you need to change it to basic authentication you may
change the auth handler as follows:
Then you need to pass basic authentication headers along with API calls.
If you are planning to use XACML as fine-grained permission
validator, you may add XACML handler in the same way we discussed
earlier.
When you hit the XACML flow you will get server URL, username, and
password defined in Java code of EntitlementClientUtils class and create
a XACML client. If users are willing to use XACML in production, it is
recommended to use some kind of configuration file and read these
properties from it when you create the XACML client. Thereafter, we will
do a web service call to the XACML service that’s running on server
using the already created client. To create the XACML policy, you may
need to install XACML features into the API manager or use WSO2 Identity
Server as the key validator.
For the initial release we will use hard-coded parameters to connect
the XACML server and later we will provide additional configuration to
be used by this interceptor. To understand XACML usage in API manager
you can refer to this article -
(http://wso2.com/library/articles/2014/02/use-of-wso2-api-manager-to-validate-fine-grained-policy-decisions-using-xacml/).
By default, OAuth has been enabled as a security mechanism for REST
APIs. Refer to the following diagram to understand OAuth flow to obtain
token and access APIs.
Figure 1
OAuth Application Registration
Dynamic client registration is a very common and widely used software
security implementation. In this case, we have provided web application
register Oauth applications using dynamic client registration protocol.
However, it’s important to note that this is not a complete
implementation of the dynamic client registration specification. In API
manager, you will see that the web application has been named
client-registration. It’s the CXF web application that’s directly
calling the API manager key manager. From WSO2 API Manager 1.9.0 onward,
you can plug your own key manager implementation to the API manager
product. We have extracted the key manager interface and anyone can
write their own implementation for it. We will reuse the same capability
here as well. We do not rely on the underlying key manager
implementation. We will simply request the key manager object and call
its method based on a pre-defined interface for the key manager.
First we have to obtain consumer key/secret key pair by calling the
dynamic client registration endpoint. Then you can request an access
token with proffered grant type. When doing so, you have to provide
scope according to your requirements. The above-mentioned steps are
almost the same for the identity server use case as well. Resource
registration was not required because we will be using the swagger
document as the resource for API. The scope to role mapping will be
stored as configuration defined in the
/_system/config/apimgt/applicationdata/tenant-conf.json configuration
file available in tenants configuration registry.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
"RESTAPIScopes": {
"Scope": [
{
"Name": "API_PUBLISHER_SCOPE",
"Roles": "admin"
},
{
"Name": "API_CREATOR_SCOPE",
"Roles": "admin"
},
{
"Name": "API_CREATOR_PUBLISHER_SCOPE",
"Roles": "admin"
},
{
"Name": "API_SUBSCRIBER_SCOPE",
"Roles": "Internal/subscriber"
},
{
"Name": "API_ADMINISTRATIVE_SCOPE",
"Roles": "admin"
}
]
}
|
Secured DCR end point with basic authentication
Now DCR is available as an installable feature, therefore anyone can
install it and use it as per requirement. If you need to use it with the
identity server, you can simply install this feature in the product.
Sample request to registration API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
{
"callbackUrl": "www.google.lk",
"clientName": "rest_api_store",
"tokenScope": "Production",
"owner": "admin",
"grantType": "password refresh_token",
"saasApp": true
}
Sample response
{
"callBackURL": "www.google.lk",
"jsonString": "{\"username\":\"admin\",\"redirect_uris\":\"www.google.lk\",\"tokenScope\":[Ljava.lang.String;@3a73796a,\"client_name\":\"admin_rest_api_store\",\"grant_types\":\"authorization_code password refresh_token iwa:ntlm urn:ietf:params:oauth:grant-type:saml2-bearer client_credentials implicit\"}",
"clientName": null,
"clientId": "HfEl1jJPdg5tbtrxhAwybN05QGoa",
"clientSecret": "l6c0aoLcWR3fwezHhc7XoGOht5Aa"
}
|
API Invocation
During the API invocation process request, first come to the CXF
handler and then it will call an introspection API to validate the
token. Post token validation, we will carry out scope validation with
the given resource. To validate resources with scope, we will use the
API object created by parsing the swagger document of API. Therefore,
you don't need to store scopes/API details in databases. It will
dynamically generate with swagger content (swagger inbuilt scope
association for resources).
If you need to change the permission model, you just have to update
swagger/configuration and redeploy application. To maintain scope to
role mapping, we will use configuration defined in the WSO2 API Manager
configuration file.
As of now we have identified 4 scopes to cover normal use cases:
API_PUBLISHER_SCOPE, publisher
API_SUBSCRIBER_SCOPE, subscriber
API_CREATOR_SCOPE, creator
API_ADMINISTRATIVE_SCOPE, admin
Token generate request
curl -k -d
"grant_type=password&username=sanjeewa&password=sanjeewa&scope=API_SUBSCRIBER_SCOPE"
-H "Authorization: Basic
SGZFbDFqSlBkZzV0YnRyeGhBd3liTjA1UUdvYTpsNmMwYW9MY1dSM2Z3ZXpIaGM3WG9HT2h0NUFh"
https://127.0.0.1:8243/token
Token response
1
2
3
|
{"scope":"API_SUBSCRIBER_SCOPE","token_type":"Bearer",
"expires_in":3600,"refresh_token":"33c3be152ebf0030b3fb76f2c1f80bf8",
"access_token":"292ff0fd256814536baca0926f483c8d"}
|
How to Write a Simple Java Client to Invoke REST API and Get all APIs
This section will focus on how you can implement Java client to
invoke REST APIs.
As discussed in previous section, API invocation is carried out in three
steps. To register Oauth application, you would need to get an access
token and invoke the API by using the following code sample:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
//Following code block will call dynamic client registration endpoint and register OAuth application
String dcrEndpointURL =getKeyManagerURLHttp()+"client-registration/v1/register";
//This is the app request body. You can create JSON payload with your details for app.
String applicationRequestBody = " {\n" +
" \"callbackUrl\": \"www.google.lk\",\n" +
" \"clientName\": \"fffff\",\n" +
" \"tokenScope\": \"Production\",\n" +
" \"owner\": \"admin\",\n" +
" \"grantType\": \"password refresh_token\",\n" +
" \"saasApp\": true\n" +
" }";
Map<string, string=""> dcrRequestHeaders = new HashMap<string, string="">();
//This is base 64 encoded basic Auth value for user name admin and password admin.
String basicAuthHeader = "admin" + ":" + "admin";
byte[] encodedBytes = Base64.encodeBase64(basicAuthHeader.getBytes("UTF-8"));
dcrRequestHeaders.put("Authorization", "Basic " + new String(encodedBytes, "UTF-8"));
//Set content type as its mandatory for API
dcrRequestHeaders.put("Content-Type", "application/json");
JSONObject clientRegistrationResponse =new JSONObject(HttpRequestUtil.doPost(new URL(dcrEndpointURL), applicationRequestBody,dcrRequestHeaders));
//Now you have consumer key and secret key obtained from client registration call.
//Let's extract these parameters from response message as follows:
String consumerKey =new JSONObject(clientRegistrationResponse.getString("data")).get("clientId").toString();
String consumerSecret =new JSONObject(clientRegistrationResponse.getString("data")).get("clientSecret").toString();
Thread.sleep(2000);
//Now we need to call token API and request access token.
//For this example we will use password grant type. You need to pass correct scope based on the resource you are trying to access.
String requestBody = "grant_type=password&username=admin&password=admin&scope=API_CREATOR_SCOPE";
URL tokenEndpointURL = new URL(getGatewayURLNhttp() + "token");
JSONObject accessTokenGenerationResponse = new JSONObject(
apiStore.generateUserAccessKey(consumerKey, consumerSecret, requestBody,
tokenEndpointURL).getData()
);
//Add consumer key and secret as basic auth header
Map<string, string=""> authenticationRequestHeaders = new HashMap<string, string="">();
String basicAuthHeader = consumerKey + ":" + consumerSecret;
byte[] encodedBytes = Base64.encodeBase64(basicAuthHeader.getBytes("UTF-8"));
authenticationRequestHeaders.put("Authorization", "Basic " + new String(encodedBytes, "UTF-8"));
JSONObject accessTokenGenerationResponse= new JSONObject(HttpRequestUtil.doPost(tokenEndpointURL, requestBody, authenticationRequestHeaders));
//Get access token and refresh token from token API call.
//Now we have access token and refresh token that we can use to invoke API.
String userAccessToken = accessTokenGenerationResponse.getString("access_token");
String refreshToken = accessTokenGenerationResponse.getString("refresh_token");
Map<string, string=""> requestHeaders = new HashMap<string, string="">();
//Check User Access Token
requestHeaders.put("Authorization", "Bearer " + userAccessToken);
requestHeaders.put("accept", "text/xml");
//Call API publisher REST API and get all registered APIs in system. This will return you API array in json format.
HttpRequestUtil.doGet(getKeyManagerURLHttp()+"/api/am/publisher/v1/apis?query=admin&type=provide",requestHeaders);
</string,></string,></string,></string,></string,></string,>
|
Conclusion
In this article, we’ve taken you through a quick introduction into
the concepts, implementation details, and how to use API manager REST
API. Being aware of REST API is beneficial in terms of either building
applications that consume WSO2 API Manager APIs or just a WSO2 API
Manager user. Exposing API manager resources via a RESTful API is a
flexible way to expose services to the outside world. It helps to meet
integration requirements of an API management platform as well as
external applications/systems. The details discussed in this article
cover just the basics, but is aimed at inspiring you to develop your
own application using API manager REST API.