1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
require "securerandom"
module ProTacts
# Ids in the shape jj spells a change id: letters from k to z, so an id
# is never a hash and never a word its author meant. Minted rather
# than chosen, which is what lets a person read one
# (db/migrations/005_group_identity.rb).
module ChangeId
ALPHABET = "klmnopqrstuvwxyz".chars.freeze #: Array[String]
# The length a contact's id is minted at. Store#put overwrites
# rather than refuses, so the width is the only guard a create has:
# 48 bits, for a family's book. The admin create, the import, and
# Store#reid draw at it; #minted? measures ids against it.
CONTACT_LENGTH = 12 #: Integer
# SecureRandom rather than rand: the draws have to be independent of
# anything a caller can observe or seed.
#: (Integer length) -> String
def self.mint(length)
SecureRandom.alphanumeric(length, chars: ALPHABET)
end
# Whether an id is one this server minted for a contact: the shape
# #mint draws at CONTACT_LENGTH, and nothing else. Ids that fail
# this — a client's UUID, a seed from before the shape settled —
# are what rake contacts:reid moves.
#: (String id) -> bool
def self.minted?(id)
/\A[#{ALPHABET.join}]{#{CONTACT_LENGTH}}\z/.match?(id)
end
end
end