Skip to content

Instantly share code, notes, and snippets.

@yocontra
Last active October 19, 2015 02:02
Show Gist options
  • Save yocontra/b2481bf71d71b39514a1 to your computer and use it in GitHub Desktop.
Save yocontra/b2481bf71d71b39514a1 to your computer and use it in GitHub Desktop.
firebase secure chat room ruleset (using the blaze compiler). users may only post new messages and update their last read date - everything else is handled by the server
{
"rules":{
".write":"false",
".read":"false",
"user-rooms": {
".write":"false",
".read":"false",
"$userId": {
".write":"false",
".read":"(((auth.uid!==null&&auth.uid===$userId)))",
"$roomId": {
".write":"false",
".read":"((auth.uid!==null&&auth.uid===$userId||auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null))",
"ts": {
".write":"((((newData.parent().val()==null||newData.parent().child('ts').exists()&&newData.parent().child('users').exists()&&newData.parent().child('lastUpdated').exists()&&newData.parent().child('lastRead').exists())&&newData.val()<=now&&newData.exists()&&!data.exists()&&(!newData.exists()||newData.isNumber())&&(auth.uid!==null&&auth.uid===$userId||auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null))))",
".read":"((auth.uid!==null&&auth.uid===$userId||auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null))"
},
"lastUpdated": {
".write":"((((newData.parent().val()==null||newData.parent().child('ts').exists()&&newData.parent().child('users').exists()&&newData.parent().child('lastUpdated').exists()&&newData.parent().child('lastRead').exists())&&newData.val()<=now&&data.exists()&&newData.exists()&&(!newData.exists()||newData.isNumber())&&(auth.uid!==null&&auth.uid===$userId||auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null))))",
".read":"((auth.uid!==null&&auth.uid===$userId||auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null))"
},
"users": {
".write":"false",
".read":"((auth.uid!==null&&auth.uid===$userId||auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null))",
"$userId": {
".write":"((((newData.parent().parent().val()==null||newData.parent().parent().child('ts').exists()&&newData.parent().parent().child('users').exists()&&newData.parent().parent().child('lastUpdated').exists()&&newData.parent().parent().child('lastRead').exists())&&(!newData.exists()||newData.hasChildren())&&(newData.val()==null||newData.child('first_name').exists()&&newData.child('image').exists())&&newData.child('first_name').exists()&&!data.child('first_name').exists()&&(!newData.child('first_name').exists()||newData.child('first_name').isString())&&newData.child('image').exists()&&!data.child('image').exists()&&(!newData.child('image').exists()||newData.child('image').isString())&&(auth.uid!==null&&auth.uid===$userId||auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null))))",
".read":"((auth.uid!==null&&auth.uid===$userId||auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null))",
"first_name": {
".write":"((((newData.parent().parent().parent().val()==null||newData.parent().parent().parent().child('ts').exists()&&newData.parent().parent().parent().child('users').exists()&&newData.parent().parent().parent().child('lastUpdated').exists()&&newData.parent().parent().parent().child('lastRead').exists())&&(newData.parent().val()==null||newData.parent().child('first_name').exists()&&newData.parent().child('image').exists())&&newData.exists()&&!data.exists()&&(!newData.exists()||newData.isString())&&(auth.uid!==null&&auth.uid===$userId||auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null))))",
".read":"((auth.uid!==null&&auth.uid===$userId||auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null))"
},
"image": {
".write":"((((newData.parent().parent().parent().val()==null||newData.parent().parent().parent().child('ts').exists()&&newData.parent().parent().parent().child('users').exists()&&newData.parent().parent().parent().child('lastUpdated').exists()&&newData.parent().parent().parent().child('lastRead').exists())&&(newData.parent().val()==null||newData.parent().child('first_name').exists()&&newData.parent().child('image').exists())&&newData.exists()&&!data.exists()&&(!newData.exists()||newData.isString())&&(auth.uid!==null&&auth.uid===$userId||auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null))))",
".read":"((auth.uid!==null&&auth.uid===$userId||auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null))"
},
"$other":{".validate":"false"}
}
},
"lastRead": {
".write":"false",
".read":"((auth.uid!==null&&auth.uid===$userId||auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null))",
"$userId": {
".write":"((((newData.parent().parent().val()==null||newData.parent().parent().child('ts').exists()&&newData.parent().parent().child('users').exists()&&newData.parent().parent().child('lastUpdated').exists()&&newData.parent().parent().child('lastRead').exists())&&auth.uid!==null&&auth.uid===$userId&&newData.val()<=now&&data.exists()&&newData.exists()&&(!newData.exists()||newData.isNumber())&&(auth.uid!==null&&auth.uid===$userId||auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null))))",
".read":"((auth.uid!==null&&auth.uid===$userId||auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null))"
}
},
"$other":{".validate":"false"}
}
}
},
"room-messages": {
".write":"false",
".read":"false",
"$roomId": {
".write":"false",
".read":"(((auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
"$msgId": {
".write":"((((!newData.exists()||newData.hasChildren())&&(newData.val()==null||newData.child('user').exists()&&newData.child('text').exists()&&newData.child('ts').exists())&&auth.uid!==null&&auth.uid===newData.child('user').val()&&newData.child('user').exists()&&!data.child('user').exists()&&(!newData.child('user').exists()||newData.child('user').isString())&&newData.child('text').exists()&&!data.child('text').exists()&&(!newData.child('text').exists()||newData.child('text').isString())&&newData.child('ts').val()<=now&&newData.child('ts').exists()&&!data.child('ts').exists()&&(!newData.child('ts').exists()||newData.child('ts').isNumber())&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
".read":"(((auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
"user": {
".write":"((((newData.parent().val()==null||newData.parent().child('user').exists()&&newData.parent().child('text').exists()&&newData.parent().child('ts').exists())&&auth.uid!==null&&auth.uid===newData.val()&&newData.exists()&&!data.exists()&&(!newData.exists()||newData.isString())&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
".read":"(((auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))"
},
"text": {
".write":"((((newData.parent().val()==null||newData.parent().child('user').exists()&&newData.parent().child('text').exists()&&newData.parent().child('ts').exists())&&newData.exists()&&!data.exists()&&(!newData.exists()||newData.isString())&&auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
".read":"(((auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))"
},
"ts": {
".write":"((((newData.parent().val()==null||newData.parent().child('user').exists()&&newData.parent().child('text').exists()&&newData.parent().child('ts').exists())&&newData.val()<=now&&newData.exists()&&!data.exists()&&(!newData.exists()||newData.isNumber())&&auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
".read":"(((auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))"
},
"$other":{".validate":"false"}
}
}
},
"rooms": {
".write":"false",
".read":"false",
"$roomId": {
".write":"false",
".read":"(((auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
"ts": {
".write":"((((newData.parent().val()==null||newData.parent().child('ts').exists()&&newData.parent().child('users').exists()&&newData.parent().child('lastUpdated').exists()&&newData.parent().child('lastRead').exists())&&newData.val()<=now&&newData.exists()&&!data.exists()&&(!newData.exists()||newData.isNumber())&&auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
".read":"(((auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))"
},
"lastUpdated": {
".write":"((((newData.parent().val()==null||newData.parent().child('ts').exists()&&newData.parent().child('users').exists()&&newData.parent().child('lastUpdated').exists()&&newData.parent().child('lastRead').exists())&&newData.val()<=now&&data.exists()&&newData.exists()&&(!newData.exists()||newData.isNumber())&&auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
".read":"(((auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))"
},
"users": {
".write":"false",
".read":"(((auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
"$userId": {
".write":"((((newData.parent().parent().val()==null||newData.parent().parent().child('ts').exists()&&newData.parent().parent().child('users').exists()&&newData.parent().parent().child('lastUpdated').exists()&&newData.parent().parent().child('lastRead').exists())&&(!newData.exists()||newData.hasChildren())&&(newData.val()==null||newData.child('first_name').exists()&&newData.child('image').exists())&&newData.child('first_name').exists()&&!data.child('first_name').exists()&&(!newData.child('first_name').exists()||newData.child('first_name').isString())&&newData.child('image').exists()&&!data.child('image').exists()&&(!newData.child('image').exists()||newData.child('image').isString())&&auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
".read":"(((auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
"first_name": {
".write":"((((newData.parent().parent().parent().val()==null||newData.parent().parent().parent().child('ts').exists()&&newData.parent().parent().parent().child('users').exists()&&newData.parent().parent().parent().child('lastUpdated').exists()&&newData.parent().parent().parent().child('lastRead').exists())&&(newData.parent().val()==null||newData.parent().child('first_name').exists()&&newData.parent().child('image').exists())&&newData.exists()&&!data.exists()&&(!newData.exists()||newData.isString())&&auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
".read":"(((auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))"
},
"image": {
".write":"((((newData.parent().parent().parent().val()==null||newData.parent().parent().parent().child('ts').exists()&&newData.parent().parent().parent().child('users').exists()&&newData.parent().parent().parent().child('lastUpdated').exists()&&newData.parent().parent().parent().child('lastRead').exists())&&(newData.parent().val()==null||newData.parent().child('first_name').exists()&&newData.parent().child('image').exists())&&newData.exists()&&!data.exists()&&(!newData.exists()||newData.isString())&&auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
".read":"(((auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))"
},
"$other":{".validate":"false"}
}
},
"lastRead": {
".write":"false",
".read":"(((auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
"$userId": {
".write":"((((newData.parent().parent().val()==null||newData.parent().parent().child('ts').exists()&&newData.parent().parent().child('users').exists()&&newData.parent().parent().child('lastUpdated').exists()&&newData.parent().parent().child('lastRead').exists())&&auth.uid!==null&&auth.uid===$userId&&newData.val()<=now&&data.exists()&&newData.exists()&&(!newData.exists()||newData.isNumber())&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))",
".read":"(((auth.uid!==null&&root.child('rooms').child($roomId).child('users').child(auth.uid).val()!==null)))"
}
},
"$other":{".validate":"false"}
}
}
}
}
functions:
- isLoggedIn(): auth.uid !== null
- isInRoom(roomId): isLoggedIn() && root.rooms[roomId].users[auth.uid] !== null
- isUser(userId): isLoggedIn() && auth.uid === userId
- createOnly(): next.exists() && !prev.exists()
- deleteOnly(): prev.exists() && !next.exists()
- modifyOnly(): prev.exists() && next.exists()
- createOrDelete(): createOnly() || deleteOnly()
schema:
# actual schema
type: object
properties:
user-rooms:
type: object
$userId:
type: object
$roomId:
$ref: "#/definitions/room"
room-messages:
type: object
$roomId:
type: object
$msgId:
$ref: "#/definitions/message"
rooms:
type: object
$roomId:
$ref: "#/definitions/room"
# defs
definitions:
message:
additionalProperties: false
type: object
required: [user, text, ts]
properties:
user:
type: string
constraint: isUser(next) && createOnly()
text:
type: string
constraint: createOnly()
ts:
type: number
constraint: next <= now && createOnly()
user:
additionalProperties: false
type: object
required: [first_name, image]
properties:
first_name:
type: string
constraint: createOnly()
image:
type: string
constraint: createOnly()
room:
additionalProperties: false
type: object
required: [ts, users, lastUpdated, lastRead]
properties:
ts:
type: number
constraint: next <= now && createOnly()
lastUpdated:
type: number
constraint: next <= now && modifyOnly()
users:
type: object
$userId:
$ref: "#/definitions/user"
lastRead:
type: object
$userId:
type: number
constraint: isUser($userId) && next <= now && modifyOnly()
access:
- location: /
read: false
write: false
# room metadata (source of truth)
- location: /rooms/$roomId/
read: isInRoom($roomId)
write: isInRoom($roomId)
# user room listing (also has metadata)
- location: /user-rooms/$userId/
read: isUser($userId)
write: isUser($userId)
- location: /user-rooms/$userId/$roomId/
read: isInRoom($roomId)
write: isInRoom($roomId)
# messages for each room
- location: /room-messages/$roomId/
read: isInRoom($roomId)
write: isInRoom($roomId)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment