Commit 29ae091a authored by Mo Firouz's avatar Mo Firouz
Browse files

Add account and storage console api. #225

parent f3d38570
Loading
Loading
Loading
Loading
+896 −101

File changed.

Preview size limit exceeded, changes collapsed.

+608 −10

File changed.

Preview size limit exceeded, changes collapsed.

+159 −9
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import "github.com/heroiclabs/nakama/api/api.proto";
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";
import "protoc-gen-swagger/options/annotations.proto";

option go_package = "console";
@@ -37,6 +38,7 @@ option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
      email: "hello@heroiclabs.com";
    };
  };
  host: "127.0.0.1:7351";
  external_docs: {
    url: "https://heroiclabs.com/docs";
    description: "Nakama server console documentation";
@@ -44,29 +46,109 @@ option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
  schemes: HTTP;
  consumes: "application/json";
  produces: "application/json";
  security_definitions: {
    security: {
      key: "BasicAuth";
      value: {
        type: TYPE_BASIC;
      }
    }
  }
  // Default security definition.
  security: {
    security_requirement: {
      key: "BasicAuth";
      value: {};
    }
  }
};

/**
 * The developer console RPC protocol service built with GRPC.
 */
service Console {
  // Authenticate a user with username+password.
  rpc Login (AuthenticateRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      post: "/v2/console/authenticate",
      body: "*"
    };
    option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = {
      security: {
        security_requirement: {}
      }
    };
  }

  // Delete all information stored for a user account.
  rpc DeleteAccount (AccountIdRequest) returns (google.protobuf.Empty) {
  rpc DeleteAccount (AccountDeleteRequest) returns (google.protobuf.Empty) {
    option (google.api.http).delete = "/v2/console/account/{id}";
  }

  // Delete all accounts.
  rpc DeleteAccounts (google.protobuf.Empty) returns (google.protobuf.Empty) {
    option (google.api.http).delete = "/v2/console/account";
  }

  // Delete a storage object.
  rpc DeleteStorageObject (DeleteStorageObjectRequest) returns (google.protobuf.Empty) {
    option (google.api.http).delete = "/v2/console/storage/{collection}/{key}/{user_id}";
  }

  // Delete all storage objects.
  rpc DeleteStorageObjects (google.protobuf.Empty) returns (google.protobuf.Empty) {
    option (google.api.http).delete = "/v2/console/storage";
  }

  // Export all information stored about a user account.
  rpc ExportAccount (AccountIdRequest) returns (AccountExport) {
    option (google.api.http).get = "/v2/console/account/{id}/export";
  }

  // Authenticate a user with username+password.
  rpc Login (AuthenticateRequest) returns (Session) {
  // Get info about a particular account
  rpc GetAccount (AccountIdRequest) returns (nakama.api.Account) {
    option (google.api.http).get = "/v2/console/account/{id}";
  }

  // Get a storage object
  rpc GetStorage (GetStorageObjectRequest) returns (StorageObject) {
    option (google.api.http).get = "/v2/console/storage/{collection}/{key}/{user_id}";
  }

  // List all recent accounts.
  rpc ListAccounts (google.protobuf.Empty) returns (AccountList) {
    option (google.api.http).get = "/v2/console/account";
  }

  // List all storage collections.
  rpc ListStorageCollections(google.protobuf.Empty) returns (StorageCollectionList) {
    option (google.api.http).get = "/v2/console/storage";
  }

  // List storage objects in a given collection.
  rpc ListStorageObjects (ListStorageObjectRequest) returns (StorageObjectList) {
    option (google.api.http) = {
      post: "/v2/console/authenticate",
      body: "*"
      get: "/v2/console/storage/{collection}",
      additional_bindings {
        get: "/v2/console/storage/{collection}/{user_id}"
      }
    };
  }

  // Create or overwrite a storage object.
  rpc WriteStorageObject (WriteStorageObjectRequest) returns (google.protobuf.Empty) {
    option (google.api.http).put = "/v2/console/storage";
  }
}

/**
 * Deletion request for a user account.
 */
message AccountDeleteRequest {
  // The unique identifier of the user account.
  string id = 1;
  // Record the user deletion - used for GDPR compliance.
  google.protobuf.BoolValue record_deletion = 2;
}

/**
@@ -99,6 +181,13 @@ message AccountIdRequest {
  string id = 1;
}

/**
 * Get the recent list of accounts.
 */
message AccountList {
  repeated nakama.api.Account accounts = 1;
}

/**
 * Authenticate against the server with username+password.
 */
@@ -109,12 +198,68 @@ message AuthenticateRequest {
  string password = 2;
}

// Delete a storage object.
message DeleteStorageObjectRequest {
  // The collection which stores the object.
  string collection = 1;
  // The key of the object within the collection.
  string key = 2;
  // ID of the user that this object belongs to.
  string user_id = 3;
}

// Retrieve a storage object.
message GetStorageObjectRequest {
  // The collection which stores the object.
  string collection = 1;
  // The key of the object within the collection.
  string key = 2;
  // The user owner of the object.
  string user_id = 3;
}

// List object belonging to a user and/or a collection.
message ListStorageObjectRequest {
 // The collection which stores the object.
  string collection = 1;
  // ID of the user that this object belongs to.
  string user_id = 2;
  // The cursor to page through results from.
  string cursor = 3; // value from StorageObjectList.cursor.
}

/**
 * List of collections available in storage.
 */
message StorageCollectionList {
  // Storage collections.
  repeated string collections = 1;
}

// A storage record.
message StorageObject {
  // The collection to store the object.
  string collection = 1;
  // The key for the object within the collection.
  string key = 2;
  // ID of the user that this object belongs to.
  string user_id = 3;
  // The value of the object.
  string value = 4;
  // The read access permissions for the object.
  int32 permission_read = 5;
  // The write access permissions for the object.
  int32 permission_write = 6;
}

/**
 * A user's session used to authenticate messages.
 * List of objects in a given collection.
 */
message Session {
  // Authentication credentials.
  string token = 1;
message StorageObjectList {
  // The list of storage objects.
  repeated StorageObject objects = 1;
  // The cursor associated with the query a page of results.
  string cursor = 2;
}

/**
@@ -134,3 +279,8 @@ message WalletLedger {
  // The UNIX time when the wallet ledger item was updated.
  google.protobuf.Timestamp update_time = 9;
}

// Write an object to storage.
message WriteStorageObjectRequest {
  StorageObject object = 1;
}
+401 −52
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
      "email": "hello@heroiclabs.com"
    }
  },
  "host": "127.0.0.1:7351",
  "schemes": [
    "http"
  ],
@@ -19,7 +20,63 @@
    "application/json"
  ],
  "paths": {
    "/v2/console/account": {
      "get": {
        "summary": "List all recent accounts.",
        "operationId": "ListAccounts",
        "responses": {
          "200": {
            "description": "",
            "schema": {
              "$ref": "#/definitions/consoleAccountList"
            }
          }
        },
        "tags": [
          "Console"
        ]
      },
      "delete": {
        "summary": "Delete all accounts.",
        "operationId": "DeleteAccounts",
        "responses": {
          "200": {
            "description": "",
            "schema": {
              "$ref": "#/definitions/protobufEmpty"
            }
          }
        },
        "tags": [
          "Console"
        ]
      }
    },
    "/v2/console/account/{id}": {
      "get": {
        "summary": "Get info about a particular account",
        "operationId": "GetAccount",
        "responses": {
          "200": {
            "description": "",
            "schema": {
              "$ref": "#/definitions/apiAccount"
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "description": "The unique identifier of the user account.",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "tags": [
          "Console"
        ]
      },
      "delete": {
        "summary": "Delete all information stored for a user account.",
        "operationId": "DeleteAccount",
@@ -38,6 +95,14 @@
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "record_deletion",
            "description": "Record the user deletion - used for GDPR compliance.",
            "in": "query",
            "required": false,
            "type": "boolean",
            "format": "boolean"
          }
        ],
        "tags": [
@@ -79,7 +144,7 @@
          "200": {
            "description": "",
            "schema": {
              "$ref": "#/definitions/nakamaconsoleSession"
              "$ref": "#/definitions/protobufEmpty"
            }
          }
        },
@@ -93,6 +158,216 @@
            }
          }
        ],
        "tags": [
          "Console"
        ],
        "security": [
          {
            "": []
          }
        ]
      }
    },
    "/v2/console/storage": {
      "get": {
        "summary": "List all storage collections.",
        "operationId": "ListStorageCollections",
        "responses": {
          "200": {
            "description": "",
            "schema": {
              "$ref": "#/definitions/consoleStorageCollectionList"
            }
          }
        },
        "tags": [
          "Console"
        ]
      },
      "delete": {
        "summary": "Delete all storage objects.",
        "operationId": "DeleteStorageObjects",
        "responses": {
          "200": {
            "description": "",
            "schema": {
              "$ref": "#/definitions/protobufEmpty"
            }
          }
        },
        "tags": [
          "Console"
        ]
      },
      "put": {
        "summary": "Create or overwrite a storage object.",
        "operationId": "WriteStorageObject",
        "responses": {
          "200": {
            "description": "",
            "schema": {
              "$ref": "#/definitions/protobufEmpty"
            }
          }
        },
        "tags": [
          "Console"
        ]
      }
    },
    "/v2/console/storage/{collection}": {
      "get": {
        "summary": "List storage objects in a given collection.",
        "operationId": "ListStorageObjects",
        "responses": {
          "200": {
            "description": "",
            "schema": {
              "$ref": "#/definitions/nakamaconsoleStorageObjectList"
            }
          }
        },
        "parameters": [
          {
            "name": "collection",
            "description": "The collection which stores the object.",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "user_id",
            "description": "ID of the user that this object belongs to.",
            "in": "query",
            "required": false,
            "type": "string"
          },
          {
            "name": "cursor",
            "description": "The cursor to page through results from.",
            "in": "query",
            "required": false,
            "type": "string"
          }
        ],
        "tags": [
          "Console"
        ]
      }
    },
    "/v2/console/storage/{collection}/{key}/{user_id}": {
      "get": {
        "summary": "Get a storage object",
        "operationId": "GetStorage",
        "responses": {
          "200": {
            "description": "",
            "schema": {
              "$ref": "#/definitions/nakamaconsoleStorageObject"
            }
          }
        },
        "parameters": [
          {
            "name": "collection",
            "description": "The collection which stores the object.",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "key",
            "description": "The key of the object within the collection.",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "user_id",
            "description": "The user owner of the object.",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "tags": [
          "Console"
        ]
      },
      "delete": {
        "summary": "Delete a storage object.",
        "operationId": "DeleteStorageObject",
        "responses": {
          "200": {
            "description": "",
            "schema": {
              "$ref": "#/definitions/protobufEmpty"
            }
          }
        },
        "parameters": [
          {
            "name": "collection",
            "description": "The collection which stores the object.",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "key",
            "description": "The key of the object within the collection.",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "user_id",
            "description": "ID of the user that this object belongs to.",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ],
        "tags": [
          "Console"
        ]
      }
    },
    "/v2/console/storage/{collection}/{user_id}": {
      "get": {
        "summary": "List storage objects in a given collection.",
        "operationId": "ListStorageObjects2",
        "responses": {
          "200": {
            "description": "",
            "schema": {
              "$ref": "#/definitions/nakamaconsoleStorageObjectList"
            }
          }
        },
        "parameters": [
          {
            "name": "collection",
            "description": "The collection which stores the object.",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "user_id",
            "description": "ID of the user that this object belongs to.",
            "in": "path",
            "required": true,
            "type": "string"
          },
          {
            "name": "cursor",
            "description": "The cursor to page through results from.",
            "in": "query",
            "required": false,
            "type": "string"
          }
        ],
        "tags": [
          "Console"
        ]
@@ -358,52 +633,6 @@
      },
      "description": "A notification in the server."
    },
    "apiStorageObject": {
      "type": "object",
      "properties": {
        "collection": {
          "type": "string",
          "description": "The collection which stores the object."
        },
        "key": {
          "type": "string",
          "description": "The key of the object within the collection."
        },
        "user_id": {
          "type": "string",
          "description": "The user owner of the object."
        },
        "value": {
          "type": "string",
          "description": "The value of the object."
        },
        "version": {
          "type": "string",
          "description": "The version hash of the object."
        },
        "permission_read": {
          "type": "integer",
          "format": "int32",
          "description": "The read access permissions for the object."
        },
        "permission_write": {
          "type": "integer",
          "format": "int32",
          "description": "The write access permissions for the object."
        },
        "create_time": {
          "type": "string",
          "format": "date-time",
          "description": "The UNIX time when the object was created."
        },
        "update_time": {
          "type": "string",
          "format": "date-time",
          "description": "The UNIX time when the object was last updated."
        }
      },
      "description": "An object within the storage engine."
    },
    "apiUser": {
      "type": "object",
      "properties": {
@@ -488,7 +717,7 @@
        "objects": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/apiStorageObject"
            "$ref": "#/definitions/nakamaapiStorageObject"
          },
          "description": "The user's storage."
        },
@@ -537,6 +766,18 @@
      },
      "description": "*\nAn export of all information stored for a user account."
    },
    "consoleAccountList": {
      "type": "object",
      "properties": {
        "accounts": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/apiAccount"
          }
        }
      },
      "description": "*\nGet the recent list of accounts."
    },
    "consoleAuthenticateRequest": {
      "type": "object",
      "properties": {
@@ -551,6 +792,19 @@
      },
      "description": "*\nAuthenticate against the server with username+password."
    },
    "consoleStorageCollectionList": {
      "type": "object",
      "properties": {
        "collections": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Storage collections."
        }
      },
      "description": "*\nList of collections available in storage."
    },
    "consoleWalletLedger": {
      "type": "object",
      "properties": {
@@ -583,15 +837,100 @@
      },
      "description": "*\nA wallet ledger item representing a change to the user's wallet."
    },
    "nakamaconsoleSession": {
    "nakamaapiStorageObject": {
      "type": "object",
      "properties": {
        "token": {
        "collection": {
          "type": "string",
          "description": "The collection which stores the object."
        },
        "key": {
          "type": "string",
          "description": "The key of the object within the collection."
        },
        "user_id": {
          "type": "string",
          "description": "The user owner of the object."
        },
        "value": {
          "type": "string",
          "description": "The value of the object."
        },
        "version": {
          "type": "string",
          "description": "Authentication credentials."
          "description": "The version hash of the object."
        },
        "permission_read": {
          "type": "integer",
          "format": "int32",
          "description": "The read access permissions for the object."
        },
        "permission_write": {
          "type": "integer",
          "format": "int32",
          "description": "The write access permissions for the object."
        },
        "create_time": {
          "type": "string",
          "format": "date-time",
          "description": "The UNIX time when the object was created."
        },
        "update_time": {
          "type": "string",
          "format": "date-time",
          "description": "The UNIX time when the object was last updated."
        }
      },
      "description": "*\nA user's session used to authenticate messages."
      "description": "An object within the storage engine."
    },
    "nakamaconsoleStorageObject": {
      "type": "object",
      "properties": {
        "collection": {
          "type": "string",
          "description": "The collection to store the object."
        },
        "key": {
          "type": "string",
          "description": "The key for the object within the collection."
        },
        "user_id": {
          "type": "string",
          "description": "ID of the user that this object belongs to."
        },
        "value": {
          "type": "string",
          "description": "The value of the object."
        },
        "permission_read": {
          "type": "integer",
          "format": "int32",
          "description": "The read access permissions for the object."
        },
        "permission_write": {
          "type": "integer",
          "format": "int32",
          "description": "The write access permissions for the object."
        }
      },
      "description": "A storage record."
    },
    "nakamaconsoleStorageObjectList": {
      "type": "object",
      "properties": {
        "objects": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/nakamaconsoleStorageObject"
          },
          "description": "The list of storage objects."
        },
        "cursor": {
          "type": "string",
          "description": "The cursor associated with the query a page of results."
        }
      },
      "description": "*\nList of objects in a given collection."
    },
    "protobufEmpty": {
      "type": "object",
@@ -599,6 +938,16 @@
      "title": "A generic empty message that you can re-use to avoid defining duplicated\nempty messages in your APIs. A typical example is to use it as the request\nor the response type of an API method. For instance:"
    }
  },
  "securityDefinitions": {
    "BasicAuth": {
      "type": "basic"
    }
  },
  "security": [
    {
      "BasicAuth": []
    }
  ],
  "externalDocs": {
    "description": "Nakama server console documentation",
    "url": "https://heroiclabs.com/docs"
+14 −2
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import (
	"net/http"
	"time"

	"github.com/golang/protobuf/ptypes/empty"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
@@ -137,6 +138,12 @@ func (s *ConsoleServer) Stop() {
func consoleInterceptorFunc(logger *zap.Logger, config Config) func(context.Context, interface{}, *grpc.UnaryServerInfo, grpc.UnaryHandler) (interface{}, error) {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {

		switch info.FullMethod {
		// skip authentication check for Login endpoint
		case "/nakama.console.Console/Login":
			return handler(ctx, req)
		}

		md, ok := metadata.FromIncomingContext(ctx)
		if !ok {
			logger.Error("Cannot extract metadata from incoming context")
@@ -164,6 +171,11 @@ func consoleInterceptorFunc(logger *zap.Logger, config Config) func(context.Cont
	}
}

func (s *ConsoleServer) Login(context.Context, *console.AuthenticateRequest) (*console.Session, error) {
	return nil, nil
func (s *ConsoleServer) Login(ctx context.Context, in *console.AuthenticateRequest) (*empty.Empty, error) {
	username := s.config.GetConsole().Username
	password := s.config.GetConsole().Password
	if in.Username == username && in.Password == password {
		return &empty.Empty{}, nil
	}
	return nil, status.Error(codes.Unauthenticated, "Console authentication invalid.")
}
Loading