Skip to content
This repository has been archived by the owner on Oct 11, 2023. It is now read-only.

WIP: Docker compose example #120

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
sudo: required
dist: xenial

services:
- docker
Expand Down
5 changes: 4 additions & 1 deletion GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ deps:
bin: generate format imports
@sh -c "'$(PWD)/scripts/build.sh'"

demo:
@sh -c "'$(PWD)/demo/build-demo.sh'"

release:
@$(MAKE) bin

Expand Down Expand Up @@ -74,4 +77,4 @@ bootstrap: deps
travis:
@sh -c "'$(PWD)/scripts/travis.sh'"

.PHONY: all dev deps bin release generate format imports test vet bootstrap travis
.PHONY: all dev deps bin demo release generate format imports test vet bootstrap travis
89 changes: 89 additions & 0 deletions demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Foulkon demo

This demo shows how Foulkon works, and how to manage it.

## Previous requirements

To run this demo, you have to set some properties and system packages.

### Go configuration

We have to set next environment vars:

- GOROOT
- GOPATH
- GOBIN

On Ubuntu (Directory examples, you can choose your own directories):

```bash

export GOROOT=$HOME/dev/golang/go
export GOPATH=$HOME/dev/sources/golang
export GOBIN=$HOME/dev/sources/golang/bin

```

This directories will be created before. Also, the GOBIN environment variable
will be in your execution path.

### System packages

This demo works with Docker, so you have to install Docker and Docker Compose.

- [Docker installation doc](https://docs.docker.com/engine/installation/)
- [Docker Compose installation doc](https://docs.docker.com/compose/install/)

## Start Demo

First, you have to download Foulkon project:

```bash

go get github.com/Tecsisa/foulkon

```

Second, go to Foulkon directory:

```bash

cd $GOPATH/src/github.com/Tecsisa/foulkon

```

Third, execute next command to get all dependencies:

```bash

make bootstrap

```

User login needs a Google client to make UI able to get a user.
To do this, follow the [Google guide](https://developers.google.com/identity/protocols/OpenIDConnect) to get a Google client
set http://localhost:8101/callback in your Authorized redirect URIs, and change next properties in [Docker-compose file](docker/docker-compose.yml):

- In foulkonworkercompose:
- FOULKON_AUTH_CLIENTID for your client id.
- In foulkondemowebcompose:
- OIDC_CLIENT_ID for your client id.
- OIDC_CLIENT_SECRET for your secret.

Finally, execute demo command to start demo:

```bash

make bootstrap

```

The applications started are next:

- Worker: Started on http://localhost:8000
- Proxy: Started on http://localhost:8001
- API demo: Started on http://localhost:8100
- UI demo: Started on http://localhost:8101

Now, you have all suite to try Foulkon, go to [Tour](tour.md) to see an example.

274 changes: 274 additions & 0 deletions demo/api/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
package main

import (
"encoding/json"
"net/http"
"os"

"bytes"

"github.com/julienschmidt/httprouter"
"github.com/sirupsen/logrus"
)

// CONSTANTS
const (
// Environment Vars
HOST = "APIHOST"
PORT = "APIPORT"
FOULKONHOST = "FOULKON_WORKER_HOST"
FOULKONPORT = "FOULKON_WORKER_PORT"

// HTTP Constants
RESOURCE_ID = "id"
)

type Resource struct {
Id string `json:"id, omitempty"`
Resource string `json:"resource, omitempty"`
}

var resources map[string]string
var logger *logrus.Logger

func HandleAddResource(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
request := &Resource{}
err := processHttpRequest(r, request)
var response *Resource
if err == nil {
resources[request.Id] = request.Resource
response = request
}
processHttpResponse(w, response, err, http.StatusCreated)
}

func HandleGetResource(w http.ResponseWriter, _ *http.Request, ps httprouter.Params) {
var response *Resource
var statusCode int
if val, ok := resources[ps.ByName(RESOURCE_ID)]; ok {
response = &Resource{
Id: ps.ByName(RESOURCE_ID),
Resource: val,
}
statusCode = http.StatusOK
} else {
statusCode = http.StatusNotFound
}
processHttpResponse(w, response, nil, statusCode)
}

func HandlePutResource(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
request := &Resource{}
err := processHttpRequest(r, request)
var response *Resource
if err == nil {
id := ps.ByName("id")
resources[id] = request.Resource
response = request
}
processHttpResponse(w, response, err, http.StatusOK)
}

func HandleDelResource(w http.ResponseWriter, _ *http.Request, ps httprouter.Params) {
var statusCode int
if _, ok := resources[ps.ByName(RESOURCE_ID)]; ok {
delete(resources, ps.ByName(RESOURCE_ID))
statusCode = http.StatusNoContent
} else {
statusCode = http.StatusNotFound
}
processHttpResponse(w, nil, nil, statusCode)
}

func HandleListResources(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
response := make([]Resource, len(resources))
for key, val := range resources {
response = append(response, Resource{Id: key, Resource: val})
}
processHttpResponse(w, response, nil, http.StatusOK)
}

func main() {

logger = &logrus.Logger{
Out: os.Stdout,
Formatter: &logrus.JSONFormatter{},
Hooks: make(logrus.LevelHooks),
Level: logrus.InfoLevel,
}

// Startup roles
createRolesAndPolicies()

// Create the muxer to handle the actual endpoints
router := httprouter.New()

router.POST("/resources", HandleAddResource)
router.GET("/resources/:id", HandleGetResource)
router.PUT("/resources/:id", HandlePutResource)
router.DELETE("/resources/:id", HandleDelResource)
router.GET("/resources", HandleListResources)

// Start server
resources = make(map[string]string)
host := os.Getenv(HOST)
port := os.Getenv(PORT)
logger.Infof("Server running in %v:%v", host, port)
logger.Fatal(http.ListenAndServe(host+":"+port, router))

}

// Private Helper Methods

func processHttpRequest(r *http.Request, request interface{}) error {
// Decode request if passed
if request != nil {
err := json.NewDecoder(r.Body).Decode(&request)
if err != nil {
return err
}
}

return nil
}

func processHttpResponse(w http.ResponseWriter, response interface{}, err error, responseCode int) {
if err != nil {
http.Error(w, err.Error(), responseCode)
return
}

var data []byte
if response != nil {
data, err = json.Marshal(response)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}

w.WriteHeader(responseCode)

switch responseCode {
case http.StatusOK:
w.Write(data)
case http.StatusCreated:
w.Write(data)
}

}

func createRolesAndPolicies() {
foulkonhost := os.Getenv(FOULKONHOST)
foulkonport := os.Getenv(FOULKONPORT)
url := "http://" + foulkonhost + ":" + foulkonport + "/api/v1"

createGroupFunc := func(name, path string) error {

type CreateGroupRequest struct {
Name string `json:"name, omitempty"`
Path string `json:"path, omitempty"`
}
var body *bytes.Buffer
data := &CreateGroupRequest{
Name: name,
Path: path,
}

jsonObject, _ := json.Marshal(data)
body = bytes.NewBuffer(jsonObject)

req, _ := http.NewRequest(http.MethodPost, url+"/organizations/demo/groups", body)
req.SetBasicAuth("admin", "admin")

_, err := http.DefaultClient.Do(req)
return err
}

type Statement struct {
Effect string `json:"effect, omitempty"`
Actions []string `json:"actions, omitempty"`
Resources []string `json:"resources, omitempty"`
}

createPolicyAndAttachToGroup := func(name, path, groupName string, statements []Statement) error {
client := http.DefaultClient
type CreatePolicyRequest struct {
Name string `json:"name, omitempty"`
Path string `json:"path, omitempty"`
Statements []Statement `json:"statements, omitempty"`
}
var body *bytes.Buffer
data := &CreatePolicyRequest{
Name: name,
Path: path,
Statements: statements,
}

jsonObject, _ := json.Marshal(data)
body = bytes.NewBuffer(jsonObject)

req, _ := http.NewRequest(http.MethodPost, url+"/organizations/demo/policies", body)
req.SetBasicAuth("admin", "admin")

_, err := client.Do(req)
if err != nil {
return err
}

// Attach
req, _ = http.NewRequest(http.MethodPost, url+"/organizations/demo/groups/"+groupName+"/policies/"+name, nil)
req.SetBasicAuth("admin", "admin")

_, err = client.Do(req)
if err != nil {
return err
}

return nil
}

// Create read role
err := createGroupFunc("read", "/path/")
if err != nil {
logger.Fatal(err)
}
statements := []Statement{
{
Effect: "allow",
Actions: []string{"example:list", "example:get"},
Resources: []string{
"urn:ews:foulkon:demo1:resource/list",
"urn:ews:foulkon:demo1:resource/demoresources/*",
},
},
}

err = createPolicyAndAttachToGroup("read-policy", "/path/", "read", statements)
if err != nil {
logger.Fatal(err)
}

// Create read write role
err = createGroupFunc("read-write", "/path/")
if err != nil {
logger.Fatal(err)
}
statements2 := []Statement{
{
Effect: "allow",
Actions: []string{"example:list", "example:get", "example:add", "example:update", "example:delete"},
Resources: []string{
"urn:ews:foulkon:demo1:resource/list",
"urn:ews:foulkon:demo1:resource/demoresources/*",
"urn:ews:foulkon:demo1:resource/add",
},
},
}

err = createPolicyAndAttachToGroup("read-write-policy", "/path/", "read-write", statements2)
if err != nil {
logger.Fatal(err)
}

}
Loading