# Access Controls

**URL:** https://heroiclabs.com/docs/nakama/concepts/storage/permissions/
**Summary:** The storage engine has two features which control access to objects. Object ownership and access permissions.
**Keywords:** object ownerships, object permissions
**Categories:** nakama, permissions, storage

---


# Access Controls

The storage engine has two features which control access to objects. Object ownership and access permissions.

## Object ownership

A storage object is created with an owner. The owner is either the user who created it, the system owner, or an owner assigned when the object is created with the [code runtime](../../../server-framework/typescript-runtime/function-reference/#Storage).

When writing an object from the code runtime the owner is implied to be the system user unless explicitly set. A user who writes a storage object from a client is set as the owner by default.

System owned objects are created under the system user, represented in the server by a nil UUID (`00000000-0000-0000-0000-000000000000`). An object which is system owned must have public read [access permissions](#object-permissions) before it can be fetched by clients.

These code examples show how to retrieve an object owned by the system (marked with public read).

{{< code type="client" >}}
```bash
curl -X POST "http://127.0.0.1:7350/v2/storage" \
  -H 'Authorization: Bearer <session token>' \
  -d '{
    "object_ids": [
      {
        "collection": "configuration",
        "key": "config",
      }
    ]
  }'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const objects = await client.readStorageObjects(session, {
  "object_ids": [{
    "collection": "configurations",
    "key": "config"
  }]
});
console.info("Read objects: %o", objects);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
var result = await client.ReadStorageObjectsAsync(session, new StorageObjectId {
    Collection = "configuration",
    Key = "config"
});

Console.WriteLine("Read objects: [{0}]", string.Join(",\n  ", result.Objects));
```
{{< / code >}}

{{< code type="client" >}}
```swift
let result = await client.readStorageObjects(session: session, ids: [StorageObjectId(collection: "configuration", key: "config")])

debugPrint("Read objects:", result.objects)
```
{{< / code >}}

{{< code type="client" >}}
```dart
final result = await client.readStorageObject(
  session: session,
  collection: 'configuration',
  key: 'config',
);

print('Read objects: ${result}');
```
{{< / code >}}

{{< code type="client" >}}
```cpp
auto successCallback = [](const NStorageObjects& objects)
{
  for (auto& object : objects)
    {
        std::cout << "Object key: " << object.key << ", value: " << object.value << std::endl;
    }
};

std::vector<NReadStorageObjectId> objectIds;
NReadStorageObjectId objectId;
objectId.collection = "configurations";
objectId.key = "config";
objectIds.push_back(objectId);
client->readStorageObjects(session, objectIds, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```java
StorageObjectId objectId = new StorageObjectId("configuration");
objectId.setKey("config");
StorageObjects objects = client.readStorageObjects(session, objectId).get();
System.out.format("Read objects %s", objects.getObjectsList().toString());
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var result : NakamaAPI.ApiStorageObjects = yield(client.read_storage_objects_async(session, [
    NakamaStorageObjectId.new("configuration", "config")
]), "completed")

if result.is_exception():
    print("An error occurred: %s" % result)
    return

print("Read objects:")

for o in result.objects:
    print("%s" % o)
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
var result : NakamaAPI.ApiStorageObjects = await client.read_storage_objects_async(session, [
    NakamaStorageObjectId.new("configuration", "config")
])

if result.is_exception():
    print("An error occurred: %s" % result)
    return

print("Read objects:")

for o in result.objects:
    print("%s" % o)
```
{{< / code >}}

{{< code type="client" >}}
```shell
POST /v2/storage
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "object_ids": [
    {
      "collection": "configuration",
      "key": "config"
    }
  ]
}
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local objects_ids = {
  {
    collection = "configuration",
    key = "config"
  }
}

local result = client.read_storage_objects(objects_ids)

if result.error then
  print(result.message)
  return
end

for _,object in ipairs(result.objects) do
  pprint(object)
end
```
{{< / code >}}

You can also use the code runtime to fetch an object. The code runtime is exempt from the standard rules around access permissions because it is run by the server as authoritative code.

{{< code type="server" >}}
```lua
local object_ids = {
  { collection = "configuration", key = "config", user_id = nil },
}

local objects = nk.storage_read(object_ids)

for _, o in ipairs(objects) do
  local message = ("value: %q"):format(o.value)
  nk.logger_info(message)
end
```
{{< / code >}}

{{< code type="server" >}}
```go
objectIds := []*runtime.StorageRead{
    &runtime.StorageRead{
        Collection: "configuration",
        Key: "config",
    },
}

objects, err := nk.StorageRead(ctx, objectIds)

if err != nil {
    // Handle error.
} else {
    for _, object := range objects {
        logger.Info("value: %s", object.Value)
    }
}
```
{{< / code >}}

{{< code type="server" >}}
```typescript
let objectIds: nkruntime.StorageReadRequest[] = [
  { collection: 'configuration', key: 'config', userId: '<uuid>' },
]

let objects: nkruntime.StorageObject[] = [];

try {
  let objects = nk.storageRead(objectIds);
} catch (error) {
  // Handle error
}

objects.forEach(function (o) {
  logger.info('value: %q', o.value);
});
```
{{< / code >}}

## Object permissions

An object has permissions which are enforced for the owner of that object when writing or updating the object:

- **ReadPermission**  can have `Public Read` (`2`), `Owner Read` (`1`), or `No Read` (`0`).
- **WritePermission**  can have `Owner Write` (`1`), or `No Write` (`0`).

{{< note "info" >}}
These permissions are ignored when interacting with the storage engine via the code runtime as the server is authoritative and can always read/write objects. As a result, `No Read` / `No Write` permissions mean that no client can read/write the object, but the server always can.
{{< /note >}}

Objects with permission `Owner Read` and `Owner Write` may only be accessed or modified by the user who owns it. No other client may access the object.

`Public Read` means that any user can read that object. This is very useful for gameplay where users need to share their game state or parts of it with other users. For example you might have users with their own `"Army"` object who want to battle each other. Each user can write their own object with public read and it can be read by the other user so that it can be rendered on each others' devices.

When modifying objects from the client, the default permission of a object is set to `Owner Read` and `Owner Write`.
When modifying objects from the code runtime, the default permission of an object is set to `No Read` and `No Write`.
When listing objects you'll only get back objects with appropriate permissions.

{{< code type="client" >}}
```bash
# "2" refers to Public Read permission
# "1" refers to Owner Write permission
curl -X PUT "http://127.0.0.1:7350/v2/storage" \
  -H 'Authorization: Bearer <session token>' \
  -d '{
    "objects": [
      {
        "collection": "battle",
        "key": "army",
        "value": "{\"soldiers\": 50}",
        "permission_read": 2,
        "permission_write": 1
      }
    ]
  }'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
var army_setup = { "soldiers": 50 };
// "2" refers to Public Read permission
// "1" refers to Owner Write permission
const object_ids = await client.writeStorageObjects(session, [
  {
    "collection": "saves",
    "key": "savegame",
    "value": army_setup,
    "permission_read": 2,
    "permission_write": 1
  }
]);

console.info("Stored objects: %o", object_ids);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
var armySetup = "{ \"soldiers\": 50 }";
// "2" refers to Public Read permission
// "1" refers to Owner Write permission
var result = await client.WriteStorageObjectsAsync(session, new WriteStorageObject
{
    Collection = "saves",
    Key = "savegame",
    Value = armySetup,
    PermissionRead = 2,
    PermissionWrite = 1
});

Console.WriteLine("Stored objects: [{0}]", string.Join(",\n  ", result.Objects));
```
{{< / code >}}

{{< code type="client" >}}
```swift
let armySetup = "{ \"soldiers\": 50 }"
let objects = [WriteStorageObject(
    collection: "saves",
    key: "savegame",
    value: armySetup,
    permissionRead: .publicRead,
    permissionWrite: .ownerWrite
)]
let result = try await client.writeStorageObjects(session: session, objects: objects)

debugPrint("Stored objects:", result.acks)
```
{{< / code >}}

{{< code type="client" >}}
```dart
final armySetup = '{"soldiers": 50}';
final result = await client.writeStorageObject(
  session: session,
  collection: 'saves',
  key: 'savegame',
  value: armySetup,
  writePermission: StorageWritePermission.ownerWrite,
  readPermission: StorageReadPermission.publicRead,
);

print('Stored objects: ${result.acks}');
```
{{< / code >}}

{{< code type="client" >}}
```cpp
auto successCallback = [](const NStorageObjectAcks& acks)
{
};

std::vector<NStorageObjectWrite> objects;
NStorageObjectWrite object;
object.collection = "saves";
object.key = "savegame";
object.value = "{ \"soldiers\": 50 }";
object.permissionRead = NStoragePermissionRead::PUBLIC_READ;   // Public Read permission
object.permissionWrite = NStoragePermissionWrite::OWNER_WRITE; // Owner Write permission
objects.push_back(object);
client->writeStorageObjects(session, objects, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```java
String armySetup = "{ \"soldiers\": 50 }";
StorageObjectWrite object = new StorageObjectWrite("saves", "savegame", armySetup, PermissionRead.PUBLIC_READ, PermissionWrite.OWNER_WRITE);
StorageObjectAcks acks = client.writeStorageObjects(session, object).get();
System.out.format("Stored objects %s", acks.getAcksList());
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var army_setup = "{ \"soldiers\": 50 }";
# "2" refers to Public Read permission
# "1" refers to Owner Write permission
var acks : NakamaAPI.ApiStorageObjectAcks = yield(client.write_storage_objects_async(session, [
    NakamaWriteStorageObject.new("saves", "savegame", 2, 1, army_setup, "")
]), "completed")

if acks.is_exception():
    print("An error occurred: %s" % acks)
    return

print("Stored objects: %s" % [acks.acks])
```
{{< / code >}}

{{< code type="client" framework="godot4" >}}
```gdscript
var army_setup = "{ \"soldiers\": 50 }";
# "2" refers to Public Read permission
# "1" refers to Owner Write permission
var acks : NakamaAPI.ApiStorageObjectAcks = await client.write_storage_objects_async(session, [
    NakamaWriteStorageObject.new("saves", "savegame", 2, 1, army_setup, "")
])

if acks.is_exception():
    print("An error occurred: %s" % acks)
    return

print("Stored objects: %s" % [acks.acks])
```
{{< / code >}}

{{< code type="client" >}}
```shell
PUT /v2/storage
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "objects": [
    {
      "collection": "battle",
      "key": "army",
      "value": "{ \"soldiers\": 50 }",
      "permission_read": 2,
      "permission_write": 1
    }
  ]
}
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local army_setup = json.encode({ soldiers = 50 })
local can_read = 2
local can_write = 1

local objects = {
  {
    collection = "battle",
    key = "army",
    permissionRead = can_read,
    permissionWrite = can_write,
    value = army_setup,
    version = version
  }
}

local result = client.write_storage_objects(objects)

if result.error then
  print(result.message)
  return
end

for _,ack in ipairs(result.acks) do
  pprint(ack)
end
```
{{< / code >}}

You can store an object with custom permissions from the code runtime.

{{< missing type="server" lang="typescript" />}}

{{< code type="server" >}}
```lua
local user_id = "4ec4f126-3f9d-11e7-84ef-b7c182b36521" -- Some user ID.

local new_objects = {
  { collection = "battle", key = "army", user_id = user_id, value = {}, permission_read = 2, permission_write = 1 }
}

nk.storage_write(new_objects)
```
{{< / code >}}

{{< code type="server" >}}
```go
userID := "4ec4f126-3f9d-11e7-84ef-b7c182b36521" // Some user ID.
objects := []*runtime.StorageWrite{
    &runtime.StorageWrite{
        Collection:      "battle",
        Key:             "army",
        UserID:          userID,
        Value:           "{}",
        PermissionRead:  2,
        PermissionWrite: 1,
    },
}

if _, err := nk.StorageWrite(ctx, objects); err != nil {
    // Handle error.
}
```
{{< / code >}}
