Provider CRUD & New OpenStack API Stuff

Create, edit, update and delete cloud providers.
Lots of new functions and hard to explaing them all

User create on provider.

Change-Id: I8d45ea14b2f62551500acf809cdb5190119548bb
This commit is contained in:
kelepirci 2016-08-25 23:36:51 +03:00
parent 9f70e2b843
commit d4d5e9fd80
22 changed files with 1123 additions and 46 deletions

View File

@ -3,7 +3,7 @@ from flask import flash
from wtforms import StringField, PasswordField, BooleanField, SubmitField, \
ValidationError, SelectField
from wtforms.validators import Required, Length, Email, Regexp, EqualTo
from ..models import User, Role
from ..models import User, Role, Provider
class EditUserAdminForm(Form):
email = StringField('Email', validators=[Required(), Length(1, 128),
@ -58,4 +58,53 @@ class CreateUserAdminForm(Form):
class DeleteUserAdminForm(Form):
confirm = BooleanField('Confirmed', validators=[Required(message='You must confirm to delete.')])
class CreateUserProviderAdminForm(Form):
provider = SelectField('Provider', coerce=int)
def __init__(self, user, *args, **kwargs):
super(CreateUserProviderAdminForm, self).__init__(*args, **kwargs)
self.provider.choices = [(provider.id, provider.name)
for provider in Provider.query.order_by(Provider.name).all()]
class CreateProviderAdminForm(Form):
provider = SelectField('Provider', validators=[Required()],
choices=[('openstack', 'OpenStack'), ('other', 'Other')])
name = StringField('Name', validators=[Required(), Length(1, 256)])
project_name = StringField('Admin Project Name', validators=[Required(), Length(1, 128)])
region = StringField('Region Name', validators=[Required(), Length(1, 128)])
default_role = StringField('Default Role Name', validators=[Required(), Length(1, 128)])
username = StringField('User Name', validators=[Required(), Length(1, 128)])
password = PasswordField('Password', validators=[Required(), Length(1, 256)])
api_version = StringField('API Version', validators=[Required(), Length(1, 64)])
url = StringField('URL', validators=[Required(), Length(1, 256)])
enabled = BooleanField('Enabled')
def validate_name(self, field):
if Provider.query.filter_by(name=field.data).first():
raise ValidationError('Provider name is already used. Use different name.')
class EditProviderAdminForm(Form):
provider = SelectField('Provider', validators=[Required()],
choices=[('openstack', 'OpenStack'), ('other', 'Other')])
name = StringField('Name', validators=[Required(), Length(1, 128)])
project_name = StringField('Tenant Name', validators=[Required(), Length(1, 128)])
region = StringField('Region Name', validators=[Required(), Length(1, 128)])
default_role = StringField('Default Role Name', validators=[Required(), Length(1, 128)])
username = StringField('User Name', validators=[Required(), Length(1, 128)])
password = PasswordField('Password', validators=[Required(), Length(1, 256)])
api_version = StringField('API Version', validators=[Required(), Length(1, 64)])
url = StringField('URL', validators=[Required(), Length(1, 256)])
enabled = BooleanField('Enabled')
class DeleteProviderAdminForm(Form):
confirm = BooleanField('Confirmed', validators=[Required(message='You must confirm to delete.')])
class ValidateProviderAdminForm(Form):
provider = SelectField('Provider', coerce=int)
confirm = BooleanField('Confirmed', validators=[Required(message='You must confirm to delete.')])
def __init__(self, provider, *args, **kwargs):
super(ValidateProviderAdminForm, self).__init__(*args, **kwargs)
self.provider.choices = [(provider.id, provider.name)
for provider in Provider.query.order_by(Provider.name).all()]

23
dash/admin/helpers.py Normal file
View File

@ -0,0 +1,23 @@
import requests, json
from openstack import connection, profile
from ..models import Provider
class Connect():
def create_connection(self, auth_url, region, project_name, username, password):
prof = profile.Profile()
prof.set_version('identity', 'v3')
prof.set_api_version('identity','v3')
prof.set_region(profile.Profile.ALL, region)
return connection.Connection(
profile=prof,
user_agent='examples',
auth_url=auth_url,
identity_api_version='3',
project_name=project_name,
username=username,
password=password
)

View File

@ -0,0 +1,86 @@
import requests, json
from ..models import Provider
# OpenStack Identity V3 API Calls
class Identity:
def auth_unscoped(self, auth_url, username, password, domain):
payload = {
"auth": {
"identity": {
"methods": [
"password"
],
"password": {
"user": {
"name": username,
"domain": {
"id": domain
},
"password": password
}
}
}
}
}
auth_url = "%s/v3/auth/tokens" %(auth_url)
resp = requests.session()
resp = resp.post(auth_url, data=json.dumps(payload))
return resp
def auth_unscoped_token(self, auth_url, provider_id):
provider = Provider.query.get_or_404(provider_id)
payload = {
"auth": {
"identity": {
"methods": [
"token"
],
"token": {
"id": provider.x_subject_token
}
}
}
}
auth_url = "%s/v3/auth/tokens" %(auth_url)
resp = requests.session()
resp = resp.post(auth_url, data=json.dumps(payload))
return resp
def auth_scoped(self, auth_url, username, password, domain):
payload = {
"auth": {
"identity": {
"methods": [
"password"
],
"password": {
"user": {
"name": username,
"password": password
}
}
},
"scope": {
"project": {
"name": project
}
}
}
}
auth_url = "%s/v3/auth/tokens" %(auth_url)
resp = requests.post(auth_url, data=json.dumps(payload))
return resp
def auth_validate_show_token(self, auth_url, provider_id):
provider = Provider.query.get_or_404(provider_id)
headers = {'X-Auth-Token': provider.x_subject_token , 'X-Subject-Token': provider.x_subject_token}
auth_url = "%s/v3/auth/tokens" % (auth_url)
resp = requests.get(auth_url, headers=headers)
return resp
def list_projects(self, url, x_subject_token):
headers = {'X-Auth-Token': x_subject_token}
auth_url = "%s/v3/projects" % (url)
resp = requests.get(auth_url, headers=json.dumps(headers))
return resp

View File

@ -1,4 +1,8 @@
import datetime
import datetime, requests, json, string, random
from keystoneauth1.identity import v3
from keystoneauth1 import session
from keystoneclient.v3 import client
from flask import render_template, redirect, request, url_for, flash
from flask_login import login_user, logout_user, login_required, \
@ -8,11 +12,13 @@ from flask_principal import Identity, AnonymousIdentity, \
from . import admin
from .. import db
from ..models import User, Role
from ..models import User, Role, Provider
from ..email import send_email
from ..decorators import requires_roles
from .forms import EditUserAdminForm, CreateUserAdminForm, CreateUserAdminForm, \
DeleteUserAdminForm
DeleteUserAdminForm, CreateProviderAdminForm, EditProviderAdminForm, \
DeleteProviderAdminForm, ValidateProviderAdminForm, \
CreateUserProviderAdminForm
@admin.route('/')
@login_required
@ -51,6 +57,50 @@ def create_user_admin():
return render_template('admin/create_user.html', form=form,
title="Create New User",
block_description = "fill all the fields to create new user")
@admin.route('/create-user-provider/<int:id>', methods=['GET', 'POST'])
@login_required
@requires_roles("admin")
def create_user_provider_admin(id):
user = User.query.get_or_404(id)
providers = Provider.query.all()
form = CreateUserProviderAdminForm(user=user)
if form.validate_on_submit():
if len(user.provider_password) != 24:
chars = string.letters + string.digits + string.punctuation
user.provider_password = ''.join((random.choice(chars)) for x in range(24))
db.session.add(user)
# make openstack api connection for chosen provider
provider = Provider.query.get_or_404(form.provider.data)
auth = v3.Password(auth_url=provider.url,
username=provider.username,
password=provider.password,
project_id=provider.project_name,
user_domain_name='Default')
sess = session.Session(auth=auth)
keystone = client.Client(session=sess)
# create project
project = keystone.projects.create(user.username,
'default',
enabled=True)
# creates user
user = keystone.users.create(user.username,
password=user.provider_password,
email=user.email,
enabled=True,
default_project=user.username)
role_search = keystone.roles.list(name=provider.default_role)
role_id = role_search[0].id
role = keystone.roles.grant(role_id,
user=user.id,
project=project.id)
return render_template('admin/create_user_provider.html', user=user, form=form,
title="Create User on Provider", providers=providers,
project=project,role=role,
block_description = "create a user account on selected cloud provider")
return render_template('admin/create_user_provider.html', user=user, form=form,
title="Create User on Provider", providers=providers,
block_description = "create a user account on selected cloud provider")
@admin.route('/edit-user/<int:id>', methods=['GET', 'POST'])
@login_required
@ -87,4 +137,99 @@ def delete_user_admin(id):
return redirect(url_for('.index'))
return render_template('admin/delete_user.html', user=user, form=form,
title="Delete User",
block_description = "delete user confirmation")
block_description = "delete user confirmation")
@admin.route('/list-providers')
@login_required
@requires_roles("admin")
def list_providers():
providers = Provider.query.all()
return render_template('admin/list_providers.html', providers=providers,
title="List Providers",
block_description = "list, edit and delete providers")
@admin.route('/create-provider', methods=['GET', 'POST'])
@login_required
@requires_roles("admin")
def create_provider_admin():
form = CreateProviderAdminForm()
if form.validate_on_submit():
provider = Provider(provider=form.provider.data,
name=form.name.data,
project_name=form.project_name.data,
default_role=form.default_role.data,
username=form.username.data,
password=form.password.data,
api_version=form.api_version.data,
url=form.url.data,
created_at=datetime.datetime.now(),
enabled=form.enabled.data)
db.session.add(provider)
db.session.commit()
flash('New provider created.')
return redirect(url_for('.edit_provider_admin', id=provider.id))
return render_template('admin/create_provider.html', form=form,
title="Create New Provider",
block_description = "add your new cloud provider")
@admin.route('/edit-provider/<int:id>', methods=['GET', 'POST'])
@login_required
@requires_roles("admin")
def edit_provider_admin(id):
provider = Provider.query.get_or_404(id)
form = EditProviderAdminForm(provider=provider)
if form.validate_on_submit():
provider.provider = form.provider.data
provider.name = form.name.data
provider.project_name = form.project_name.data
provider.region = form.region.data
provider.default_role=form.default_role.data
provider.username = form.username.data
provider.password = form.password.data
provider.api_version = form.api_version.data
provider.url = form.url.data
provider.enabled = form.enabled.data
db.session.add(provider)
flash('The provider has been updated.')
return redirect(url_for('.edit_provider_admin', id=provider.id))
return render_template('admin/edit_provider.html', provider=provider, form=form,
title="Edit Provider",
block_description = "edit and update provider info")
@admin.route('/delete-provider/<int:id>', methods=['GET', 'POST'])
@login_required
@requires_roles("admin")
def delete_provider_admin(id):
provider = Provider.query.get_or_404(id)
form = DeleteProviderAdminForm(provider=provider)
if form.validate_on_submit():
db.session.delete(provider)
db.session.commit()
flash('The provider has been deleted.')
return redirect(url_for('.index'))
return render_template('admin/delete_provider.html', provider=provider, form=form,
title="Delete Provider",
block_description = "delete provider confirmation")
@admin.route('/validate-provider/<int:id>', methods=['GET', 'POST'])
@login_required
@requires_roles("admin")
def validate_provider_admin(id):
provider = Provider.query.get_or_404(id)
auth = v3.Password(auth_url=provider.url,
username=provider.username,
password=provider.password,
project_id=provider.project_name,
user_domain_name='Default')
sess = session.Session(auth=auth)
keystone = client.Client(session=sess)
roles = keystone.roles.list(name='_member_')
role_id = roles[0].id
roles_type = type(roles)
project = keystone.projects.list(name='admin')
projects = 'projects'
return render_template('admin/validate_provider.html', provider=provider,
title="Validate Provider", #projects=projects,
role_id=role_id,
keystone=keystone, roles=roles, project=project, roles_type=roles_type,
block_description = "validate provider")

View File

@ -34,6 +34,7 @@ class User(UserMixin, db.Model):
avatar = db.Column(db.String(255), index=True)
created_at = db.Column(db.DateTime)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
provider_password = db.Column(db.Text(255))
confirmed = db.Column(db.Boolean, default=False)
suspended = db.Column(db.Boolean, default=False)
@ -108,6 +109,24 @@ class User(UserMixin, db.Model):
def __repr__(self):
return '<User %r>' % self.username
class Provider(db.Model):
__tablename__ = 'providers'
id = db.Column(db.Integer, primary_key=True)
provider = db.Column(db.String(128), unique=False, index=True)
name = db.Column(db.String(128), unique=True, index=True)
region = db.Column(db.String(128), unique=False, index=True)
project_name = db.Column(db.String(128), unique=False, index=True)
default_role = db.Column(db.String(128), unique=False, index=True)
username = db.Column(db.String(128), unique=False, index=True)
api_version = db.Column(db.String(64), unique=False, index=True)
password = db.Column(db.Text(255), unique=False, index=False)
url = db.Column(db.Text(512))
created_at = db.Column(db.DateTime)
enabled = db.Column(db.Boolean, default=True, nullable=False)
validated = db.Column(db.Boolean, default=False, nullable=False)
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))

View File

@ -0,0 +1,132 @@
{% extends "adminlte/base.html" %}
{% import "adminlte/layout.html" as layout with context %}
{% import "adminlte/widgets.html" as widgets with context %}
{% from "_formhelpers.html" import render_field %}
{% block title %}Admin - {{ title }}{% endblock %}
{% block description %}{{ block_description }}{% endblock %}
{% block navbar %}
{% include "navbar.html" %}
{%- endblock navbar %}
{% block sidebar -%}
{% include 'sidebar.html' %}
{% include 'admin/sidebar_menu.html' %}
{%- endblock sidebar %}
{% block content_header -%}
{% include 'admin/content_header.html' %}
{%- endblock content_header %}
{% block content -%}
<!-- Main content -->
<div class="row">
<div class="col-xs-12">
<div class="box">
<!-- /.box-header -->
<div class="register-box-body">
<form class="form-horizontal" action="" method="post">
{{ form.hidden_tag() }}
<div class="form-group has-feedback">
<select type="select" name="provider" class="form-control">
<option value="openstack" selected >OpenStack</option>
<option value="other" disabled>Other</option>
</select>
<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
{% if form.provider.errors %}
<span class="text-red">{% for error in form.provider.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="text" name="name" class="form-control" placeholder="Name your provider" value="{{ request.form['name'] }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
{% if form.name.errors %}
<span class="text-red">{% for error in form.name.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="text" name="region" class="form-control" placeholder="Name of the region" value="{{ request.form['region'] }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
{% if form.region.errors %}
<span class="text-red">{% for error in form.region.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="text" name="username" class="form-control" placeholder="Admin user name" value="{{ request.form['username'] }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
{% if form.username.errors %}
<span class="text-red">{% for error in form.username.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="text" name="project_name" class="form-control" placeholder="Project name" value="{{ request.form['project_name'] }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
{% if form.project_name.errors %}
<span class="text-red">{% for error in form.project_name.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="text" name="default_role" class="form-control" placeholder="Default role name" value="{{ request.form['default_role'] }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
{% if form.default_role.errors %}
<span class="text-red">{% for error in form.default_role.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="text" name="api_version" class="form-control" placeholder="API Version" value="{{ request.form['api_version'] }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
{% if form.api_version.errors %}
<span class="text-red">{% for error in form.api_version.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="text" name="url" class="form-control" placeholder="API URL" value="{{ request.form['url'] }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
{% if form.url.errors %}
<span class="text-red">{% for error in form.url.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="password" name="password" class="form-control" placeholder="Password">
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
{% if form.password.errors %}
<span class="text-red">{% for error in form.password.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="row">
<div class="col-xs-8 col-xs-offset-0">
<div class="checkbox">
<label>
<input type="checkbox" name="enabled"> Enabled?
</label>
{% if form.enabled.errors %}
<br />
<span class="text-red">{% for error in form.enabled.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
</div>
<!-- /.col -->
<div class="col-xs-4">
<button type="submit" name="submit" class="btn btn-primary btn-block btn-flat" value="Update">Create</button>
</div>
<!-- /.col -->
</div>
</form>
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
{%- endblock content %}

View File

@ -0,0 +1,89 @@
{% extends "adminlte/base.html" %}
{% import "adminlte/layout.html" as layout with context %}
{% import "adminlte/widgets.html" as widgets with context %}
{% from "_formhelpers.html" import render_field %}
{% block title %}Admin - {{ title }}{% endblock %}
{% block description %}{{ block_description }}{% endblock %}
{% block navbar %}
{% include "navbar.html" %}
{%- endblock navbar %}
{% block sidebar -%}
{% include 'sidebar.html' %}
{% include 'admin/sidebar_menu.html' %}
{%- endblock sidebar %}
{% block content_header -%}
{% include 'admin/content_header.html' %}
{%- endblock content_header %}
{% block content -%}
<!-- Main content -->
<div class="row">
<div class="col-xs-12">
<div class="box">
<!-- /.box-header -->
<div class="box-header with-border">
<i class="fa fa-info"></i>
<h3 class="box-title">Select Cloud Proivder and Create User Account</h3>
</div>
<!-- /.box-header -->
<div class="register-box-body">
<form class="form-horizontal" action="" method="post">
{{ form.hidden_tag() }}
<div class="row">
<div class="col-xs-8 col-xs-offset-0">
<div class="form-group has-feedback">
<label>Chose the Provider</label>
<select name="provider" class="form-control">
{% for provider in providers %}
<option value="{{ provider.id }}" >{{ provider.name }}</option>
{% endfor %}
</select>
<span class="glyphicon glyphicon-signal form-control-feedback"></span>
{% if form.provider.errors %}
<span class="text-red">{% for error in form.provider.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
</div>
<!-- /.col -->
<div class="col-xs-4">
<label>Start</label>
<button type="submit" name="submit" class="btn btn-danger btn-block btn-flat" value="Update">Create</button>
</div>
<!-- /.col -->
</div>
</form>
</div>
</div>
<!-- /.box -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="register-box-body">
<div class="row">
<h3>Result</h3>
{% if project %}
{{ project }} <br />
{{ user }} <br />
{% endif %}
</div>
</div>
</div>
</div>
</div>
{%- endblock content %}

View File

@ -0,0 +1,84 @@
{% extends "adminlte/base.html" %}
{% import "adminlte/layout.html" as layout with context %}
{% import "adminlte/widgets.html" as widgets with context %}
{% from "_formhelpers.html" import render_field %}
{% block title %}Admin - {{ title }}{% endblock %}
{% block description %}{{ block_description }}{% endblock %}
{% block navbar %}
{% include "navbar.html" %}
{%- endblock navbar %}
{% block sidebar -%}
{% include 'sidebar.html' %}
{% include 'admin/sidebar_menu.html' %}
{%- endblock sidebar %}
{% block content_header -%}
{% include 'admin/content_header.html' %}
{%- endblock content_header %}
{% block content -%}
<!-- Main content -->
<div class="row">
<div class="col-xs-12">
<div class="box">
<!-- /.box-header -->
<div class="box-header with-border">
<i class="fa fa-warning"></i>
<h3 class="box-title">You are Warned</h3>
</div>
<!-- /.box-header -->
<div class="box-body">
<div class="alert alert-danger alert-dismissible">
<h4><i class="icon fa fa-ban"></i> Provider Delete Alert!</h4>
Your are about to delete cloud provider "{{ provider.name }}" from database.
You will not able to undo this action.
<br />
Are you sure you want to delete this provider?
</div>
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
<div class="register-box-body">
<form class="form-horizontal" action="" method="post">
{{ form.hidden_tag() }}
<div class="row">
<div class="col-xs-8 col-xs-offset-0">
<div class="checkbox">
<label>
<input type="checkbox" name="confirm"> I confirm to delete "{{ provider.name }}".
</label>
{% if form.confirm.errors %}
<br />
<span class="text-red">{% for error in form.confirm.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
</div>
<!-- /.col -->
<div class="col-xs-4">
<button type="submit" name="submit" class="btn btn-danger btn-block btn-flat" value="Update">Delete Provider</button>
</div>
<!-- /.col -->
</div>
</form>
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
{%- endblock content %}

View File

@ -0,0 +1,132 @@
{% extends "adminlte/base.html" %}
{% import "adminlte/layout.html" as layout with context %}
{% import "adminlte/widgets.html" as widgets with context %}
{% from "_formhelpers.html" import render_field %}
{% block title %}Admin - {{ title }}{% endblock %}
{% block description %}{{ block_description }}{% endblock %}
{% block navbar %}
{% include "navbar.html" %}
{%- endblock navbar %}
{% block sidebar -%}
{% include 'sidebar.html' %}
{% include 'admin/sidebar_menu.html' %}
{%- endblock sidebar %}
{% block content_header -%}
{% include 'admin/content_header.html' %}
{%- endblock content_header %}
{% block content -%}
<!-- Main content -->
<div class="row">
<div class="col-xs-12">
<div class="box">
<!-- /.box-header -->
<div class="register-box-body">
<form class="form-horizontal" action="" method="post">
{{ form.hidden_tag() }}
<div class="form-group has-feedback">
<select type="select" name="provider" class="form-control">
<option value="openstack" selected >OpenStack</option>
<option value="other" disabled>Other</option>
</select>
<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
{% if form.provider.errors %}
<span class="text-red">{% for error in form.provider.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="text" name="name" class="form-control" placeholder="Name your provider" value="{{ provider.name }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
{% if form.name.errors %}
<span class="text-red">{% for error in form.name.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="text" name="region" class="form-control" placeholder="Name of the region" value="{{ provider.region }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
{% if form.region.errors %}
<span class="text-red">{% for error in form.region.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="text" name="username" class="form-control" placeholder="Admin user name" value="{{ provider.username }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
{% if form.username.errors %}
<span class="text-red">{% for error in form.username.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="text" name="project_name" class="form-control" placeholder="Project name" value="{{ provider.project_name }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
{% if form.project_name.errors %}
<span class="text-red">{% for error in form.project_name.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="text" name="default_role" class="form-control" placeholder="Tenant name" value="{{ provider.default_role }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
{% if form.default_role.errors %}
<span class="text-red">{% for error in form.default_role.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="text" name="api_version" class="form-control" placeholder="API Version" value="{{ provider.api_version }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
{% if form.api_version.errors %}
<span class="text-red">{% for error in form.api_version.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="text" name="url" class="form-control" placeholder="API URL" value="{{ provider.url }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
{% if form.url.errors %}
<span class="text-red">{% for error in form.url.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="form-group has-feedback">
<input type="password" name="password" class="form-control" placeholder="Password" value="{{ provider.password }}">
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
{% if form.password.errors %}
<span class="text-red">{% for error in form.password.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
<div class="row">
<div class="col-xs-8 col-xs-offset-0">
<div class="checkbox">
<label>
<input type="checkbox" name="enabled" {% if provider.enabled %} checked {% endif %}> Enabled?
</label>
{% if form.enabled.errors %}
<br />
<span class="text-red">{% for error in form.enabled.errors %} {{ error }} {% endfor %}</span>
{% endif %}
</div>
</div>
<!-- /.col -->
<div class="col-xs-4">
<button type="submit" name="submit" class="btn btn-primary btn-block btn-flat" value="Update">Update</button>
</div>
<!-- /.col -->
</div>
</form>
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
{%- endblock content %}

View File

@ -31,7 +31,6 @@
<div class="row">
<div class="col-xs-12">
<div class="box">
<!-- /.box-header -->
<div class="register-box-body">
<form class="form-horizontal" action="" method="post">
{{ form.hidden_tag() }}
@ -103,5 +102,15 @@
<!-- /.col -->
</div>
<!-- /.row -->
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="register-box-body">
<a href="{{ url_for('.create_user_provider_admin', id=user.id) }}">
<button class="btn btn-primary btn-flat">Create Account on Provider</button>
</div>
</div>
</div>
</div>
{%- endblock content %}

View File

@ -0,0 +1,87 @@
{% extends "adminlte/base.html" %}
{% import "adminlte/layout.html" as layout with context %}
{% import "adminlte/widgets.html" as widgets with context %}
{% block title %}Admin - {{ title }}{% endblock %}
{% block description %}{{ block_description }}{% endblock %}
{% block navbar %}
{% include "navbar.html" %}
{%- endblock navbar %}
{% block sidebar -%}
{% include 'sidebar.html' %}
{% include 'admin/sidebar_menu.html' %}
{%- endblock sidebar %}
{% block content_header -%}
{% include 'admin/content_header.html' %}
{%- endblock content_header %}
{% block content -%}
<!-- Main content -->
<div class="row">
<div class="col-xs-12">
<div class="box">
<!-- /.box-header -->
<div class="box-body">
<table id="example2" class="table table-bordered table-hover">
<thead>
<tr>
<th>ID</th>
<th>Provider</th>
<th>Name</th>
<th>Tenant Name</th>
<th>User Name</th>
<th>API Version</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for provider in providers %}
<tr>
<td>{{ provider.id }}</td>
<td>{{ provider.provider }}</td>
<td>{{ provider.name }}</td>
<td>{{ provider.project_name }}</td>
<td>{{ provider.username }}</td>
<td>{{ provider.api_version }}</td>
<td>
<a href="{{ url_for('admin.edit_provider_admin', id=provider.id) }}">Edit</a>
|
<a href="{{ url_for('admin.delete_provider_admin', id=provider.id) }}">Delete</a>
|
<a href="{{ url_for('admin.validate_provider_admin', id=provider.id) }}">Validate</a>
</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<th>ID</th>
<th>Provider</th>
<th>Name</th>
<th>Tenant Name</th>
<th>User Name</th>
<th>API Version</th>
<th>Action</th>
</tr>
</tfoot>
</table>
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
{%- endblock content %}

View File

@ -5,7 +5,7 @@
<i class="fa fa-dashboard"></i> <span>Admin Dashboard</span>
</a>
</li>
<!-- User management menu -->
<li class="treeview">
<a href="#">
<i class="fa fa-users"></i>
@ -33,4 +33,27 @@
</li>
</ul>
</li>
<!-- Provider management menu -->
<li class="treeview">
<a href="#">
<i class="fa fa-cloud"></i>
<span>Providers</span>
<i class="fa fa-angle-left pull-right"></i>
</a>
<ul class="treeview-menu">
<li>
<a href="{{ url_for('admin.create_provider_admin') }}">
<i class="fa fa-cloud-upload"></i>
Create Provider
</a>
</li>
<li>
<a href="{{ url_for('admin.list_providers') }}">
<i class="fa fa-list"></i>
List Providers
</a>
</li>
</ul>
</li>
</ul>

View File

@ -0,0 +1,55 @@
{% extends "adminlte/base.html" %}
{% import "adminlte/layout.html" as layout with context %}
{% import "adminlte/widgets.html" as widgets with context %}
{% from "_formhelpers.html" import render_field %}
{% block title %}Admin - {{ title }}{% endblock %}
{% block description %}{{ block_description }}{% endblock %}
{% block navbar %}
{% include "navbar.html" %}
{%- endblock navbar %}
{% block sidebar -%}
{% include 'sidebar.html' %}
{% include 'admin/sidebar_menu.html' %}
{%- endblock sidebar %}
{% block content_header -%}
{% include 'admin/content_header.html' %}
{%- endblock content_header %}
{% block content -%}
<!-- Main content -->
<div class="row">
<div class="col-xs-12">
<div class="box">
<!-- /.box-header -->
<div class="box-body">
Validating: {{provider.name}} - {{ provider.url }} <br />
<hr />
{{ project[0]['id'] }} <br />
{{ role_id }}
<hr />
{{ roles_type }} <br />
{% for r in roles %}
{{ r['id'] }}
{% endfor %}
<hr />
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
{%- endblock content %}

View File

@ -1,14 +1,14 @@
"""empty message
Revision ID: 5cc72c718d14
Revises: ad5c9cae2c6d
Create Date: 2016-08-24 22:56:39.588007
Revision ID: 2b52e8261507
Revises: 9f9f6374b616
Create Date: 2016-08-27 17:17:07.016592
"""
# revision identifiers, used by Alembic.
revision = '5cc72c718d14'
down_revision = 'ad5c9cae2c6d'
revision = '2b52e8261507'
down_revision = '9f9f6374b616'
from alembic import op
import sqlalchemy as sa
@ -16,11 +16,11 @@ import sqlalchemy as sa
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('users', sa.Column('suspended', sa.Boolean(), nullable=True))
op.add_column('users', sa.Column('provider_password', sa.String(length=128), nullable=True))
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_column('users', 'suspended')
op.drop_column('users', 'provider_password')
### end Alembic commands ###

View File

@ -0,0 +1,28 @@
"""empty message
Revision ID: 48c96b4cae0c
Revises: 73982db0bb88
Create Date: 2016-08-31 21:53:02.488962
"""
# revision identifiers, used by Alembic.
revision = '48c96b4cae0c'
down_revision = '73982db0bb88'
from alembic import op
import sqlalchemy as sa
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('providers', sa.Column('default_role', sa.String(length=128), nullable=True))
op.create_index(op.f('ix_providers_default_role'), 'providers', ['default_role'], unique=False)
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_providers_default_role'), table_name='providers')
op.drop_column('providers', 'default_role')
### end Alembic commands ###

View File

@ -0,0 +1,26 @@
"""empty message
Revision ID: 56c33e417a5c
Revises: ede7ee3212ad
Create Date: 2016-08-28 15:55:11.835537
"""
# revision identifiers, used by Alembic.
revision = '56c33e417a5c'
down_revision = 'ede7ee3212ad'
from alembic import op
import sqlalchemy as sa
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('providers', sa.Column('x_session_token', sa.Text(length=255), nullable=True))
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_column('providers', 'x_session_token')
### end Alembic commands ###

View File

@ -0,0 +1,30 @@
"""empty message
Revision ID: 73982db0bb88
Revises: ad4bacc19155
Create Date: 2016-08-28 21:51:42.194149
"""
# revision identifiers, used by Alembic.
revision = '73982db0bb88'
down_revision = 'ad4bacc19155'
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('providers', sa.Column('region', sa.String(length=128), nullable=True))
op.create_index(op.f('ix_providers_region'), 'providers', ['region'], unique=False)
op.drop_column('providers', 'x_subject_token')
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('providers', sa.Column('x_subject_token', mysql.TEXT(collation=u'utf8_bin'), nullable=True))
op.drop_index(op.f('ix_providers_region'), table_name='providers')
op.drop_column('providers', 'region')
### end Alembic commands ###

View File

@ -1,13 +1,13 @@
"""empty message
Revision ID: 070b614d54f1
Revision ID: 9f9f6374b616
Revises: None
Create Date: 2016-08-07 18:01:50.124123
Create Date: 2016-08-25 22:48:06.893609
"""
# revision identifiers, used by Alembic.
revision = '070b614d54f1'
revision = '9f9f6374b616'
down_revision = None
from alembic import op
@ -16,11 +16,29 @@ import sqlalchemy as sa
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.create_table('providers',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('provider', sa.String(length=128), nullable=True),
sa.Column('name', sa.String(length=128), nullable=True),
sa.Column('tenant_name', sa.String(length=128), nullable=True),
sa.Column('username', sa.String(length=128), nullable=True),
sa.Column('api_version', sa.String(length=64), nullable=True),
sa.Column('password', sa.Text(length=256), nullable=True),
sa.Column('url', sa.Text(length=512), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('enabled', sa.Boolean(), default=True, nullable=False),
sa.Column('validated', sa.Boolean(), default=False, nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_providers_api_version'), 'providers', ['api_version'], unique=False)
op.create_index(op.f('ix_providers_name'), 'providers', ['name'], unique=True)
op.create_index(op.f('ix_providers_provider'), 'providers', ['provider'], unique=False)
op.create_index(op.f('ix_providers_tenant_name'), 'providers', ['tenant_name'], unique=False)
op.create_index(op.f('ix_providers_username'), 'providers', ['username'], unique=False)
op.create_table('roles',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=128), nullable=True),
sa.Column('default', sa.Boolean(), nullable=True),
sa.Column('permissions', sa.Integer(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name')
)
@ -35,6 +53,7 @@ def upgrade():
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('role_id', sa.Integer(), nullable=True),
sa.Column('confirmed', sa.Boolean(), nullable=True),
sa.Column('suspended', sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(['role_id'], ['roles.id'], ),
sa.PrimaryKeyConstraint('id')
)
@ -54,4 +73,10 @@ def downgrade():
op.drop_table('users')
op.drop_index(op.f('ix_roles_default'), table_name='roles')
op.drop_table('roles')
op.drop_index(op.f('ix_providers_username'), table_name='providers')
op.drop_index(op.f('ix_providers_tenant_name'), table_name='providers')
op.drop_index(op.f('ix_providers_provider'), table_name='providers')
op.drop_index(op.f('ix_providers_name'), table_name='providers')
op.drop_index(op.f('ix_providers_api_version'), table_name='providers')
op.drop_table('providers')
### end Alembic commands ###

View File

@ -0,0 +1,28 @@
"""empty message
Revision ID: ad4bacc19155
Revises: 56c33e417a5c
Create Date: 2016-08-28 16:08:23.350311
"""
# revision identifiers, used by Alembic.
revision = 'ad4bacc19155'
down_revision = '56c33e417a5c'
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('providers', sa.Column('x_subject_token', sa.Text(length=255), nullable=True))
op.drop_column('providers', 'x_session_token')
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('providers', sa.Column('x_session_token', mysql.TEXT(collation=u'utf8_bin'), nullable=True))
op.drop_column('providers', 'x_subject_token')
### end Alembic commands ###

View File

@ -1,26 +0,0 @@
"""empty message
Revision ID: ad5c9cae2c6d
Revises: 070b614d54f1
Create Date: 2016-08-07 18:16:57.701675
"""
# revision identifiers, used by Alembic.
revision = 'ad5c9cae2c6d'
down_revision = '070b614d54f1'
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_column('roles', 'permissions')
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('roles', sa.Column('permissions', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True))
### end Alembic commands ###

View File

@ -0,0 +1,32 @@
"""empty message
Revision ID: ede7ee3212ad
Revises: 2b52e8261507
Create Date: 2016-08-27 17:38:36.769787
"""
# revision identifiers, used by Alembic.
revision = 'ede7ee3212ad'
down_revision = '2b52e8261507'
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('providers', sa.Column('project_name', sa.String(length=128), nullable=True))
op.create_index(op.f('ix_providers_project_name'), 'providers', ['project_name'], unique=False)
op.drop_index('ix_providers_tenant_name', table_name='providers')
op.drop_column('providers', 'tenant_name')
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('providers', sa.Column('tenant_name', mysql.VARCHAR(collation=u'utf8_bin', length=128), nullable=True))
op.create_index('ix_providers_tenant_name', 'providers', ['tenant_name'], unique=False)
op.drop_index(op.f('ix_providers_project_name'), table_name='providers')
op.drop_column('providers', 'project_name')
### end Alembic commands ###

View File

@ -15,4 +15,5 @@ Werkzeug
alembic
blinker
itsdangerous
flask-principal
flask-principal
requests