Skip to content

Instantly share code, notes, and snippets.

@jimweirich
Created November 19, 2010 22:12
Show Gist options
  • Save jimweirich/707290 to your computer and use it in GitHub Desktop.
Save jimweirich/707290 to your computer and use it in GitHub Desktop.
#
# I was asked for an example of code that was DRY, but still had an
# instance of Connascence of Algorithm going on.
#
# Consider the following code. I think most people would agree that
# the code is fairly DRY in the sense that there is no duplicated
# code.
#
require 'digest/sha1'
SALT_SIZE = 2
def generate_salt
(1..SALT_SIZE).map { rand(26) }.map { |i| ("a".."z").to_a[i] }.join
end
def join_with_salt(encoded_string, salt)
salt + encoded_string
end
def parse_salt(encoded_password)
encoded_password[0...SALT_SIZE]
end
def encode_password_with_salt(password, salt)
digest = Digest::SHA1.new
digest << (salt + password)
join_with_salt(digest.hexdigest, salt)
end
def encode_password(password)
salt = generate_salt
encode_password_with_salt(password, salt)
end
def check_password(password, encoded_password)
salt = parse_salt(encoded_password)
re_encoded_password = encode_password_with_salt(password, salt)
encoded_password == re_encoded_password
end
# Now think through the following changes to various algorithms in the
# example:
#
# (1) Change the hash algorithm from SHA1 to SHA256
#
# It's a one line change to the encode_password_with_salt method.
# DRY, but no CoA.
#
# (2) Change the length of the salt.
#
# Not sure if this counts as an _algorithm_ change, but you need only
# change the size of SALT_SIZE in one line. DRY and no CoA.
#
# (3) Change the algorithm that joins the salt to the encoded
# password.
#
# Suppose you want to put the salt value at the end of the encoded
# password rather than the beginning. That requires a change to
# "join_with_salt". However to get the code to work, you must also
# change the "parse_salt" method so that it picks up the salt at the
# same place. Although the code is DRY (in that there is no
# duplicated code), there is still Connascence of Algorithm between
# the "join_with_salt" and "parse_salt" method.
#
# SIDE NOTE: You could argue that the code isn't DRY in the Thomas and
# Hunt sense: "Every piece of knowledge must have a single,
# unambiguous, authoritative representation within a system". For
# certainly the knowledge of how the salt is joined to the encoded
# password is represented in two separate methods. I'm not sure the
# is an example of CoA that is also DRY in the Thomas/Hunt sense.
# --------------------------------------------------------------------
require 'test/unit'
class TestPasswordEncoding < Test::Unit::TestCase
def test_passwords_can_be_round_tripped
encoding = encode_password("password")
assert check_password("password", encoding)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment