Users and Groups

In this guide you are going to add Users and Groups to a Usage Plan that is associated with an API Product. A Usage Plan can specify the authentication policy and rate limit policy for a set of users or groups. When a user wants to consume an API Product, they can authenticate for access using either an API Key or OAuth (coming soon). Users can also log into the Portal UI to test out the API Product commands and procure an API Key for authentication.

We will do the following:

  1. Create a user and group
  2. Associate the group with a Usage Plan on an API Product
  3. Log into the Portal UI to procure an API Key
  4. Use the API Key to call the API Product

Prerequisites

You will need these things in place to follow the guide:

Creating a user and group

We are first going to create a user (dev1) and then add them to a group (developers). Users and groups are both stored as Custom Resources (CRs) in Kubernetes. Each user can log into the Portal UI using a hashed password stored in a Kubernetes secret. Other authentication methods will be added in future releases. A user can be added through the Admin Dashboard or by creating the Kubernetes Secret and Custom Resource using kubectl.

In our example, we will add a user by using kubectl. You can also create a user with the Admin Dashboard.

First we will create and store a password in a Kubernetes Secret. The plaintext password is hashed using a bcrypt function with a cost of 10. We can use the htpasswd function from the apache-utils package to do the same at the command line.

# Install the apache2-utils if you don't already have them
sudo apt install apache2-utils

# Generate the bcrypt hash with cost of 10
pass=$(htpasswd -bnBC 10 "" mysecurepassword | tr -d ':\n')

# Store the hash as a Kubernetes Secret
kubectl create secret generic dev1-password \
  -n dev-portal --type=opaque \
  --from-literal=password=$pass

Next we will create the user by submitting a Custom Resource configuration:

cat << EOF | kubectl apply -f-
apiVersion: devportal.solo.io/v1alpha1
kind: User
metadata:
  name: dev1
  namespace: dev-portal
spec:
  accessLevel: {}
  basicAuth:
    passwordSecretKey: password
    passwordSecretName: dev1-password
    passwordSecretNamespace: dev-portal
  username: dev1
EOF

We can verify our user was created successfully by running:

kubectl get user dev1 -n dev-portal -oyaml

The state of the user CR should be Succeeded.

Create a Group

With user dev1 created, we can now create a group and make dev1 a member. Users are associated with groups based on a label applied to the user CR. The group CR has a userSelector setting specifying which labels it should match when evaluating user CRs. We’ll first create the group and then apply the label set in userSelector to dev1’s CR to make them a member of the group.

You can also create a group with the Admin Dashboard.

Let’s create the group developers using kubectl:

cat << EOF | kubectl apply -f-
apiVersion: devportal.solo.io/v1alpha1
kind: Group
metadata:
  name: developers
  namespace: dev-portal
spec:
  displayName: developers
  userSelector:
    matchLabels:
      groups.devportal.solo.io/dev-portal.developers: "true"
EOF

Now let’s verify that the developers group was created successfully:

kubectl get group developers -n dev-portal -oyaml

The state of the group CR should be Succeeded.

Finally, we’ll update dev1’s CR with the new label:

kubectl label user dev1 -n dev-portal groups.devportal.solo.io/dev-portal.developers="true"

We can validate that dev1 has been added to developers by looking at the status of the developers CR:

kubectl get group developers -n dev-portal -oyaml
status:
  observedGeneration: "1"
  state: Succeeded
  users:
  - name: dev1
    namespace: dev-portal

Now that we have a user and group, we can set them up with a Usage Plan on the Petstore API Product.

Adding a group to an API Product

API Products have Usage Plans to determine how users and groups may access and use the API. If there are no plans associated with an API Product, then everyone has unauthenticated and unlimited access. Once a plan is added, unauthenticated access is revoked for all existing users and groups.

We are going to create a Basic usage plan for our Petstore API Product and then associate that plan with the developers group we created earlier. You can also configure a usage plan with the Admin Dashboard.

We will first update the configuration of the Petstore API Product to include the Basic usage plan:

cat << EOF | kubectl apply -f-
apiVersion: devportal.solo.io/v1alpha1
kind: APIProduct
metadata:
  name: petstore-product
  namespace: default
  labels: 
    portals.devportal.solo.io/default.petstore-portal: "true"
spec:
  apis:
  - apiDoc:
      name: petstore-schema
      namespace: default
  defaultRoute:
    inlineRoute:
      backends:
      - kube:
          name: petstore
          namespace: default
          port: 8080
  domains:
  - api.example.com
  displayInfo: 
    description: Petstore Product
    title: Petstore Product
    image:
      fetchUrl: https://i.imgur.com/Co2A5lK.png
  
  # Each plan will have an authPolicy and an option rate limit
  # that determines the maximum number of requests in a time period
  plans:
  - authPolicy:
      apiKey: {}
    displayName: Basic
    name: basic
    rateLimit:
      requestsPerUnit: 5
      unit: MINUTE

EOF

We have now created a usage plan that uses the API Key authentication method and is limited to 5 requests per minute. The next step is to update our developers group to have access to the Petstore API Product with the Basic usage plan. We will also grant them access to the Petstore portal.

cat << EOF | kubectl apply -f-
apiVersion: devportal.solo.io/v1alpha1
kind: Group
metadata:
  name: developers
  namespace: dev-portal
spec:
  displayName: developers
  # accessLevel is a list of items the group has access to including
  # apiProducts and portals
  accessLevel:
    apiProducts:
    - name: petstore-product
      namespace: default
      plans:
      - basic
    portals:
    - name: petstore-portal
      namespace: default
  userSelector:
    matchLabels:
      groups.devportal.solo.io/dev-portal.developers: "true"

EOF

Our user dev1 should now have access through the developers group to log into the Petstore portal, procure an API key, and test the API Product’s functionality.

Using the Petstore portal

In order to access the Petstore portal, you will need to update your hosts file to map `petstore.example.com’ to the IP address of the Istio ingress controller. If you haven’t already done so from the Getting Started exercise, you will need to do the following. Let’s get the address of the Gateway. Choose the option corresponding to your Ingress Service Type:

export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].port}')
export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
export INGRESS_HOST=$(kubectl -n gloo-system get service gateway-proxy -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
export INGRESS_PORT=$(kubectl -n gloo-system get service gateway-proxy -o jsonpath='{.spec.ports[?(@.name=="http")].port}')
export INGRESS_HOST=$(kubectl get po -l gloo=gateway-proxy -n gloo-system -o jsonpath='{.items[0].status.hostIP}')
export INGRESS_PORT=$(kubectl -n gloo-system get service gateway-proxy -o jsonpath='{.spec.ports[?(@.name=="http")].nodePort}')

Let’s add entries for the api.example.com and petstore.example.com domains:

cat <<EOF | sudo tee -a /etc/hosts

# Added for the Dev Portal Guides
${INGRESS_HOST} api.example.com
${INGRESS_HOST} petstore.example.com
EOF

Now you can go to the Petstore Portal in your browser using http://petstore.example.com.

Login portal

Click on the Log In button and enter the credentials of the dev1 user - dev1 and mysecurepassword :

Login screen

The first time a user logs in, they will be prompted to change their password. Change dev1’s password to a new value and click the Change My Password button.

Change password

Now that we’re logged in, we can see the APIs that are available through the portal.

APIs page

Before we try and test the APIs, let’s procure an API Key. To do this, we can click the down arrow next to the dev1 login badge and select API Keys.

APIs Keys

This will take you to the authentication page, where you can generate API Keys for any plan that has been assigned to you.

APIs Add Keys

In our case, the Basic plan has been assigned, so we will click on Add Api Key to generate a new key.

APIs Generate Key

A new API Key will be generated, which you should copy for later use as we test the Petstore API.

Now we can go back to the APIs page and select the Petstore Product to test the API in the browser.

APIs Page

On the main Petstore Product page, you can view the different API commands and test them in the context of the browser. Since we have an API Key, we are first going to click on Authorize to add that key to the test commands.

Petstore Main Page

In the Available Authorizations pop-up, enter the API Key from earlier and click Authorize.

Petstore Auth Page

Now we are ready to test the API commands with an authorized key. Expand the GET /api/pets command and click on Try it out to try the command in the browser.

Petstore Test Command

Click on the Execute button and you will see a response similar to the graphic below.

Petstore GET Command

The request uses the header api-key with the value we set in the authorization section. The response code is 200, showing that the request was processed successfully.

Now that we have enabled a Usage Plan for the API Product, unauthenticated users will no longer be able to access the published operations. Let’s try the curl command displayed by the Portal UI without the API key:

curl -X GET "http://api.example.com/api/pets" -H  "accept: application/json"

We will get a response of Error: Unauthorized with a status code of 401.

Likewise, we placed a rate limit of 5 operations per minute in our Usage Plan. If we execute an authenticated request 6 times in rapid succession, the sixth request will receive the message Error: Too Many Requests with a status code of 429.

Too Many Requests

Summary

In this guide you created a user, group, and usage plan. You made the user a member of the developers group, and granted the developers group access to the Petstore product and portal with the Basic usage plan. Finally, you logged into the Portal UI as the user, provisioned an API key and tested out the Petstore Product API.

Next steps

You may want to learn more about the concepts behind users, groups, plans, and portals. Check out the architecture page of our concepts section as an excellent jumping off point to better understand how all the pieces of the Dev Portal fit together.