diff --git a/accounts/forms.py b/accounts/forms.py
index 8c05b75..e3bcd42 100644
--- a/accounts/forms.py
+++ b/accounts/forms.py
@@ -1,9 +1,10 @@
+from appsettings.settings import app_settings
+from django.contrib.auth import get_user_model
 from django.forms import ModelForm, ValidationError
 from django.utils.translation import ugettext_lazy as _
 
-from appsettings.models import AppSettings
-
-from .models import UserInstance
+from .models import UserInstance, UserSSHKey
+from .utils import validate_ssh_key
 
 
 class UserInstanceForm(ModelForm):
@@ -18,7 +19,7 @@ class UserInstanceForm(ModelForm):
 
     def clean_instance(self):
         instance = self.cleaned_data['instance']
-        if AppSettings.objects.get(key="ALLOW_INSTANCE_MULTIPLE_OWNER").value == 'False':
+        if app_settings.ALLOW_INSTANCE_MULTIPLE_OWNER == 'False':
             exists = UserInstance.objects.filter(instance=instance)
             if exists:
                 raise ValidationError(_('Instance owned by another user'))
@@ -28,3 +29,43 @@ class UserInstanceForm(ModelForm):
     class Meta:
         model = UserInstance
         fields = '__all__'
+
+
+class ProfileForm(ModelForm):
+    class Meta:
+        model = get_user_model()
+        fields = ('first_name', 'last_name', 'email')
+
+
+class UserSSHKeyForm(ModelForm):
+    def __init__(self, *args, **kwargs):
+        self.user = kwargs.pop('user', None)
+        self.publickeys = UserSSHKey.objects.filter(user=self.user)
+        super().__init__(*args, **kwargs)
+
+    def clean_keyname(self):
+        for key in self.publickeys:
+            if self.cleaned_data['keyname'] == key.keyname:
+                raise ValidationError(_("Key name already exist"))
+
+        return self.cleaned_data['keyname']
+
+    def clean_keypublic(self):
+        for key in self.publickeys:
+            if self.cleaned_data['keypublic'] == key.keypublic:
+                raise ValidationError(_("Public key already exist"))
+
+        if not validate_ssh_key(self.cleaned_data['keypublic']):
+            raise ValidationError(_('Invalid key'))
+        return self.cleaned_data['keypublic']
+
+    def save(self, commit=True):
+        ssh_key = super().save(commit=False)
+        ssh_key.user = self.user
+        if commit:
+            ssh_key.save()
+        return ssh_key
+
+    class Meta:
+        model = UserSSHKey
+        fields = ('keyname', 'keypublic')
diff --git a/accounts/models.py b/accounts/models.py
index 33b120c..15e0ddf 100644
--- a/accounts/models.py
+++ b/accounts/models.py
@@ -1,9 +1,7 @@
-from django.conf import settings
 from django.contrib.auth.models import User
 from django.core.validators import MinValueValidator
 from django.db import models
 from django.utils.translation import ugettext_lazy as _
-
 from instances.models import Instance
 
 
@@ -11,6 +9,7 @@ class UserInstanceManager(models.Manager):
     def get_queryset(self):
         return super().get_queryset().select_related('instance', 'user')
 
+
 class UserInstance(models.Model):
     user = models.ForeignKey(User, on_delete=models.CASCADE)
     instance = models.ForeignKey(Instance, on_delete=models.CASCADE)
diff --git a/accounts/templates/account.html b/accounts/templates/account.html
index 2deacf8..54e3ad9 100644
--- a/accounts/templates/account.html
+++ b/accounts/templates/account.html
@@ -5,20 +5,15 @@
 {% load qr_code %}
 
 {% block title %}{% trans "User Profile" %} - {{ user }}{% endblock %}
+{% block page_header %}{% trans "User Profile" %}: {{ user }}{% endblock page_header %}
+
+{% block page_header_extra %}
+<a href="{% url 'accounts:user_instance_create' user.id %}" class="btn btn-success">
+    {% icon 'plus' %}
+</a>
+{% endblock page_header_extra %}
+
 {% block content %}
-    <!-- Page Heading -->
-    <div class="row">
-        <div class="col-lg-12">
-            <a href="{% url 'user_instance_create' user.id %}" class="btn btn-success btn-header float-right">
-                {% icon 'plus' %}
-            </a>
-            <h2 class="page-header">{% trans "User Profile" %}: {{ user }}</h2>
-        </div>
-    </div>
-    <!-- /.row -->
-
-    {% include 'errors_block.html' %}
-
     <ul class="nav nav-tabs">
         <li class="nav-item">
             <a class="nav-link active" data-toggle="tab" href="#instances">{% trans "Instances" %}</a>
@@ -55,12 +50,12 @@
                             <td>{{ inst.is_change }}</td>
                             <td>{{ inst.is_delete }}</td>
                             <td style="width:5px;">
-                                <a href="{% url 'user_instance_update' inst.id %}" class="btn btn-sm btn-secondary" title="{% trans "edit" %}">
+                                <a href="{% url 'accounts:user_instance_update' inst.id %}" class="btn btn-sm btn-secondary" title="{% trans "edit" %}">
                                     {% icon 'pencil' %}
                                 </a>
                             </td>
                             <td style="width:5px;">
-                                <a class="btn btn-sm btn-secondary" href="{% url 'user_instance_delete' inst.id %}" title="{% trans "Delete" %}">
+                                <a class="btn btn-sm btn-secondary" href="{% url 'accounts:user_instance_delete' inst.id %}" title="{% trans "Delete" %}">
                                     {% icon 'trash' %}
                                 </a>
                             </td>
diff --git a/accounts/templates/accounts-list.html b/accounts/templates/accounts-list.html
index 9cea6bb..a600678 100644
--- a/accounts/templates/accounts-list.html
+++ b/accounts/templates/accounts-list.html
@@ -41,7 +41,7 @@
                             {% for user in users %}
                                 <tr class="{% if not user.is_active %}danger{% endif %}">
                                     <td>
-                                        <a href="{% url 'account' user.id %}"><strong>{{ user.username }}</strong></a>
+                                        <a href="{% url 'accounts:account' user.id %}"><strong>{{ user.username }}</strong></a>
                                         <a data-toggle="modal" href="#editUser{{ user.id }}" class="float-right" title="{% trans "Edit" %}">
                                             <span class="fa fa-cog"></span>
                                         </a>
diff --git a/accounts/templates/accounts.html b/accounts/templates/accounts.html
index 0164d43..162d179 100644
--- a/accounts/templates/accounts.html
+++ b/accounts/templates/accounts.html
@@ -32,7 +32,7 @@
                         <div class="card-header bg-secondary">
                 {% endif %}    
                         <h5 class="my-0 card-title">
-                            <a class="card-link text-light" href="{% url 'account' user.id %}"><strong>{{ user.username }}</strong></a>
+                            <a class="card-link text-light" href="{% url 'accounts:account' user.id %}"><strong>{{ user.username }}</strong></a>
                             <a class="card-link text-light float-right" data-toggle="modal" href="#editUser{{ user.id }}" title="{% trans "Edit" %}">
                                 <span class="fa fa-cog"></span>
                             </a>
diff --git a/accounts/templates/accounts/change_password_form.html b/accounts/templates/accounts/change_password_form.html
index 9dfb6a3..398a048 100644
--- a/accounts/templates/accounts/change_password_form.html
+++ b/accounts/templates/accounts/change_password_form.html
@@ -7,23 +7,28 @@
 {% block title %}{%trans "Change Password" %}{% endblock title %}
 
 {% block content %}
-<div class="card">
-    <div class="card-header">
-        <h4 class="card-title">{%trans "Change Password" %}: {{ user }}</h4>
-    </div>
-    <div class="card-body">
-        <form method="post" id="password-change">
-            {% csrf_token %}
-            {% bootstrap_form form layout='horizontal' %}
-        </form>
-    </div>
-    <div class="card-footer">
-        <div class="float-right">
-            <a class="btn btn-primary" href="javascript:history.back()">{% icon 'times' %} {% trans "Cancel" %}</a>
-            <button type="submit" form="password-change" class="btn btn-success">
-                {% icon 'check' %} {% trans "Change" %}
-            </button>
+<div class="row">
+    <div class="offset-2 col-lg-8">
+        <div class="card">
+            <div class="card-header">
+                <h4 class="card-title">{%trans "Change Password" %}: {{ user }}</h4>
+            </div>
+            <div class="card-body">
+                <form method="post" id="password-change">
+                    {% csrf_token %}
+                    {% bootstrap_form form layout='horizontal' %}
+                </form>
+            </div>
+            <div class="card-footer">
+                <div class="float-right">
+                    <a class="btn btn-primary" href="javascript:history.back()">{% icon 'times' %}
+                        {% trans "Cancel" %}</a>
+                    <button type="submit" form="password-change" class="btn btn-success">
+                        {% icon 'check' %} {% trans "Change" %}
+                    </button>
+                </div>
+            </div>
         </div>
     </div>
 </div>
-{% endblock content %}
+{% endblock content %}
\ No newline at end of file
diff --git a/accounts/templates/profile.html b/accounts/templates/profile.html
index 4d11564..23ab53a 100644
--- a/accounts/templates/profile.html
+++ b/accounts/templates/profile.html
@@ -1,93 +1,80 @@
 {% extends "base.html" %}
 {% load i18n %}
+{% load bootstrap4 %}
 {% load icons %}
 {% load tags_fingerprint %}
-{% block title %}{% trans "Profile" %}{% endblock %}
+
+{% block title %}{% trans "Profile" %}: {{ request.user.first_name }} {{ request.user.last_name}}{% endblock %}
+
+{% block page_header %}{% trans "Profile" %}: {{ request.user.first_name }} {{ request.user.last_name}}{% endblock page_header %}
+
 {% block content %}
-            <!-- Page Heading -->
-            <div class="row">
-                <div class="col-lg-12">
-                    <h2 class="page-header">{% trans "Profile" %}</h2>
-                </div>
-            </div>
-            <!-- /.row -->
-
-            {% include 'errors_block.html' %}
-
-            <div class="row">
-                <div class="col-lg-12">
-                    <h3 class="page-header">{% trans "Edit Profile" %}</h3>
+<ul class="nav nav-tabs">
+    <li class="nav-item">
+        <a class="nav-link active" data-toggle="tab" href="#edit-profile">{% trans "Edit Profile" %}</a>
+    </li>
+    <li class="nav-item">
+        <a class="nav-link" data-toggle="tab" href="#ssh-keys">{% trans "SSH Keys" %}</a>
+    </li>
+</ul>
+<div class="tab-content">
+    <div class="tab-pane tab-pane-bordered active" id="edit-profile">
+        <div class="card">
+            <div class="card-body">
+                <form method="post" action="" role="form" aria-label="Edit user info form">
+                    {% csrf_token %}
+                    {% bootstrap_form profile_form layout='horizontal' %}
                     {% if perms.accounts.change_password %}
-                        <a href="{% url 'change_password' %}" class="ml-3 btn btn-primary">{% icon 'lock' %} {% trans "Change Password" %}</a>
+                        <a href="{% url 'accounts:change_password' %}" class="btn btn-primary">
+                            {% icon 'lock' %} {% trans "Change Password" %}
+                        </a>
                     {% endif %}
-                    <form method="post" action="" role="form" aria-label="Edit user info form">{% csrf_token %}
-                        <div class="form-group">
-                            <label class="col-sm-2 col-form-label">{% trans "Login" %}</label>
-                            <div class="col-sm-4">
-                                <input type="text" class="form-control" value="{{ request.user.username }}" disabled>
-                            </div>
-                        </div>
-                        <div class="form-group bridge_name_form_group_dhcp">
-                            <label class="col-sm-2 col-form-label">{% trans "Username" %}</label>
-                            <div class="col-sm-4">
-                                <input type="text" class="form-control" name="username" value="{{ request.user.first_name }}" pattern="[0-9a-zA-Z]+">
-                            </div>
-                        </div>
-                        <div class="form-group bridge_name_form_group_dhcp">
-                            <label class="col-sm-2 col-form-label">{% trans "Email" %}</label>
-                            <div class="col-sm-4">
-                                <input type="email" class="form-control" name="email" value="{{ request.user.email }}">
-                            </div>
-                        </div>
-                        <div class="form-group">
-                            <div class="col-sm-offset-2 col-sm-10">
-                                <button type="submit" class="btn btn-primary">{% trans "Change" %}</button>
-                            </div>
-                        </div>
-                    </form>
-                    <h3 class="page-header">{% trans "SSH Keys" %}</h3>
-                    {% if publickeys %}
-                        <div class="col-lg-12">
-                            <div class="table-responsive">
-                                <table class="table table-hover">
-                                    <tbody class="text-center">
-                                    {% for key in publickeys %}
-                                        <tr>
-                                            <td>{{ key.keyname }} ({% ssh_to_fingerprint key.keypublic %})</td>
-                                             <td>
-                                                <form action="" method="post" role="form" aria-label="Delete user public key form">{% csrf_token %}
-                                                    <input type="hidden" name="keyid" value="{{ key.id }}"/>
-                                                    <button type="submit" class="btn btn-sm btn-secondary" name="keydelete" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
-                                                        <span class="fa fa-trash"></span>
-                                                    </button>
-                                                </form>
-                                            </td>
-                                        </tr>
-                                    {% endfor %}
-                                    </tbody>
-                                </table>
-                            </div>
-                        </div>
-                    {% endif %}
-                    <form method="post" action="" role="form" aria-label="Add key to user form">{% csrf_token %}
-                        <div class="form-group bridge_name_form_group_dhcp">
-                            <label class="col-sm-2 col-form-label">{% trans "Key name" %}</label>
-                            <div class="col-sm-4">
-                                <input type="text" class="form-control" name="keyname" placeholder="{% trans "Enter Name" %}">
-                            </div>
-                        </div>
-                        <div class="form-group bridge_name_form_group_dhcp">
-                            <label class="col-sm-2 col-form-label">{% trans "Public key" %}</label>
-                            <div class="col-sm-8">
-                                <textarea name="keypublic" class="form-control" rows="6" placeholder="{% trans "Enter Public Key" %}"></textarea>
-                            </div>
-                        </div>
-                        <div class="form-group">
-                            <div class="col-sm-10">
-                                <button type="submit" class="btn btn-primary">{% trans "Add" %}</button>
-                            </div>
-                        </div>
-                    </form>
-                </div>
+                    <div class="form-group mb-0 float-right">
+                        <button type="submit" class="btn btn-primary">
+                            {% icon 'pencil' %} {% trans "Update" %}
+                        </button>
+                    </div>
+                </form>
             </div>
-{% endblock %}
+        </div>
+    </div>
+    <div class="tab-pane tab-pane-bordered fade" id="ssh-keys">
+        {% if publickeys %}
+        <div class="col-lg-12">
+            <div class="table-responsive">
+                <table class="table table-hover">
+                    <tbody class="text-center">
+                        {% for key in publickeys %}
+                        <tr>
+                            <td>{{ key.keyname }} ({% ssh_to_fingerprint key.keypublic %})</td>
+                            <td>
+                                <a href="{% url 'accounts:ssh_key_delete' key.id %}" title="{% trans "Delete" %}" class="btn btn-sm btn-secondary">
+                                    {% icon 'trash' %}
+                                </a>
+                            </td>
+                        </tr>
+                        {% endfor %}
+                    </tbody>
+                </table>
+            </div>
+        </div>
+        {% endif %}
+        <div class="card">
+            <div class="card-header">
+                {%trans "Add SSH Key" %}
+            </div>
+            <div class="card-body">
+                <form method="post" action="{% url 'accounts:ssh_key_create' %}" role="form" aria-label="Add key to user form">
+                    {% csrf_token %}
+                    {% bootstrap_form ssh_key_form layout='horizontal' %}
+                    <div class="form-group mb-0 float-right">
+                        <button type="submit" class="btn btn-primary">
+                            {% icon 'plus' %} {% trans "Add" %}
+                        </button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+</div>
+{% endblock %}
\ No newline at end of file
diff --git a/accounts/tests.py b/accounts/tests.py
index 051eb0a..2419990 100644
--- a/accounts/tests.py
+++ b/accounts/tests.py
@@ -1,45 +1,111 @@
-from django.contrib.auth.models import Permission, User
+from appsettings.settings import app_settings
+from computes.models import Compute
+from django.conf import settings
+from django.contrib.auth import get_user_model
+from django.contrib.auth.models import Permission
 from django.shortcuts import reverse
 from django.test import Client, TestCase
+from instances.models import Instance
+from instances.utils import refr
+from libvirt import VIR_DOMAIN_UNDEFINE_NVRAM
+from vrtManager.create import wvmCreate
+
+from accounts.forms import UserInstanceForm, UserSSHKeyForm
+from accounts.models import UserInstance, UserSSHKey
+from accounts.utils import validate_ssh_key
 
 
 class AccountsTestCase(TestCase):
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+
+        # Add users for testing purposes
+        User = get_user_model()
+        cls.admin_user = User.objects.get(pk=1)
+        cls.test_user = User.objects.create_user(username='test', password='test')
+
+        # Add localhost compute
+        cls.compute = Compute(
+            name='test-compute',
+            hostname='localhost',
+            login='',
+            password='',
+            details='local',
+            type=4,
+        )
+        cls.compute.save()
+
+        cls.connection = wvmCreate(
+            cls.compute.hostname,
+            cls.compute.login,
+            cls.compute.password,
+            cls.compute.type,
+        )
+
+        # Add disks for testing
+        cls.connection.create_volume(
+            'default',
+            'test-volume',
+            1,
+            'qcow2',
+            False,
+            0,
+            0,
+        )
+
+        # XML for testing vm
+        with open('conf/test-vm.xml', 'r') as f:
+            cls.xml = f.read()
+
+        # Create testing vm from XML
+        cls.connection._defineXML(cls.xml)
+        refr(cls.compute)
+        cls.instance = Instance.objects.get(pk=1)
+
+    @classmethod
+    def tearDownClass(cls):
+        # Destroy testing vm
+        cls.instance.proxy.delete_all_disks()
+        cls.instance.proxy.delete(VIR_DOMAIN_UNDEFINE_NVRAM)
+        super().tearDownClass()
+
     def setUp(self):
         self.client.login(username='admin', password='admin')
-        user = User.objects.create_user(username='test', password='test')
         permission = Permission.objects.get(codename='change_password')
-        user.user_permissions.add(permission)
+        self.test_user.user_permissions.add(permission)
+        self.rsa_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC6OOdbfv27QVnSC6sKxGaHb6YFc+3gxCkyVR3cTSXE/n5BEGf8aOgBpepULWa1RZfxYHY14PlKULDygdXSdrrR2kNSwoKz/Oo4d+3EE92L7ocl1+djZbptzgWgtw1OseLwbFik+iKlIdqPsH+IUQvX7yV545ZQtAP8Qj1R+uCqkw== test@test'
+        self.ecdsa_key = 'ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJc5xpT3R0iFJYNZbmWgAiDlHquX/BcV1kVTsnBfiMsZgU3lGaqz2eb7IBcir/dxGnsVENTTmPQ6sNcxLxT9kkQ= realgecko@archlinux'
 
     def test_profile(self):
-        response = self.client.get(reverse('profile'))
+        response = self.client.get(reverse('accounts:profile'))
         self.assertEqual(response.status_code, 200)
 
-        response = self.client.get(reverse('account', args=[2]))
+        response = self.client.get(reverse('accounts:account', args=[self.test_user.id]))
+        self.assertEqual(response.status_code, 200)
+
+    def test_account_with_otp(self):
+        settings.OTP_ENABLED = True
+        response = self.client.get(reverse('accounts:account', args=[self.test_user.id]))
         self.assertEqual(response.status_code, 200)
 
     def test_login_logout(self):
-        user = User.objects.get(username='test')
-        self.assertEqual(user.id, 2)
-
         client = Client()
 
-        response = client.post(reverse("login"), {"username": "test", "password": "test"})
-        self.assertRedirects(response, reverse('profile'))
+        response = client.post(reverse("accounts:login"), {"username": "test", "password": "test"})
+        self.assertRedirects(response, reverse('accounts:profile'))
 
-        response = client.get(reverse('logout'))
-        self.assertRedirects(response, reverse('login'))
+        response = client.get(reverse('accounts:logout'))
+        self.assertRedirects(response, reverse('accounts:login'))
 
-    def test_password_change(self):
-        client = Client()
+    def test_change_password(self):
+        self.client.force_login(self.test_user)
 
-        logged_in = client.login(username='test', password='test')
-        self.assertTrue(logged_in)
-
-        response = client.get(reverse('change_password'))
+        response = self.client.get(reverse('accounts:change_password'))
         self.assertEqual(response.status_code, 200)
 
-        response = client.post(
-            reverse('change_password'),
+        response = self.client.post(
+            reverse('accounts:change_password'),
             {
                 'old_password': 'wrongpass',
                 'new_password1': 'newpw',
@@ -48,17 +114,154 @@ class AccountsTestCase(TestCase):
         )
         self.assertEqual(response.status_code, 200)
 
-        response = client.post(
-            reverse('change_password'),
+        response = self.client.post(
+            reverse('accounts:change_password'),
             {
                 'old_password': 'test',
                 'new_password1': 'newpw',
                 'new_password2': 'newpw',
             },
         )
-        self.assertRedirects(response, reverse('profile'))
+        self.assertRedirects(response, reverse('accounts:profile'))
 
-        client.logout()
+        self.client.logout()
 
-        logged_in = client.login(username='test', password='newpw')
+        logged_in = self.client.login(username='test', password='newpw')
         self.assertTrue(logged_in)
+
+    def test_user_instance_create_update_delete(self):
+        # create
+        response = self.client.get(reverse('accounts:user_instance_create', args=[self.test_user.id]))
+        self.assertEqual(response.status_code, 200)
+
+        response = self.client.post(
+            reverse('accounts:user_instance_create', args=[self.test_user.id]),
+            {
+                'user': self.test_user.id,
+                'instance': self.instance.id,
+                'is_change': False,
+                'is_delete': False,
+                'is_vnc': False,
+            },
+        )
+        self.assertRedirects(response, reverse('accounts:account', args=[self.test_user.id]))
+
+        user_instance: UserInstance = UserInstance.objects.get(pk=1)
+        self.assertEqual(user_instance.user, self.test_user)
+        self.assertEqual(user_instance.instance, self.instance)
+        self.assertEqual(user_instance.is_change, False)
+        self.assertEqual(user_instance.is_delete, False)
+        self.assertEqual(user_instance.is_vnc, False)
+
+        # update
+        response = self.client.get(reverse('accounts:user_instance_update', args=[user_instance.id]))
+        self.assertEqual(response.status_code, 200)
+
+        response = self.client.post(
+            reverse('accounts:user_instance_update', args=[user_instance.id]),
+            {
+                'user': self.test_user.id,
+                'instance': self.instance.id,
+                'is_change': True,
+                'is_delete': True,
+                'is_vnc': True,
+            },
+        )
+        self.assertRedirects(response, reverse('accounts:account', args=[self.test_user.id]))
+
+        user_instance: UserInstance = UserInstance.objects.get(pk=1)
+        self.assertEqual(user_instance.user, self.test_user)
+        self.assertEqual(user_instance.instance, self.instance)
+        self.assertEqual(user_instance.is_change, True)
+        self.assertEqual(user_instance.is_delete, True)
+        self.assertEqual(user_instance.is_vnc, True)
+
+        # delete
+        response = self.client.get(reverse('accounts:user_instance_delete', args=[user_instance.id]))
+        self.assertEqual(response.status_code, 200)
+
+        response = self.client.post(reverse('accounts:user_instance_delete', args=[user_instance.id]))
+        self.assertRedirects(response, reverse('accounts:account', args=[self.test_user.id]))
+
+        # test 'next' redirect during deletion
+        user_instance = UserInstance.objects.create(user=self.test_user, instance=self.instance)
+        response = self.client.post(
+            reverse('accounts:user_instance_delete', args=[user_instance.id]) + '?next=' + reverse('index'))
+        self.assertRedirects(response, reverse('index'))
+
+    def test_update_user_profile(self):
+        self.client.force_login(self.test_user)
+
+        user = get_user_model().objects.get(username='test')
+        self.assertEqual(user.first_name, '')
+        self.assertEqual(user.last_name, '')
+        self.assertEqual(user.email, '')
+
+        response = self.client.post(reverse('accounts:profile'), {
+            'first_name': 'first name',
+            'last_name': 'last name',
+            'email': 'email@mail.mail',
+        })
+        self.assertRedirects(response, reverse('accounts:profile'))
+
+        user = get_user_model().objects.get(username='test')
+        self.assertEqual(user.first_name, 'first name')
+        self.assertEqual(user.last_name, 'last name')
+        self.assertEqual(user.email, 'email@mail.mail')
+
+    def test_create_delete_ssh_key(self):
+        response = self.client.get(reverse('accounts:ssh_key_create'))
+        self.assertEqual(response.status_code, 200)
+
+        response = self.client.post(reverse('accounts:ssh_key_create'), {
+            'keyname': 'keyname',
+            'keypublic': self.rsa_key,
+        })
+        self.assertRedirects(response, reverse('accounts:profile'))
+
+        key = UserSSHKey.objects.get(pk=1)
+        self.assertEqual(key.keyname, 'keyname')
+        self.assertEqual(key.keypublic, self.rsa_key)
+
+        response = self.client.get(reverse('accounts:ssh_key_delete', args=[1]))
+        self.assertEqual(response.status_code, 200)
+
+        response = self.client.post(reverse('accounts:ssh_key_delete', args=[1]))
+        self.assertRedirects(response, reverse('accounts:profile'))
+
+    def test_validate_ssh_key(self):
+        self.assertFalse(validate_ssh_key(''))
+        self.assertFalse(validate_ssh_key('ssh-rsa ABBA test@test'))
+        self.assertFalse(validate_ssh_key('ssh-rsa AAAABwdzZGY= test@test'))
+        self.assertFalse(validate_ssh_key('ssh-rsa AAA test@test'))
+        # validate ecdsa key
+        self.assertTrue(validate_ssh_key(self.ecdsa_key))
+
+    def test_forms(self):
+        # raise available validation errors for maximum coverage
+        form = UserSSHKeyForm({'keyname': 'keyname', 'keypublic': self.rsa_key}, user=self.test_user)
+        form.save()
+
+        form = UserSSHKeyForm({'keyname': 'keyname', 'keypublic': self.rsa_key}, user=self.test_user)
+        self.assertFalse(form.is_valid())
+
+        form = UserSSHKeyForm({'keyname': 'keyname', 'keypublic': 'invalid key'}, user=self.test_user)
+        self.assertFalse(form.is_valid())
+
+        app_settings.ALLOW_INSTANCE_MULTIPLE_OWNER = 'False'
+        form = UserInstanceForm({
+            'user': self.admin_user.id,
+            'instance': self.instance.id,
+            'is_change': False,
+            'is_delete': False,
+            'is_vnc': False,
+        })
+        form.save()
+        form = UserInstanceForm({
+            'user': self.test_user.id,
+            'instance': self.instance.id,
+            'is_change': False,
+            'is_delete': False,
+            'is_vnc': False,
+        })
+        self.assertFalse(form.is_valid())
diff --git a/accounts/urls.py b/accounts/urls.py
index 01f2408..e7a0d13 100644
--- a/accounts/urls.py
+++ b/accounts/urls.py
@@ -5,6 +5,8 @@ from django_otp.forms import OTPAuthenticationForm
 
 from . import views
 
+app_name = 'accounts'
+
 urlpatterns = [
     path('logout/', LogoutView.as_view(template_name='logout.html'), name='logout'),
     path('profile/', views.profile, name='profile'),
@@ -13,6 +15,8 @@ urlpatterns = [
     path('user_instance/create/<int:user_id>/', views.user_instance_create, name='user_instance_create'),
     path('user_instance/<int:pk>/update/', views.user_instance_update, name='user_instance_update'),
     path('user_instance/<int:pk>/delete/', views.user_instance_delete, name='user_instance_delete'),
+    path('ssh_key/create/', views.ssh_key_create, name='ssh_key_create'),
+    path('ssh_key/<int:pk>/delete/', views.ssh_key_delete, name='ssh_key_delete'),
 ]
 
 if settings.OTP_ENABLED:
diff --git a/accounts/utils.py b/accounts/utils.py
index 4e003bf..12cf2c1 100644
--- a/accounts/utils.py
+++ b/accounts/utils.py
@@ -1,3 +1,7 @@
+import base64
+import binascii
+import struct
+
 from django_otp import devices_for_user
 from django_otp.plugins.otp_totp.models import TOTPDevice
 
@@ -7,3 +11,29 @@ def get_user_totp_device(user):
     for device in devices:
         if isinstance(device, TOTPDevice):
             return device
+
+
+def validate_ssh_key(key):
+    array = key.encode().split()
+    # Each rsa-ssh key has 3 different strings in it, first one being
+    # typeofkey second one being keystring third one being username .
+    if len(array) != 3:
+        return False
+    typeofkey = array[0]
+    string = array[1]
+    username = array[2]
+    # must have only valid rsa-ssh key characters ie binascii characters
+    try:
+        data = base64.decodestring(string)
+    except binascii.Error:
+        return False
+    # unpack the contents of data, from data[:4] , property of ssh key .
+    try:
+        str_len = struct.unpack('>I', data[:4])[0]
+    except struct.error:
+        return False
+    # data[4:str_len] must have string which matches with the typeofkey, another ssh key property.
+    if data[4:4 + str_len] == typeofkey:
+        return True
+    else:
+        return False
diff --git a/accounts/views.py b/accounts/views.py
index 5bfe73e..7226f45 100644
--- a/accounts/views.py
+++ b/accounts/views.py
@@ -1,14 +1,15 @@
 from admin.decorators import superuser_only
+from django.conf import settings
 from django.contrib import messages
 from django.contrib.auth import update_session_auth_hash
 from django.contrib.auth.decorators import permission_required
 from django.contrib.auth.forms import PasswordChangeForm
-from django.http import HttpResponseRedirect
 from django.shortcuts import get_object_or_404, redirect, render
 from django.urls import reverse
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
 from instances.models import Instance
 
+from accounts.forms import ProfileForm, UserSSHKeyForm
 from accounts.models import *
 
 from . import forms
@@ -16,49 +17,50 @@ from .utils import get_user_totp_device
 
 
 def profile(request):
-    error_messages = []
     publickeys = UserSSHKey.objects.filter(user_id=request.user.id)
+    profile_form = ProfileForm(request.POST or None, instance=request.user)
+    ssh_key_form = UserSSHKeyForm()
 
-    if request.method == "POST":
-        if "username" in request.POST:
-            username = request.POST.get("username", "")
-            email = request.POST.get("email", "")
-            request.user.first_name = username
-            request.user.email = email
-            request.user.save()
-            return HttpResponseRedirect(request.get_full_path())
-        if "keyname" in request.POST:
-            keyname = request.POST.get("keyname", "")
-            keypublic = request.POST.get("keypublic", "")
-            for key in publickeys:
-                if keyname == key.keyname:
-                    msg = _("Key name already exist")
-                    error_messages.append(msg)
-                if keypublic == key.keypublic:
-                    msg = _("Public key already exist")
-                    error_messages.append(msg)
-            if "\n" in keypublic or "\r" in keypublic:
-                msg = _("Invalid characters in public key")
-                error_messages.append(msg)
-            if not error_messages:
-                addkeypublic = UserSSHKey(
-                    user_id=request.user.id,
-                    keyname=keyname,
-                    keypublic=keypublic,
-                )
-                addkeypublic.save()
-                return HttpResponseRedirect(request.get_full_path())
-        if "keydelete" in request.POST:
-            keyid = request.POST.get("keyid", "")
-            delkeypublic = UserSSHKey.objects.get(id=keyid)
-            delkeypublic.delete()
-            return HttpResponseRedirect(request.get_full_path())
-    return render(request, "profile.html", locals())
+    if profile_form.is_valid():
+        profile_form.save()
+        messages.success(request, _('Profile updated'))
+        return redirect('accounts:profile')
+
+    return render(request, "profile.html", {
+        'publickeys': publickeys,
+        'profile_form': profile_form,
+        'ssh_key_form': ssh_key_form,
+    })
+
+
+def ssh_key_create(request):
+    key_form = UserSSHKeyForm(request.POST or None, user=request.user)
+    if key_form.is_valid():
+        key_form.save()
+        messages.success(request, _('SSH key added'))
+        return redirect('accounts:profile')
+
+    return render(request, 'common/form.html', {
+        'form': key_form,
+        'title': _('Add SSH key'),
+    })
+
+
+def ssh_key_delete(request, pk):
+    ssh_key = get_object_or_404(UserSSHKey, pk=pk, user=request.user)
+    if request.method == 'POST':
+        ssh_key.delete()
+        messages.success(request, _('SSH key deleted'))
+        return redirect('accounts:profile')
+
+    return render(request, 'common/confirm_delete.html', {
+        'object': ssh_key,
+        'title': _('Delete SSH key'),
+    })
 
 
 @superuser_only
 def account(request, user_id):
-    error_messages = []
     user = User.objects.get(id=user_id)
     user_insts = UserInstance.objects.filter(user_id=user_id)
     instances = Instance.objects.all().order_by("name")
@@ -74,17 +76,14 @@ def account(request, user_id):
 
 @permission_required("accounts.change_password", raise_exception=True)
 def change_password(request):
-    if request.method == "POST":
-        form = PasswordChangeForm(request.user, request.POST)
-        if form.is_valid():
-            user = form.save()
-            update_session_auth_hash(request, user)  # Important!
-            messages.success(request, _("Password Changed"))
-            return redirect("profile")
-        else:
-            messages.error(request, _("Wrong Data Provided"))
-    else:
-        form = PasswordChangeForm(request.user)
+    form = PasswordChangeForm(request.user, request.POST or None)
+
+    if form.is_valid():
+        user = form.save()
+        update_session_auth_hash(request, user)  # Important!
+        messages.success(request, _("Password Changed"))
+        return redirect("accounts:profile")
+
     return render(request, "accounts/change_password_form.html", {"form": form})
 
 
@@ -95,7 +94,7 @@ def user_instance_create(request, user_id):
     form = forms.UserInstanceForm(request.POST or None, initial={"user": user})
     if form.is_valid():
         form.save()
-        return redirect(reverse("account", args=[user.id]))
+        return redirect(reverse("accounts:account", args=[user.id]))
 
     return render(
         request,
@@ -113,7 +112,7 @@ def user_instance_update(request, pk):
     form = forms.UserInstanceForm(request.POST or None, instance=user_instance)
     if form.is_valid():
         form.save()
-        return redirect(reverse("account", args=[user_instance.user.id]))
+        return redirect(reverse("accounts:account", args=[user_instance.user.id]))
 
     return render(
         request,
@@ -135,7 +134,7 @@ def user_instance_delete(request, pk):
         if next:
             return redirect(next)
         else:
-            return redirect(reverse("account", args=[user.id]))
+            return redirect(reverse("accounts:account", args=[user.id]))
 
     return render(
         request,
diff --git a/admin/templates/admin/user_list.html b/admin/templates/admin/user_list.html
index 1e544f1..746e726 100644
--- a/admin/templates/admin/user_list.html
+++ b/admin/templates/admin/user_list.html
@@ -57,7 +57,7 @@
                         <td>{% if can_clone %}{% icon 'check' %}{% endif %}</td>
                         <td>
                             <div class="float-right btn-group">
-                                <a class="btn btn-success" title="{%trans "View Profile" %}" href="{% url 'account' user.id %}">{% icon 'eye' %}</a>
+                                <a class="btn btn-success" title="{%trans "View Profile" %}" href="{% url 'accounts:account' user.id %}">{% icon 'eye' %}</a>
                                 <a class="btn btn-primary" title="{%trans "Edit" %}" href="{% url 'admin:user_update' user.id %}">{% icon 'pencil' %}</a>
                                 {% if user.is_active %}
                                     <a class="btn btn-warning" title="{%trans "Block" %}" href="{% url 'admin:user_block' user.id %}">{% icon 'stop' %}</a>
diff --git a/appsettings/settings.py b/appsettings/settings.py
index c63a9f0..d575f37 100644
--- a/appsettings/settings.py
+++ b/appsettings/settings.py
@@ -1,7 +1,7 @@
 from .models import AppSettings
 
 
-class Settings(object):
+class Settings:
     pass
 
 
diff --git a/conf/test-vm.xml b/conf/test-vm.xml
new file mode 100644
index 0000000..c407266
--- /dev/null
+++ b/conf/test-vm.xml
@@ -0,0 +1,120 @@
+<domain type="kvm">
+    <name>test-vm</name>
+    <uuid>1bd3c1f2-dd12-4b8d-a298-dff387cb9f93</uuid>
+    <description>None</description>
+    <memory unit="KiB">131072</memory>
+    <currentMemory unit="KiB">131072</currentMemory>
+    <vcpu placement="static">1</vcpu>
+    <os>
+        <type arch="x86_64" machine="pc-q35-5.1">hvm</type>
+        <boot dev="hd" />
+        <boot dev="cdrom" />
+        <bootmenu enable="yes" />
+    </os>
+    <features>
+        <acpi />
+        <apic />
+    </features>
+    <cpu mode="host-model" check="partial" />
+    <clock offset="utc" />
+    <on_poweroff>destroy</on_poweroff>
+    <on_reboot>restart</on_reboot>
+    <on_crash>restart</on_crash>
+    <devices>
+        <emulator>/usr/bin/qemu-system-x86_64</emulator>
+        <disk type="file" device="disk">
+            <driver name="qemu" type="qcow2" cache="directsync" />
+            <source file="/var/lib/libvirt/images/test-volume.qcow2" />
+            <target dev="vda" bus="virtio" />
+            <address type="pci" domain="0x0000" bus="0x03" slot="0x00" function="0x0" />
+        </disk>
+        <disk type="file" device="cdrom">
+            <driver name="qemu" type="raw" />
+            <target dev="sda" bus="sata" />
+            <readonly />
+            <address type="drive" controller="0" bus="0" target="0" unit="0" />
+        </disk>
+        <controller type="usb" index="0" model="qemu-xhci">
+            <address type="pci" domain="0x0000" bus="0x02" slot="0x00" function="0x0" />
+        </controller>
+        <controller type="sata" index="0">
+            <address type="pci" domain="0x0000" bus="0x00" slot="0x1f" function="0x2" />
+        </controller>
+        <controller type="pci" index="0" model="pcie-root" />
+        <controller type="pci" index="1" model="pcie-root-port">
+            <model name="pcie-root-port" />
+            <target chassis="1" port="0x10" />
+            <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x0" multifunction="on" />
+        </controller>
+        <controller type="pci" index="2" model="pcie-root-port">
+            <model name="pcie-root-port" />
+            <target chassis="2" port="0x11" />
+            <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x1" />
+        </controller>
+        <controller type="pci" index="3" model="pcie-root-port">
+            <model name="pcie-root-port" />
+            <target chassis="3" port="0x12" />
+            <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x2" />
+        </controller>
+        <controller type="pci" index="4" model="pcie-root-port">
+            <model name="pcie-root-port" />
+            <target chassis="4" port="0x13" />
+            <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x3" />
+        </controller>
+        <controller type="pci" index="5" model="pcie-root-port">
+            <model name="pcie-root-port" />
+            <target chassis="5" port="0x14" />
+            <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x4" />
+        </controller>
+        <controller type="pci" index="6" model="pcie-root-port">
+            <model name="pcie-root-port" />
+            <target chassis="6" port="0x15" />
+            <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x5" />
+        </controller>
+        <controller type="pci" index="7" model="pcie-root-port">
+            <model name="pcie-root-port" />
+            <target chassis="7" port="0x16" />
+            <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x6" />
+        </controller>
+        <controller type="pci" index="8" model="pcie-root-port">
+            <model name="pcie-root-port" />
+            <target chassis="8" port="0x17" />
+            <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x7" />
+        </controller>
+        <interface type="network">
+            <mac address="52:54:00:a2:3c:e7" />
+            <source network="default" />
+            <model type="virtio" />
+            <address type="pci" domain="0x0000" bus="0x01" slot="0x00" function="0x0" />
+        </interface>
+        <serial type="pty">
+            <target type="isa-serial" port="0">
+                <model name="isa-serial" />
+            </target>
+        </serial>
+        <console type="pty">
+            <target type="serial" port="0" />
+        </console>
+        <input type="mouse" bus="virtio">
+            <address type="pci" domain="0x0000" bus="0x05" slot="0x00" function="0x0" />
+        </input>
+        <input type="keyboard" bus="virtio">
+            <address type="pci" domain="0x0000" bus="0x06" slot="0x00" function="0x0" />
+        </input>
+        <input type="tablet" bus="virtio">
+            <address type="pci" domain="0x0000" bus="0x07" slot="0x00" function="0x0" />
+        </input>
+        <input type="mouse" bus="ps2" />
+        <input type="keyboard" bus="ps2" />
+        <graphics type="spice" autoport="yes" listen="0.0.0.0">
+            <listen type="address" address="0.0.0.0" />
+        </graphics>
+        <video>
+            <model type="vga" vram="16384" heads="1" primary="yes" />
+            <address type="pci" domain="0x0000" bus="0x00" slot="0x01" function="0x0" />
+        </video>
+        <memballoon model="virtio">
+            <address type="pci" domain="0x0000" bus="0x04" slot="0x00" function="0x0" />
+        </memballoon>
+    </devices>
+</domain>
diff --git a/instances/models.py b/instances/models.py
index 34d7e19..563a5e1 100644
--- a/instances/models.py
+++ b/instances/models.py
@@ -204,6 +204,10 @@ class Instance(models.Model):
     def formats(self):
         return self.proxy.get_image_formats()
 
+    @cached_property
+    def interfaces(self):
+        return self.proxy.get_ifaces()
+
 
 class PermissionSet(models.Model):
     """
@@ -211,8 +215,9 @@ class PermissionSet(models.Model):
     """
     class Meta:
         default_permissions = ()
-        permissions = [('clone_instances', 'Can clone instances'),  
-                       ('passwordless_console',  _('Can access console without password')),
-                    ]
+        permissions = [
+            ('clone_instances', 'Can clone instances'),
+            ('passwordless_console', _('Can access console without password')),
+        ]
 
         managed = False
diff --git a/instances/templates/add_instance_network_block.html b/instances/templates/add_instance_network_block.html
index 724ca80..6d82fd5 100644
--- a/instances/templates/add_instance_network_block.html
+++ b/instances/templates/add_instance_network_block.html
@@ -27,7 +27,7 @@
                                     {% for c_net in networks_host %}
                                         <option value="net:{{ c_net }}">Network {{ c_net }}</option>
                                     {% endfor %}
-                                    {% for c_iface in interfaces_host %}
+                                    {% for c_iface in instance.interfaces %}
                                         <option value="iface:{{ c_iface }}">Interface {{ c_iface }}</option>
                                     {% endfor %}
                                 </select>
diff --git a/instances/templates/instances/settings_tab.html b/instances/templates/instances/settings_tab.html
index 62d6c5a..caec28f 100644
--- a/instances/templates/instances/settings_tab.html
+++ b/instances/templates/instances/settings_tab.html
@@ -393,7 +393,7 @@
                                                             {% for c_net in networks_host %}
                                                             <option value="net:{{ c_net }}" {% if c_net == network.nic %} selected {% endif %}>{% trans 'Network' %} {{ c_net }}</option>
                                                             {% endfor %}
-                                                            {% for c_iface in interfaces_host %}
+                                                            {% for c_iface in instance.interfaces %}
                                                             <option value="iface:{{ c_iface }}" {% if c_iface == network.nic %} selected {% endif %}>{% trans 'Interface' %} {{ c_iface }}</option>
                                                             {% endfor %}
                                                         </select>
@@ -657,9 +657,9 @@
                     <tbody class="searchable">
                     {% for userinstance in userinstances %}
                         <tr>
-                            <td><a href="{% url 'account' userinstance.user.id %}">{{ userinstance.user }}</a></td>
+                            <td><a href="{% url 'accounts:account' userinstance.user.id %}">{{ userinstance.user }}</a></td>
                             <td style="width:30px;">
-                                <a href="{% url 'user_instance_delete' userinstance.id %}?next={% url 'instances:instance' instance.id %}#users">
+                                <a href="{% url 'accounts:user_instance_delete' userinstance.id %}?next={% url 'instances:instance' instance.id %}#users">
                                     {% icon 'trash' %}
                                 </a>
                             </td>
diff --git a/instances/tests.py b/instances/tests.py
index bcdf9ea..3dac112 100644
--- a/instances/tests.py
+++ b/instances/tests.py
@@ -116,7 +116,7 @@ class InstancesTestCase(TestCase):
         self.assertEqual(instance.disks[0]['size'], 1024**3)
 
         response = self.client.post(reverse('instances:resize_disk', args=[instance.id]), {
-            'disk_size_vda': '2.0 GB',
+            'disk_size_vda': '2',
         })
         self.assertRedirects(response, reverse('instances:instance', args=[instance.id]) + '#resize')
 
@@ -449,9 +449,8 @@ class InstancesTestCase(TestCase):
         response = self.client.post(
             reverse('instances:destroy', args=[instance.id]),
             {'delete_disk': True},
-            HTTP_REFERER=reverse('index'),
         )
-        self.assertRedirects(response, reverse('instances', args=[compute.id]))
+        self.assertRedirects(response, reverse('instances:index'))
 
         # # create volume
         # response = self.client.post(
diff --git a/templates/common/confirm_delete.html b/templates/common/confirm_delete.html
index 015e46c..6f4a42c 100644
--- a/templates/common/confirm_delete.html
+++ b/templates/common/confirm_delete.html
@@ -3,17 +3,25 @@
 {% load icons %}
 {% load i18n %}
 
-{% block title %}{%trans "Delete" %}{% endblock %}
+{% block title %}{{ title }}{% endblock %}
+
+{% block page_header %}{{ title }}{% endblock page_header %}
 
 {% block content %}
-<form method="post">
-    {% csrf_token %}
-    <div class="alert alert-warning">
-        {%trans "Are you sure you want to delete" %} "{{ object }}"?
+<div class="row">
+    <div class="offset-3 col-6">
+        <form method="post">
+            {% csrf_token %}
+            <div class="alert alert-warning">
+                {%trans "Are you sure you want to delete" %} "{{ object }}"?
+            </div>
+            <div class="form-group mb-0 float-right">
+                <a class="btn btn-primary" href="javascript:history.back()">{% icon 'times' %} {% trans "Cancel" %}</a>
+                <button type="submit" class="btn btn-danger">
+                    {% icon 'trash' %} {% trans "Delete" %}
+                </button>
+            </div>
+        </form>
     </div>
-    <a class="btn btn-primary" href="javascript:history.back()">{% icon 'times' %} {% trans "Cancel" %}</a>
-    <button type="submit" class="btn btn-danger">
-        {% icon 'check' %} {% trans "Delete" %}
-    </button>
-</form>
+</div>
 {% endblock %}
\ No newline at end of file
diff --git a/templates/navbar.html b/templates/navbar.html
index c1881fa..106d366 100644
--- a/templates/navbar.html
+++ b/templates/navbar.html
@@ -42,9 +42,9 @@
             <a class="dropdown-item disabled" href="#">
               {% trans "Language" %}: <span class="badge badge-secondary">{{ LANGUAGE_CODE }}</span>
             </a>
-            <a class="dropdown-item {% view_active request 'profile' %}" href="{% url 'profile' %}">{% icon 'vcard' %} {% trans "Profile" %}</a>
+            <a class="dropdown-item {% view_active request 'accounts:profile' %}" href="{% url 'accounts:profile' %}">{% icon 'vcard' %} {% trans "Profile" %}</a>
             <div class="dropdown-divider"></div>
-            <a class="dropdown-item" href="{% url 'logout' %}"><i class="fa fa-fw fa-power-off"></i> {% trans "Log Out" %}</a>
+            <a class="dropdown-item" href="{% url 'accounts:logout' %}"><i class="fa fa-fw fa-power-off"></i> {% trans "Log Out" %}</a>
           </div>
         </li>
       </ul>