Skip to content
Snippets Groups Projects
Commit 43c55f83 authored by Steffen Schulze's avatar Steffen Schulze
Browse files

Merge branch 'gitlab/merge-request-3' into 'main'

Sprint 5

See merge request eclipse/xfsc/common-services/didcomm-connector!4
parents 71cc3185 81b2a589
No related branches found
No related tags found
No related merge requests found
Showing
with 354 additions and 126 deletions
......@@ -5,7 +5,7 @@ tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ."
cmd = "go build -o ./tmp/main ./cmd/api/"
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
......@@ -25,7 +25,7 @@ tmp_dir = "tmp"
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = false
stop_on_error = true
[color]
app = ""
......
.env 0 → 100644
# Cassandra Database
CASSANDRA_USERNAME=cassandra
CASSANDRA_PASSWORD=cassandra
CASSANDRA_HOST=cassandra
CASSANDRA_PORT=9042
CASSANDRA_KEYSPACE=dcc
# Service
APP_PORT=9090
\ No newline at end of file
......@@ -5,6 +5,7 @@
*.so
*.dylib
didcommconnector
__debug_bin*
# Test binary, build with `go test -c`
*.test
......@@ -15,3 +16,5 @@ tmp/
# didcomm-rust lib is generated with make file
didcomm/lib/
# swagger auto generated files
docs/
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch test function",
"type": "go",
"request": "launch",
"mode": "test",
"program": "${workspaceFolder}/cmd/api",
"env": {
"LD_LIBRARY_PATH": "../didcomm/lib"
},
"args": [
"-test.run",
"TestEnv"
]
},
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/api",
"env": {
"LD_LIBRARY_PATH": "../../didcomm/lib"
},
"envFile": "${workspaceFolder}/app.env",
}
]
}
\ No newline at end of file
{
"go.testEnvVars": {
"LD_LIBRARY_PATH": "../didcomm/lib"
}
}
\ No newline at end of file
......@@ -35,6 +35,10 @@ How to run the application:
- To start the application execute `make dev`.
## Test
To run all test execute `make test`
### swagger
The swagger is available at the URL: `localhost:9090/swagger/index.html`
......@@ -67,3 +71,6 @@ TODO: For open source projects, say how it is licensed.
The project is in an early phase of development. Therefore it cannot be used as it is now.
The API is not final.
### Debugging
While under WSL to be able to use the WS, please do not use localhost from the Windows machine. Instead use the given ip address given by `ip add | grep "eth0"`
\ No newline at end of file
package controller
package main
import (
"fmt"
"net/http"
"net/url"
"gaiax/didcommconnector/service"
"github.com/gin-gonic/gin"
"github.com/gocql/gocql"
)
type ConnectionController struct{}
// PingExample godoc
// @Summary Create a new connection
// @Schemes
......@@ -20,8 +20,16 @@ type ConnectionController struct{}
// @Param did path string true "DID"
// @Success 201 {object} model.Connection
// @Router /connections/{did} [post]
func (cc ConnectionController) CreateConnectionEndpoint(context *gin.Context) {
context.Status(http.StatusNotImplemented)
func (app *application) CreateConnection(context *gin.Context) {
did := context.Param("did")
cs := service.CassandraService{}
_, err := cs.Insert(did)
if err != nil {
app.logger.Error("CreateConnectionEndpoint", err)
context.Status(http.StatusBadRequest)
}
context.Status(http.StatusOK)
}
// PingExample godoc
......@@ -34,9 +42,9 @@ func (cc ConnectionController) CreateConnectionEndpoint(context *gin.Context) {
// @Param did body model.Connection true "DID"
// @Success 204 {string} string "Updated"
// @Router /connections [patch]
func (cc ConnectionController) UpdateConnection(context *gin.Context) {
func (app *application) UpdateConnection(context *gin.Context) {
did := context.Param("did")
fmt.Println("Given DID: " + did)
app.logger.Info("UpdateConnection", "did", did)
context.Status(http.StatusNotImplemented)
}
......@@ -50,10 +58,18 @@ func (cc ConnectionController) UpdateConnection(context *gin.Context) {
// @Param did path string true "DID"
// @Success 204 {string} string "Deleted"
// @Router /connections/{did} [delete]
func (cc ConnectionController) DeleteConnection(context *gin.Context) {
func (app *application) DeleteConnection(context *gin.Context) {
did := context.Param("did")
fmt.Println("Given DID: " + did)
context.Status(http.StatusNotImplemented)
cs := service.CassandraService{}
d, err := gocql.ParseUUID(did)
cs.Delete(d)
if err != nil {
app.logger.Error("DeleteConnection", err)
context.Status(http.StatusBadRequest)
}
context.Status(http.StatusOK)
}
// PingExample godoc
......@@ -66,14 +82,14 @@ func (cc ConnectionController) DeleteConnection(context *gin.Context) {
// @Param did path string true "DID"
// @Success 200 {object} model.Connection
// @Router /connections/{did} [get]
func (cc ConnectionController) ConnectionInformation(context *gin.Context) {
func (app *application) ConnectionInformation(context *gin.Context) {
did := context.Param("did")
did, err := url.QueryUnescape(did)
if err != nil {
context.Status(http.StatusBadRequest)
return
}
fmt.Println("Given DID:", did)
app.logger.Info("ConnectionInformation", "did", did)
context.Status(http.StatusNotImplemented)
}
......@@ -86,23 +102,28 @@ func (cc ConnectionController) ConnectionInformation(context *gin.Context) {
// @Produce json
// @Success 200 {array} model.Connection
// @Router /connections [get]
func (cc ConnectionController) ListConnections(context *gin.Context) {
context.Status(http.StatusNotImplemented)
func (app *application) ListConnections(context *gin.Context) {
cs := service.CassandraService{}
all, err := cs.GetAll()
if err != nil {
context.Status(http.StatusBadRequest)
}
context.IndentedJSON(http.StatusOK, all)
}
// PingExample godoc
// @Summary Get connection status
// @Schemes
// @Description Returns connection status (more to be added)
// @Description Returns if a connections is blocked or not
// @Tags Connections
// @Accept json
// @Produce json
// @Param did path string true "DID"
// @Success 200 {object} model.ConnectionStatus
// @Router /connections/status/{did} [get]
func (cc ConnectionController) ConnectionStatus(context *gin.Context) {
func (app *application) ConnectionStatus(context *gin.Context) {
did := context.Param("did")
fmt.Println("Given DID: " + did)
app.logger.Info("ConnectionStatus", "did", did)
context.Status(http.StatusNotImplemented)
}
......@@ -116,8 +137,8 @@ func (cc ConnectionController) ConnectionStatus(context *gin.Context) {
// @Param did path string true "DID"
// @Success 204 {string} string "Blocked"
// @Router /connections/block/{did} [post]
func (cc ConnectionController) BlockConnection(context *gin.Context) {
func (app *application) BlockConnection(context *gin.Context) {
did := context.Param("did")
fmt.Println("Given DID: " + did)
app.logger.Info("BlockConnection", "did", did)
context.Status(http.StatusNotImplemented)
}
package main
import (
"gaiax/didcommconnector/helper"
"gaiax/didcommconnector/mediator"
"log/slog"
"os"
"strconv"
)
// @title DIDComm Connector API
// @version 1.0
// @description The DIDCommConnector can be used as a Mediator and Connection Management Service by parties who want to set up trust with another party. The DIDCommConnector uses DIDComm v2 and provides a message layer and a management component for the following two use cases: - Pairing a cloud solution with a smartphone / app solution - DIDComm v2 based message protocols
// @description - Pairing a cloud solution with a smartphone / app solution - DIDComm v2 based message protocols
// @description - DIDComm v2 based message protocols
// @termsOfService http://swagger.io/terms/
// @contact.name API Support
// @contact.url http://www.change.this/url
// @contact.email email@todo.io
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @host localhost:9090
// @BasePath /
// @securityDefinitions.basic BasicAuth
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name Authorization
// @description Description for what is this security definition being used
// @securitydefinitions.oauth2.application OAuth2Application
// @tokenUrl https://example.com/oauth/token
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information
// @securitydefinitions.oauth2.implicit OAuth2Implicit
// @authorizationUrl https://example.com/oauth/authorize
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information
// @securitydefinitions.oauth2.password OAuth2Password
// @tokenUrl https://example.com/oauth/token
// @scope.read Grants read access
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information
// @securitydefinitions.oauth2.accessCode OAuth2AccessCode
// @tokenUrl https://example.com/oauth/token
// @authorizationUrl https://example.com/oauth/authorize
// @scope.admin Grants read and write access to administrative information
// TODO: use viper for config
type config struct {
port int
env string
url string
}
type application struct {
logger *slog.Logger
mediator *mediator.Mediator
config config
}
func main() {
// TODO: outsource to helper file
port_string := helper.APP_PORT.GetEnvironmentVariable()
port, err := strconv.Atoi(port_string)
if err != nil {
panic(err)
}
cfg := config{
port: port,
env: "dev",
url: "https://localhost:9090",
}
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
app := application{
// TODO: Change Handler in prod
logger: logger,
config: cfg,
mediator: mediator.NewMediator(logger),
}
router := app.NewRouter()
err = router.Run(":" + port_string)
if err != nil {
panic(err)
}
}
package main
import (
"encoding/json"
"errors"
"fmt"
"gaiax/didcommconnector/didcomm"
intErr "gaiax/didcommconnector/internal/errors"
"gaiax/didcommconnector/protocol"
"io"
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
)
// PingExample godoc
// @Summary TODO
// @Schemes TODO
// @Description TODO
// @Tags Message
// @Accept json
// @Produce json
// @Success 200 {string} test
// @Router /message [patch]
func (app *application) ReceiveMessage(context *gin.Context) {
// TODO: replace with middleware
mediator := app.mediator
body, err := io.ReadAll(context.Request.Body)
if err != nil {
context.String(500, "Error reading request body")
return
}
bodyString := string(body)
msg, err := mediator.UnpackMessage(bodyString)
// TODO: check created_time and expiration_time is valid
if err != nil {
app.logger.Error("ReceiveMessage", "Error unpacking message:", err)
context.String(http.StatusInternalServerError, "Error unpacking message")
return
}
// handle message
var responseMsg didcomm.Message = didcomm.Message{}
if strings.HasPrefix(msg.Type, protocol.PIURI_COORDINATE_MEDIATION) {
coordinateMediation := protocol.NewCoordinateMediation(mediator)
responseMsg, err = coordinateMediation.Handle(msg)
} else if strings.HasPrefix(msg.Type, protocol.PIURI_TRUST_PING) {
trustping := protocol.NewTrustPing(mediator)
responseMsg, err = trustping.Handle(msg)
if err != nil {
switch {
case errors.Is(err, intErr.ErrNoPingResponseRequested):
context.String(http.StatusOK, "")
default:
context.String(http.StatusInternalServerError, "Error handling trust ping")
}
return
}
} else if strings.HasPrefix(msg.Type, protocol.PIURI_ROUTING) {
routing := protocol.NewRouting(mediator)
responseMsg, err = routing.Handle(msg)
if responseMsg.Type == "" {
context.String(http.StatusOK, "")
return
}
} else if strings.HasPrefix(msg.Type, protocol.PIURI_MESSAGEPICKUP) {
messagePickup := protocol.NewMessagePickup(mediator)
responseMsg, err = messagePickup.Handle(msg)
} else {
app.logger.Warn("Message type not handled yet.")
responseMsg = protocol.PR_UNKNOWN_MESSAGE_TYPE
}
if err != nil {
app.logger.Error("Handling Error", "Error while handling a didcomm request", err)
}
responseMsg.To = &[]string{*msg.From}
responseMsg.From = &app.mediator.Config.Did
t := uint64(time.Now().UTC().UnixNano())
responseMsg.CreatedTime = &t
// TODO: decide if to pack encryptet or to use plain text with config
//packMsg, err := mediator.PackEncryptedMessage(responseMsg, *msg.From, mediator.Did)
packMsg, err := mediator.PackPlainMessage(responseMsg)
if err != nil {
context.String(http.StatusInternalServerError, "Error packing message")
return
}
var jsonMap map[string]interface{}
err = json.Unmarshal([]byte(packMsg), &jsonMap)
if err != nil {
context.String(http.StatusInternalServerError, "Error marshling message")
return
}
context.JSON(http.StatusOK, jsonMap)
}
func (app *application) InvitationMessage(context *gin.Context) {
mediator := app.mediator
oob := protocol.NewOutOfBand(mediator)
msg, err := oob.Handle()
if err != nil {
context.String(http.StatusInternalServerError, "Error handling out of band")
return
} else {
oob := fmt.Sprintf("%s?_oob=%s", app.config.url, msg)
context.String(http.StatusOK, oob)
}
}
package server
package main
import (
"gaiax/didcommconnector/controller"
_ "gaiax/didcommconnector/docs"
"github.com/gin-gonic/gin"
sloggin "github.com/samber/slog-gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
func NewRouter() *gin.Engine {
func (app *application) NewRouter() *gin.Engine {
router := gin.New()
router.Use(gin.Logger())
// hello world
helloWorld := new(controller.HelloWorldController)
router.GET("/helloworld", helloWorld.HelloWorld)
router.Use(sloggin.New(app.logger))
// connection
cc := new(controller.ConnectionController)
connectionsGroup := router.Group("connections")
{
router.POST("/connections", cc.CreateConnectionEndpoint)
connectionsGroup.PATCH(":did", cc.UpdateConnection)
connectionsGroup.DELETE(":did", cc.DeleteConnection)
connectionsGroup.GET(":did", cc.ConnectionInformation)
connectionsGroup.GET("", cc.ListConnections)
connectionsGroup.GET("status/:did", cc.ConnectionStatus)
router.POST("/connections/block/:did", cc.BlockConnection)
}
connectionsGroup.POST(":did", app.CreateConnection)
connectionsGroup.PATCH(":did", app.UpdateConnection)
connectionsGroup.DELETE(":did", app.DeleteConnection)
connectionsGroup.GET(":did", app.ConnectionInformation)
connectionsGroup.GET("", app.ListConnections)
connectionsGroup.GET("status/:did", app.ConnectionStatus)
connectionsGroup.POST("block/:did", app.BlockConnection)
// messages
mc := new(controller.MessageController)
messagesGroup := router.Group("message")
{
messagesGroup.POST("receive", mc.ReceiveMessage)
}
messagesGroup.POST("receive", app.ReceiveMessage)
messagesGroup.GET("invitation", app.InvitationMessage)
// synchronization
sc := new(SynchronizationController)
syncGroup := router.Group("synchronization")
syncGroup.GET("", sc.Synchronization)
// swagger
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
// TODO: add other controllers to the router
return router
}
package controller
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
......@@ -28,8 +27,6 @@ var clients = make(map[*websocket.Conn]bool)
func (sc SynchronizationController) Synchronization(context *gin.Context) {
fmt.Println("websocket called!")
conn, err := upgrader.Upgrade(context.Writer, context.Request, nil)
if err != nil {
context.JSON(http.StatusInternalServerError, gin.H{
......@@ -51,6 +48,5 @@ func handleWebSocketConnection(conn *websocket.Conn) {
delete(clients, conn)
break
}
fmt.Println(message)
}
}
package controller
type DidCommController struct{}
// TODO: add routes
package controller
import (
"net/http"
"github.com/gin-gonic/gin"
)
type HelloWorldController struct{}
func (h HelloWorldController) HelloWorld(context *gin.Context) {
context.String(http.StatusOK, "Hello World!")
}
package controller
import (
"encoding/json"
"fmt"
"gaiax/didcommconnector/protocol"
"gaiax/didcommconnector/utils"
"io"
"net/http"
"github.com/gin-gonic/gin"
)
type MessageController struct{}
// PingExample godoc
// @Summary TODO
// @Schemes TODO
// @Description TODO
// @Tags Message
// @Accept json
// @Produce json
// @Success 200 {string} test
// @Router /message [patch]
func (cc MessageController) ReceiveMessage(context *gin.Context) {
// dcomm := didcomm.CreateDidcommInstance()
// options := didcomm.UnpackOptions{
// ExpectDecryptByAllKeys: false,
// UnwrapReWrappingForward: false,
// }
// msgCh := make(chan didcomm.Message, 1)
// errCh := make(chan didcomm.UnpackErrorPair, 1)
// unpackCB := &didcomm.UnpackResultCallback{}
// unpackCB.New(msgCh, errCh)
// go dcomm.Unpack(bodyString, options, unpackCB)
// select {
// case err := <-errCh:
// fmt.Println("Error unpacking message:", err)
// context.String(http.StatusInternalServerError, "Error unpacking message")
// case message := <-msgCh:
// fmt.Println("Message received:", message.To)
// context.Status(http.StatusOK)
// }
body, err := io.ReadAll(context.Request.Body)
if err != nil {
context.String(500, "Error reading request body")
return
}
bodyString := string(body)
msg, err := utils.UnpackMessage(bodyString)
if err != nil {
fmt.Println("Error unpacking message:", err)
context.String(http.StatusInternalServerError, "Error unpacking message")
return
}
switch msg.Type {
case "https://didcomm.org/coordinate-mediation/3.0/mediate-request":
responseMsg, err := protocol.HandleCoordinateMediation(msg)
if err != nil {
context.String(http.StatusInternalServerError, "Error handling coordinate mediation")
return
}
packMsg, err := utils.PackPlainMessage(responseMsg, "did:example:mediator")
if err != nil {
context.String(http.StatusInternalServerError, "Error packing message")
return
} else {
var jsonMap map[string]interface{}
json.Unmarshal([]byte(packMsg), &jsonMap)
context.JSON(http.StatusOK, jsonMap)
}
default:
context.String(http.StatusBadRequest, "Unknown message type")
}
}
File deleted
File deleted
File deleted
2190465669
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment