Skip to content
This repository has been archived by the owner on Jul 4, 2024. It is now read-only.

Hash password for IAAS/PAAS before sending then to the API. #265

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions gandi/cli/core/utils/password.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# coding: utf-8
"""Contains methods to generate a random password."""

import crypt
import random
import re
import string

# remove backslash from generated password to avoid
Expand Down Expand Up @@ -42,3 +44,26 @@ def mkpassword(length=16, chars=None, punctuation=None):
random.shuffle(data)

return ''.join(data)


def hash_password(password):
"""
Hash (if not already done) a string valid for use with PAAS/IAAS password

WARNING: Using a hash password will make impossible for the API to
check/validate the password strength so you should check it
before.

:param password: The string to hash
:type password: ``str``

:rtype: ``str``
"""

# crypt SHA-512
if re.match('^\$6\$[a-zA-Z0-9\./]{16}\$[a-zA-Z0-9\./]{86}$', password):
return password

salt = mkpassword(length=16,
chars=string.ascii_letters + string.digits + './')
return crypt.crypt(password, '$6$%s$' % (salt, ))
5 changes: 3 additions & 2 deletions gandi/cli/modules/iaas.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from gandi.cli.core.base import GandiModule
from gandi.cli.core.utils import randomstring
from gandi.cli.core.utils.password import hash_password
from gandi.cli.modules.datacenter import Datacenter
from gandi.cli.modules.sshkey import SshkeyHelper
from gandi.cli.core.utils import MigrationNotFinalized
Expand Down Expand Up @@ -180,7 +181,7 @@ def update(cls, id, memory, cores, console, password, background,
vm_params['console'] = console

if password:
vm_params['password'] = password
vm_params['password'] = hash_password(password)

if max_memory:
vm_params['vm_max_memory'] = max_memory
Expand Down Expand Up @@ -224,7 +225,7 @@ def create(cls, datacenter, memory, cores, ip_version, bandwidth,
vm_params['run'] = run

if password:
vm_params['password'] = password
vm_params['password'] = hash_password(password)

if ip_version:
vm_params['ip_version'] = ip_version
Expand Down
5 changes: 3 additions & 2 deletions gandi/cli/modules/paas.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import sys

from gandi.cli.core.base import GandiModule
from gandi.cli.core.utils.password import hash_password
from gandi.cli.modules.metric import Metric
from gandi.cli.modules.vhost import Vhost
from gandi.cli.modules.datacenter import Datacenter
Expand Down Expand Up @@ -208,7 +209,7 @@ def update(cls, id, name, size, quantity, password, sshkey, upgrade,
paas_params['quantity'] = quantity

if password:
paas_params['password'] = password
paas_params['password'] = hash_password(password)

paas_params.update(cls.convert_sshkey(sshkey))

Expand Down Expand Up @@ -251,7 +252,7 @@ def create(cls, name, size, type, quantity, duration, datacenter, vhosts,
}

if password:
paas_params['password'] = password
paas_params['password'] = hash_password(password)

if quantity:
paas_params['quantity'] = quantity
Expand Down
18 changes: 12 additions & 6 deletions gandi/cli/tests/commands/test_paas.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,9 @@ def test_delete_background(self):
""")
self.assertEqual(result.exit_code, 0)

def test_create_default(self):
@mock.patch('gandi.cli.modules.paas.hash_password')
def test_create_default(self, mock_hash_password):
mock_hash_password.return_value = '- hash pwd -'
args = []
result = self.invoke_with_exceptions(paas.create, args,
obj=GandiContextHelper(),
Expand All @@ -468,10 +470,12 @@ def test_create_default(self):
self.assertEqual(params['datacenter_id'], 3)
self.assertEqual(params['size'], 's')
self.assertEqual(params['duration'], '1m')
self.assertEqual(params['password'], 'ploki')
self.assertEqual(params['password'], '- hash pwd -')
self.assertTrue(params['name'].startswith('paas'))

def test_create_size(self):
@mock.patch('gandi.cli.modules.paas.hash_password')
def test_create_size(self, mock_hash_password):
mock_hash_password.return_value = '- hash pwd -'
args = ['--size', 's+']
result = self.invoke_with_exceptions(paas.create, args,
obj=GandiContextHelper(),
Expand All @@ -490,7 +494,7 @@ def test_create_size(self):
self.assertEqual(params['datacenter_id'], 3)
self.assertEqual(params['size'], 's+')
self.assertEqual(params['duration'], '1m')
self.assertEqual(params['password'], 'ploki')
self.assertEqual(params['password'], '- hash pwd -')
self.assertTrue(params['name'].startswith('paas'))

def test_create_name(self):
Expand Down Expand Up @@ -553,7 +557,9 @@ def test_create_name_vhost_ssl(self):

self.assertEqual(result.exit_code, 0)

def test_create_datacenter_limited(self):
@mock.patch('gandi.cli.modules.paas.hash_password')
def test_create_datacenter_limited(self, mock_hash_password):
mock_hash_password.return_value = '- hash pwd -'
args = ['--datacenter', 'FR-SD2']
result = self.invoke_with_exceptions(paas.create, args,
obj=GandiContextHelper(),
Expand All @@ -574,7 +580,7 @@ def test_create_datacenter_limited(self):
self.assertEqual(params['datacenter_id'], 1)
self.assertEqual(params['size'], 's')
self.assertEqual(params['duration'], '1m')
self.assertEqual(params['password'], 'ploki')
self.assertEqual(params['password'], '- hash pwd -')
self.assertTrue(params['name'].startswith('paas'))

self.assertEqual(result.exit_code, 0)
Expand Down
28 changes: 24 additions & 4 deletions gandi/cli/tests/commands/test_vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,9 @@ def test_update_background(self):
""")
self.assertEqual(result.exit_code, 0)

def test_update_password(self):
@mock.patch('gandi.cli.modules.iaas.hash_password')
def test_update_password(self, mock_hash_password):
mock_hash_password.return_value = '- hash pwd -'
args = ['server01', '--password']
result = self.invoke_with_exceptions(vm.update, args,
input='plokiploki\nplokiploki\n')
Expand All @@ -843,6 +845,9 @@ def test_update_password(self):
password: \nRepeat for confirmation: \nUpdating your Virtual Machine server01.
\rProgress: [###] 100.00% 00:00:00""")

params = self.api_calls['hosting.vm.update'][0][1]
self.assertEqual(params['password'], '- hash pwd -')

self.assertEqual(result.exit_code, 0)

def test_update_console(self):
Expand Down Expand Up @@ -942,7 +947,9 @@ def test_ssh_args(self):

self.assertEqual(result.exit_code, 0)

def test_create_default_hostname_ok(self):
@mock.patch('gandi.cli.modules.iaas.hash_password')
def test_create_default_hostname_ok(self, mock_hash_password):
mock_hash_password.return_value = '- hash pwd -'
args = ['--hostname', 'server500']
result = self.invoke_with_exceptions(vm.create, args,
obj=GandiContextHelper(),
Expand All @@ -956,9 +963,14 @@ def test_create_default_hostname_ok(self):
\rProgress: [###] 100.00% 00:00:00 \n\
Your Virtual Machine server500 has been created.""")

params = self.api_calls['hosting.vm.create_from'][0][0]
self.assertEqual(params['password'], '- hash pwd -')

self.assertEqual(result.exit_code, 0)

def test_create_default_ok(self):
@mock.patch('gandi.cli.modules.iaas.hash_password')
def test_create_default_ok(self, mock_hash_password):
mock_hash_password.return_value = '- hash pwd -'
args = []
result = self.invoke_with_exceptions(vm.create, args,
obj=GandiContextHelper(),
Expand All @@ -973,6 +985,9 @@ def test_create_default_ok(self):
\rProgress: [###] 100.00% 00:00:00 \n\
Your Virtual Machine vm has been created.""")

params = self.api_calls['hosting.vm.create_from'][0][0]
self.assertEqual(params['password'], '- hash pwd -')

self.assertEqual(result.exit_code, 0)

def test_create_ip_not_vlan_ko(self):
Expand Down Expand Up @@ -1062,7 +1077,9 @@ def test_create_sshkey_ok(self):

self.assertEqual(result.exit_code, 0)

def test_create_gen_password_root_ok(self):
@mock.patch('gandi.cli.modules.iaas.hash_password')
def test_create_gen_password_root_ok(self, mock_hash_password):
mock_hash_password.return_value = '- hash pwd -'
args = ['--gen-password']
result = self.invoke_with_exceptions(vm.create, args,
obj=GandiContextHelper())
Expand All @@ -1080,6 +1097,9 @@ def test_create_gen_password_root_ok(self):
\rProgress: [###] 100.00% 00:00:00 \n\
Your Virtual Machine vm has been created.""")

params = self.api_calls['hosting.vm.create_from'][0][0]
self.assertEqual(params['password'], '- hash pwd -')

self.assertEqual(result.exit_code, 0)

def test_create_gen_password_user_ok(self):
Expand Down
24 changes: 24 additions & 0 deletions gandi/cli/tests/test_hash_password.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from ..core.utils.password import hash_password
from .compat import unittest, mock


class TestHashPwd(unittest.TestCase):

@mock.patch('gandi.cli.core.utils.password.mkpassword')
def test_hash_pwd(self, mkpassword):
mkpassword.return_value = 'aSaltSting.12345'

self.assertEqual(hash_password('.aPwd42!'),
'$6$aSaltSting.12345$'
'kQ0e3QAP5MxJA4un4xkGCK4OwMc5dX/xKubYypmasAb'
'U6ptnq5vyPi8IDfPm9zsKrUMKHhL056bD5rXsZqAt6.')

def test_pwd_hashed(self):
pwd = ('$6$aSaltSting.12345$'
'kQ0e3QAP5MxJA4un4xkGCK4OwMc5dX/xKubYypmasAb'
'U6ptnq5vyPi8IDfPm9zsKrUMKHhL056bD5rXsZqAt6.')

self.assertEqual(hash_password(pwd), pwd)