Merge pull request #20 from kevgliss/destinations
Adds support for 'Destinations'
This commit is contained in:
commit
14b62a145a
|
@ -15,11 +15,12 @@ from lemur.roles.views import mod as roles_bp
|
||||||
from lemur.auth.views import mod as auth_bp
|
from lemur.auth.views import mod as auth_bp
|
||||||
from lemur.domains.views import mod as domains_bp
|
from lemur.domains.views import mod as domains_bp
|
||||||
from lemur.elbs.views import mod as elbs_bp
|
from lemur.elbs.views import mod as elbs_bp
|
||||||
from lemur.accounts.views import mod as accounts_bp
|
from lemur.destinations.views import mod as destinations_bp
|
||||||
from lemur.authorities.views import mod as authorities_bp
|
from lemur.authorities.views import mod as authorities_bp
|
||||||
from lemur.listeners.views import mod as listeners_bp
|
from lemur.listeners.views import mod as listeners_bp
|
||||||
from lemur.certificates.views import mod as certificates_bp
|
from lemur.certificates.views import mod as certificates_bp
|
||||||
from lemur.status.views import mod as status_bp
|
from lemur.status.views import mod as status_bp
|
||||||
|
from lemur.plugins.views import mod as plugins_bp
|
||||||
|
|
||||||
LEMUR_BLUEPRINTS = (
|
LEMUR_BLUEPRINTS = (
|
||||||
users_bp,
|
users_bp,
|
||||||
|
@ -27,11 +28,12 @@ LEMUR_BLUEPRINTS = (
|
||||||
auth_bp,
|
auth_bp,
|
||||||
domains_bp,
|
domains_bp,
|
||||||
elbs_bp,
|
elbs_bp,
|
||||||
accounts_bp,
|
destinations_bp,
|
||||||
authorities_bp,
|
authorities_bp,
|
||||||
listeners_bp,
|
listeners_bp,
|
||||||
certificates_bp,
|
certificates_bp,
|
||||||
status_bp
|
status_bp,
|
||||||
|
plugins_bp,
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_app(config=None):
|
def create_app(config=None):
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
"""
|
|
||||||
.. module: lemur.accounts.models
|
|
||||||
:platform: unix
|
|
||||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
|
||||||
:license: Apache, see LICENSE for more details.
|
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
|
||||||
"""
|
|
||||||
from sqlalchemy import Column, Integer, String, Text
|
|
||||||
from sqlalchemy.orm import relationship
|
|
||||||
|
|
||||||
from lemur.database import db
|
|
||||||
|
|
||||||
|
|
||||||
class Account(db.Model):
|
|
||||||
__tablename__ = 'accounts'
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
account_number = Column(String(32), unique=True)
|
|
||||||
label = Column(String(32))
|
|
||||||
notes = Column(Text())
|
|
||||||
elbs = relationship("ELB", backref='account', cascade="all, delete, delete-orphan")
|
|
||||||
|
|
||||||
def as_dict(self):
|
|
||||||
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
|
||||||
|
|
||||||
def serialize(self):
|
|
||||||
blob = self.as_dict()
|
|
||||||
blob['elbs'] = [x.id for x in self.elbs]
|
|
||||||
return blob
|
|
||||||
|
|
|
@ -1,112 +0,0 @@
|
||||||
"""
|
|
||||||
.. module: lemur.accounts.views
|
|
||||||
:platform: Unix
|
|
||||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
|
||||||
:license: Apache, see LICENSE for more details.
|
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
|
||||||
"""
|
|
||||||
from lemur import database
|
|
||||||
from lemur.accounts.models import Account
|
|
||||||
from lemur.certificates.models import Certificate
|
|
||||||
|
|
||||||
|
|
||||||
def create(account_number, label=None, comments=None):
|
|
||||||
"""
|
|
||||||
Creates a new account, that can then be used as a destination for certificates.
|
|
||||||
|
|
||||||
:param account_number: AWS assigned ID
|
|
||||||
:param label: Account common name
|
|
||||||
:param comments:
|
|
||||||
:rtype : Account
|
|
||||||
:return: New account
|
|
||||||
"""
|
|
||||||
acct = Account(account_number=account_number, label=label, notes=comments)
|
|
||||||
return database.create(acct)
|
|
||||||
|
|
||||||
|
|
||||||
def update(account_id, account_number, label, comments=None):
|
|
||||||
"""
|
|
||||||
Updates an existing account.
|
|
||||||
|
|
||||||
:param account_id: Lemur assigned ID
|
|
||||||
:param account_number: AWS assigned ID
|
|
||||||
:param label: Account common name
|
|
||||||
:param comments:
|
|
||||||
:rtype : Account
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
account = get(account_id)
|
|
||||||
|
|
||||||
account.account_number = account_number
|
|
||||||
account.label = label
|
|
||||||
account.notes = comments
|
|
||||||
|
|
||||||
return database.update(account)
|
|
||||||
|
|
||||||
|
|
||||||
def delete(account_id):
|
|
||||||
"""
|
|
||||||
Deletes an account.
|
|
||||||
|
|
||||||
:param account_id: Lemur assigned ID
|
|
||||||
"""
|
|
||||||
database.delete(get(account_id))
|
|
||||||
|
|
||||||
|
|
||||||
def get(account_id):
|
|
||||||
"""
|
|
||||||
Retrieves an account by it's lemur assigned ID.
|
|
||||||
|
|
||||||
:param account_id: Lemur assigned ID
|
|
||||||
:rtype : Account
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
return database.get(Account, account_id)
|
|
||||||
|
|
||||||
|
|
||||||
def get_by_account_number(account_number):
|
|
||||||
"""
|
|
||||||
Retrieves an account by it's amazon assigned ID.
|
|
||||||
|
|
||||||
:rtype : Account
|
|
||||||
:param account_number: AWS assigned ID
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
return database.get(Account, account_number, field='account_number')
|
|
||||||
|
|
||||||
|
|
||||||
def get_all():
|
|
||||||
"""
|
|
||||||
Retrieves all account currently known by Lemur.
|
|
||||||
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
query = database.session_query(Account)
|
|
||||||
return database.find_all(query, Account, {}).all()
|
|
||||||
|
|
||||||
|
|
||||||
def render(args):
|
|
||||||
sort_by = args.pop('sort_by')
|
|
||||||
sort_dir = args.pop('sort_dir')
|
|
||||||
page = args.pop('page')
|
|
||||||
count = args.pop('count')
|
|
||||||
filt = args.pop('filter')
|
|
||||||
certificate_id = args.pop('certificate_id', None)
|
|
||||||
|
|
||||||
if certificate_id:
|
|
||||||
query = database.session_query(Account).join(Certificate, Account.certificate)
|
|
||||||
query = query.filter(Certificate.id == certificate_id)
|
|
||||||
else:
|
|
||||||
query = database.session_query(Account)
|
|
||||||
|
|
||||||
if filt:
|
|
||||||
terms = filt.split(';')
|
|
||||||
query = database.filter(query, Account, terms)
|
|
||||||
|
|
||||||
query = database.find_all(query, Account, args)
|
|
||||||
|
|
||||||
if sort_by and sort_dir:
|
|
||||||
query = database.sort(query, Account, sort_by, sort_dir)
|
|
||||||
|
|
||||||
return database.paginate(query, page, count)
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ def create(kwargs):
|
||||||
kwargs['creator'] = g.current_user.email
|
kwargs['creator'] = g.current_user.email
|
||||||
cert_body, intermediate, issuer_roles = issuer.create_authority(kwargs)
|
cert_body, intermediate, issuer_roles = issuer.create_authority(kwargs)
|
||||||
|
|
||||||
cert = cert_service.save_cert(cert_body, None, intermediate, None)
|
cert = cert_service.save_cert(cert_body, None, intermediate, [])
|
||||||
cert.user = g.current_user
|
cert.user = g.current_user
|
||||||
|
|
||||||
# we create and attach any roles that the issuer gives us
|
# we create and attach any roles that the issuer gives us
|
||||||
|
|
|
@ -22,7 +22,7 @@ from lemur.database import db
|
||||||
from lemur.domains.models import Domain
|
from lemur.domains.models import Domain
|
||||||
|
|
||||||
from lemur.constants import SAN_NAMING_TEMPLATE, DEFAULT_NAMING_TEMPLATE, NONSTANDARD_NAMING_TEMPLATE
|
from lemur.constants import SAN_NAMING_TEMPLATE, DEFAULT_NAMING_TEMPLATE, NONSTANDARD_NAMING_TEMPLATE
|
||||||
from lemur.models import certificate_associations, certificate_account_associations
|
from lemur.models import certificate_associations, certificate_destination_associations
|
||||||
|
|
||||||
|
|
||||||
def create_name(issuer, not_before, not_after, subject, san):
|
def create_name(issuer, not_before, not_after, subject, san):
|
||||||
|
@ -215,7 +215,7 @@ class Certificate(db.Model):
|
||||||
date_created = Column(DateTime, PassiveDefault(func.now()), nullable=False)
|
date_created = Column(DateTime, PassiveDefault(func.now()), nullable=False)
|
||||||
user_id = Column(Integer, ForeignKey('users.id'))
|
user_id = Column(Integer, ForeignKey('users.id'))
|
||||||
authority_id = Column(Integer, ForeignKey('authorities.id'))
|
authority_id = Column(Integer, ForeignKey('authorities.id'))
|
||||||
accounts = relationship("Account", secondary=certificate_account_associations, backref='certificate')
|
accounts = relationship("Destination", secondary=certificate_destination_associations, backref='certificate')
|
||||||
domains = relationship("Domain", secondary=certificate_associations, backref="certificate")
|
domains = relationship("Domain", secondary=certificate_associations, backref="certificate")
|
||||||
elb_listeners = relationship("Listener", lazy='dynamic', backref='certificate')
|
elb_listeners = relationship("Listener", lazy='dynamic', backref='certificate')
|
||||||
|
|
||||||
|
|
|
@ -13,12 +13,10 @@ from sqlalchemy import func, or_
|
||||||
from flask import g, current_app
|
from flask import g, current_app
|
||||||
|
|
||||||
from lemur import database
|
from lemur import database
|
||||||
from lemur.common.services.aws import iam
|
|
||||||
from lemur.plugins.base import plugins
|
from lemur.plugins.base import plugins
|
||||||
from lemur.certificates.models import Certificate
|
from lemur.certificates.models import Certificate
|
||||||
|
|
||||||
from lemur.accounts.models import Account
|
from lemur.destinations.models import Destination
|
||||||
from lemur.accounts import service as account_service
|
|
||||||
from lemur.authorities.models import Authority
|
from lemur.authorities.models import Authority
|
||||||
|
|
||||||
from lemur.roles.models import Role
|
from lemur.roles.models import Role
|
||||||
|
@ -59,28 +57,6 @@ def delete(cert_id):
|
||||||
database.delete(get(cert_id))
|
database.delete(get(cert_id))
|
||||||
|
|
||||||
|
|
||||||
def disassociate_aws_account(certs, account):
|
|
||||||
"""
|
|
||||||
Removes the account association from a certificate. We treat AWS as a completely
|
|
||||||
external service. Certificates are added and removed from this service but a record
|
|
||||||
of that certificate is always kept and tracked by Lemur. This allows us to migrate
|
|
||||||
certificates to different accounts with ease.
|
|
||||||
|
|
||||||
:param certs:
|
|
||||||
:param account:
|
|
||||||
"""
|
|
||||||
account_certs = Certificate.query.filter(Certificate.accounts.any(Account.id == 1)).\
|
|
||||||
filter(~Certificate.body.in_(certs)).all()
|
|
||||||
|
|
||||||
for a_cert in account_certs:
|
|
||||||
try:
|
|
||||||
a_cert.accounts.remove(account)
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.debug("Skipping {0} account {1} is already disassociated".format(a_cert.name, account.label))
|
|
||||||
continue
|
|
||||||
database.update(a_cert)
|
|
||||||
|
|
||||||
|
|
||||||
def get_all_certs():
|
def get_all_certs():
|
||||||
"""
|
"""
|
||||||
Retrieves all certificates within Lemur.
|
Retrieves all certificates within Lemur.
|
||||||
|
@ -134,7 +110,7 @@ def mint(issuer_options):
|
||||||
issuer_options['creator'] = g.user.email
|
issuer_options['creator'] = g.user.email
|
||||||
cert_body, cert_chain = issuer.create_certificate(csr, issuer_options)
|
cert_body, cert_chain = issuer.create_certificate(csr, issuer_options)
|
||||||
|
|
||||||
cert = save_cert(cert_body, private_key, cert_chain, issuer_options.get('accounts'))
|
cert = save_cert(cert_body, private_key, cert_chain, issuer_options.get('destinations'))
|
||||||
cert.user = g.user
|
cert.user = g.user
|
||||||
cert.authority = authority
|
cert.authority = authority
|
||||||
database.update(cert)
|
database.update(cert)
|
||||||
|
@ -154,9 +130,10 @@ def import_certificate(**kwargs):
|
||||||
|
|
||||||
:param kwargs:
|
:param kwargs:
|
||||||
"""
|
"""
|
||||||
|
from lemur.users import service as user_service
|
||||||
cert = Certificate(kwargs['public_certificate'])
|
cert = Certificate(kwargs['public_certificate'])
|
||||||
cert.owner = kwargs.get('owner', )
|
cert.owner = kwargs.get('owner', current_app.config.get('LEMUR_SECURITY_TEAM_EMAIL'))
|
||||||
cert.creator = kwargs.get('creator', 'Lemur')
|
cert.creator = kwargs.get('creator', user_service.get_by_email('lemur@nobody'))
|
||||||
|
|
||||||
# NOTE existing certs may not follow our naming standard we will
|
# NOTE existing certs may not follow our naming standard we will
|
||||||
# overwrite the generated name with the actual cert name
|
# overwrite the generated name with the actual cert name
|
||||||
|
@ -166,31 +143,29 @@ def import_certificate(**kwargs):
|
||||||
if kwargs.get('user'):
|
if kwargs.get('user'):
|
||||||
cert.user = kwargs.get('user')
|
cert.user = kwargs.get('user')
|
||||||
|
|
||||||
if kwargs.get('account'):
|
if kwargs.get('destination'):
|
||||||
cert.accounts.append(kwargs.get('account'))
|
cert.destinations.append(kwargs.get('destination'))
|
||||||
|
|
||||||
cert = database.create(cert)
|
cert = database.create(cert)
|
||||||
return cert
|
return cert
|
||||||
|
|
||||||
|
|
||||||
def save_cert(cert_body, private_key, cert_chain, accounts):
|
def save_cert(cert_body, private_key, cert_chain, destinations):
|
||||||
"""
|
"""
|
||||||
Determines if the certificate needs to be uploaded to AWS or other services.
|
Determines if the certificate needs to be uploaded to AWS or other services.
|
||||||
|
|
||||||
:param cert_body:
|
:param cert_body:
|
||||||
:param private_key:
|
:param private_key:
|
||||||
:param cert_chain:
|
:param cert_chain:
|
||||||
:param challenge:
|
:param destinations:
|
||||||
:param csr_config:
|
|
||||||
:param accounts:
|
|
||||||
"""
|
"""
|
||||||
cert = Certificate(cert_body, private_key, cert_chain)
|
cert = Certificate(cert_body, private_key, cert_chain)
|
||||||
# if we have an AWS accounts lets upload them
|
|
||||||
if accounts:
|
# we should save them to any destination that is requested
|
||||||
for account in accounts:
|
for destination in destinations:
|
||||||
account = account_service.get(account['id'])
|
destination_plugin = plugins.get(destination['plugin']['slug'])
|
||||||
iam.upload_cert(account.account_number, cert, private_key, cert_chain)
|
destination_plugin.upload(cert, private_key, cert_chain, destination['plugin']['pluginOptions'])
|
||||||
cert.accounts.append(account)
|
|
||||||
return cert
|
return cert
|
||||||
|
|
||||||
|
|
||||||
|
@ -198,13 +173,11 @@ def upload(**kwargs):
|
||||||
"""
|
"""
|
||||||
Allows for pre-made certificates to be imported into Lemur.
|
Allows for pre-made certificates to be imported into Lemur.
|
||||||
"""
|
"""
|
||||||
# save this cert the same way we save all of our certs, including uploading
|
|
||||||
# to aws if necessary
|
|
||||||
cert = save_cert(
|
cert = save_cert(
|
||||||
kwargs.get('public_cert'),
|
kwargs.get('public_cert'),
|
||||||
kwargs.get('private_key'),
|
kwargs.get('private_key'),
|
||||||
kwargs.get('intermediate_cert'),
|
kwargs.get('intermediate_cert'),
|
||||||
kwargs.get('accounts')
|
kwargs.get('destinations')
|
||||||
)
|
)
|
||||||
|
|
||||||
cert.owner = kwargs['owner']
|
cert.owner = kwargs['owner']
|
||||||
|
@ -237,7 +210,7 @@ def render(args):
|
||||||
query = database.session_query(Certificate)
|
query = database.session_query(Certificate)
|
||||||
|
|
||||||
time_range = args.pop('time_range')
|
time_range = args.pop('time_range')
|
||||||
account_id = args.pop('account_id')
|
destination_id = args.pop('destination_id')
|
||||||
show = args.pop('show')
|
show = args.pop('show')
|
||||||
owner = args.pop('owner')
|
owner = args.pop('owner')
|
||||||
creator = args.pop('creator') # TODO we should enabling filtering by owner
|
creator = args.pop('creator') # TODO we should enabling filtering by owner
|
||||||
|
@ -260,8 +233,8 @@ def render(args):
|
||||||
)
|
)
|
||||||
return database.sort_and_page(query, Certificate, args)
|
return database.sort_and_page(query, Certificate, args)
|
||||||
|
|
||||||
if 'account' in terms:
|
if 'destination' in terms:
|
||||||
query = query.filter(Certificate.accounts.any(Account.id == terms[1]))
|
query = query.filter(Certificate.destinations.any(Destination.id == terms[1]))
|
||||||
elif 'active' in filt: # this is really weird but strcmp seems to not work here??
|
elif 'active' in filt: # this is really weird but strcmp seems to not work here??
|
||||||
query = query.filter(Certificate.active == terms[1])
|
query = query.filter(Certificate.active == terms[1])
|
||||||
else:
|
else:
|
||||||
|
@ -276,8 +249,8 @@ def render(args):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if account_id:
|
if destination_id:
|
||||||
query = query.filter(Certificate.accounts.any(Account.id == account_id))
|
query = query.filter(Certificate.destinations.any(Destination.id == destination_id))
|
||||||
|
|
||||||
if time_range:
|
if time_range:
|
||||||
to = arrow.now().replace(weeks=+time_range).format('YYYY-MM-DD')
|
to = arrow.now().replace(weeks=+time_range).format('YYYY-MM-DD')
|
||||||
|
@ -404,8 +377,8 @@ def stats(**kwargs):
|
||||||
if kwargs.get('active') == 'true':
|
if kwargs.get('active') == 'true':
|
||||||
query = query.filter(Certificate.elb_listeners.any())
|
query = query.filter(Certificate.elb_listeners.any())
|
||||||
|
|
||||||
if kwargs.get('account_id'):
|
if kwargs.get('destination_id'):
|
||||||
query = query.filter(Certificate.accounts.any(Account.id == kwargs.get('account_id')))
|
query = query.filter(Certificate.destinations.any(Destination.id == kwargs.get('destination_id')))
|
||||||
|
|
||||||
if kwargs.get('metric') == 'not_after':
|
if kwargs.get('metric') == 'not_after':
|
||||||
start = arrow.utcnow()
|
start = arrow.utcnow()
|
||||||
|
|
|
@ -7,10 +7,6 @@
|
||||||
to 'sync' with as many different datasources as possible to try and track
|
to 'sync' with as many different datasources as possible to try and track
|
||||||
any certificate that may be in use.
|
any certificate that may be in use.
|
||||||
|
|
||||||
This include querying AWS for certificates attached to ELBs, querying our own
|
|
||||||
internal CA for certificates issued. As well as some rudimentary source code
|
|
||||||
scraping that attempts to find certificates checked into source code.
|
|
||||||
|
|
||||||
These operations are typically run on a periodic basis from either the command
|
These operations are typically run on a periodic basis from either the command
|
||||||
line or a cron job.
|
line or a cron job.
|
||||||
|
|
||||||
|
@ -18,151 +14,33 @@
|
||||||
:license: Apache, see LICENSE for more details.
|
:license: Apache, see LICENSE for more details.
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
"""
|
"""
|
||||||
import requests
|
|
||||||
from bs4 import BeautifulSoup
|
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from lemur.users import service as user_service
|
|
||||||
from lemur.accounts import service as account_service
|
|
||||||
from lemur.certificates import service as cert_service
|
from lemur.certificates import service as cert_service
|
||||||
from lemur.certificates.models import Certificate, get_name_from_arn
|
|
||||||
from lemur.common.services.aws.iam import get_all_server_certs
|
|
||||||
from lemur.common.services.aws.iam import get_cert_from_arn
|
|
||||||
|
|
||||||
from lemur.plugins.base import plugins
|
from lemur.plugins.base import plugins
|
||||||
|
from lemur.plugins.bases.source import SourcePlugin
|
||||||
|
|
||||||
def aws():
|
def sync():
|
||||||
"""
|
for plugin in plugins:
|
||||||
Attempts to retrieve all certificates located in known AWS accounts
|
new = 0
|
||||||
:raise e:
|
updated = 0
|
||||||
"""
|
if isinstance(plugin, SourcePlugin):
|
||||||
new = 0
|
if plugin.is_enabled():
|
||||||
updated = 0
|
current_app.logger.error("Retrieving certificates from {0}".format(plugin.title))
|
||||||
|
certificates = plugin.get_certificates()
|
||||||
|
|
||||||
# all certificates 'discovered' by lemur are tracked by the lemur
|
for certificate in certificates:
|
||||||
# user
|
exists = cert_service.find_duplicates(certificate)
|
||||||
user = user_service.get_by_email('lemur@nobody')
|
|
||||||
|
|
||||||
# we don't need to check regions as IAM is a global service
|
if not exists:
|
||||||
for account in account_service.get_all():
|
cert_service.import_certificate(**certificate)
|
||||||
certificate_bodies = []
|
new += 1
|
||||||
try:
|
|
||||||
cert_arns = get_all_server_certs(account.account_number)
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error("Failed to to get Certificates from '{}/{}' reason {}".format(
|
|
||||||
account.label, account.account_number, e.message)
|
|
||||||
)
|
|
||||||
raise e
|
|
||||||
|
|
||||||
current_app.logger.info("found {} certs from '{}/{}' ... ".format(
|
if len(exists) == 1:
|
||||||
len(cert_arns), account.account_number, account.label)
|
updated += 1
|
||||||
)
|
|
||||||
|
|
||||||
for cert in cert_arns:
|
# TODO associated cert with source
|
||||||
cert_body = get_cert_from_arn(cert.arn)[0]
|
# TODO update cert if found from different source
|
||||||
certificate_bodies.append(cert_body)
|
# TODO dissassociate source if missing
|
||||||
existing = cert_service.find_duplicates(cert_body)
|
|
||||||
|
|
||||||
if not existing:
|
|
||||||
cert_service.import_certificate(
|
|
||||||
**{'owner': 'secops@netflix.com',
|
|
||||||
'creator': 'Lemur',
|
|
||||||
'name': get_name_from_arn(cert.arn),
|
|
||||||
'account': account,
|
|
||||||
'user': user,
|
|
||||||
'public_certificate': cert_body
|
|
||||||
}
|
|
||||||
)
|
|
||||||
new += 1
|
|
||||||
|
|
||||||
elif len(existing) == 1: # we check to make sure we know about the current account for this certificate
|
|
||||||
for e_account in existing[0].accounts:
|
|
||||||
if e_account.account_number == account.account_number:
|
|
||||||
break
|
|
||||||
else: # we have a new account
|
|
||||||
existing[0].accounts.append(account)
|
|
||||||
updated += 1
|
|
||||||
|
|
||||||
else:
|
|
||||||
current_app.logger.error(
|
|
||||||
"Multiple certificates with the same body found, unable to correctly determine which entry to update"
|
|
||||||
)
|
|
||||||
|
|
||||||
# make sure we remove any certs that have been removed from AWS
|
|
||||||
cert_service.disassociate_aws_account(certificate_bodies, account)
|
|
||||||
current_app.logger.info("found {} new certificates in aws {}".format(new, account.label))
|
|
||||||
|
|
||||||
|
|
||||||
def cloudca():
|
|
||||||
"""
|
|
||||||
Attempts to retrieve all certificates that are stored in CloudCA
|
|
||||||
"""
|
|
||||||
user = user_service.get_by_email('lemur@nobody')
|
|
||||||
# sync all new certificates/authorities not created through lemur
|
|
||||||
issuer = plugins.get('cloudca')
|
|
||||||
authorities = issuer.get_authorities()
|
|
||||||
total = 0
|
|
||||||
new = 1
|
|
||||||
for authority in authorities:
|
|
||||||
certs = issuer.get_cert(ca_name=authority)
|
|
||||||
for cert in certs:
|
|
||||||
total += 1
|
|
||||||
cert['user'] = user
|
|
||||||
existing = cert_service.find_duplicates(cert['public_certificate'])
|
|
||||||
if not existing:
|
|
||||||
new += 1
|
|
||||||
try:
|
|
||||||
cert_service.import_certificate(**cert)
|
|
||||||
except NameError as e:
|
|
||||||
current_app.logger.error("Cannot import certificate {0}".format(cert))
|
|
||||||
|
|
||||||
current_app.logger.debug("Found {0} total certificates in cloudca".format(total))
|
|
||||||
current_app.logger.debug("Found {0} new certificates in cloudca".format(new))
|
|
||||||
|
|
||||||
|
|
||||||
def source():
|
|
||||||
"""
|
|
||||||
Attempts to track certificates that are stored in Source Code
|
|
||||||
"""
|
|
||||||
new = 0
|
|
||||||
keywords = ['"--- Begin Certificate ---"']
|
|
||||||
endpoint = current_app.config.get('LEMUR_SOURCE_SEARCH')
|
|
||||||
maxresults = 25000
|
|
||||||
|
|
||||||
current_app.logger.info("Searching {0} for new certificates".format(endpoint))
|
|
||||||
|
|
||||||
for keyword in keywords:
|
|
||||||
current_app.logger.info("Looking for keyword: {0}".format(keyword))
|
|
||||||
url = "{}/source/s?n={}&start=1&sort=relevancy&q={}&project=github%2Cperforce%2Cstash".format(endpoint, maxresults, keyword)
|
|
||||||
|
|
||||||
current_app.logger.debug("Request url: {0}".format(url))
|
|
||||||
r = requests.get(url, timeout=20)
|
|
||||||
|
|
||||||
if r.status_code != 200:
|
|
||||||
current_app.logger.error("Unable to retrieve: {0} Status Code: {1}".format(url, r.status_code))
|
|
||||||
continue
|
|
||||||
|
|
||||||
soup = BeautifulSoup(r.text, "lxml")
|
|
||||||
results = soup.find_all(title='Download')
|
|
||||||
for result in results:
|
|
||||||
parts = result['href'].split('/')
|
|
||||||
path = "/".join(parts[:-1])
|
|
||||||
filename = parts[-1:][0]
|
|
||||||
r = requests.get("{0}{1}/{2}".format(endpoint, path, filename))
|
|
||||||
|
|
||||||
if r.status_code != 200:
|
|
||||||
current_app.logger.error("Unable to retrieve: {0} Status Code: {1}".format(url, r.status_code))
|
|
||||||
continue
|
|
||||||
|
|
||||||
try:
|
|
||||||
# validate we have a real certificate
|
|
||||||
cert = Certificate(r.content)
|
|
||||||
# do a lookup to see if we know about this certificate
|
|
||||||
existing = cert_service.find_duplicates(r.content)
|
|
||||||
if not existing:
|
|
||||||
current_app.logger.debug(cert.name)
|
|
||||||
cert_service.import_certificate()
|
|
||||||
new += 1
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.debug("Could not parse the following 'certificate': {0} Reason: {1}".format(r.content, e))
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ class CertificatesList(AuthenticatedResource):
|
||||||
parser.add_argument('owner', type=bool, location='args')
|
parser.add_argument('owner', type=bool, location='args')
|
||||||
parser.add_argument('id', type=str, location='args')
|
parser.add_argument('id', type=str, location='args')
|
||||||
parser.add_argument('active', type=bool, location='args')
|
parser.add_argument('active', type=bool, location='args')
|
||||||
parser.add_argument('accountId', type=int, dest="account_id", location='args')
|
parser.add_argument('destinationId', type=int, dest="destination_id", location='args')
|
||||||
parser.add_argument('creator', type=str, location='args')
|
parser.add_argument('creator', type=str, location='args')
|
||||||
parser.add_argument('show', type=str, location='args')
|
parser.add_argument('show', type=str, location='args')
|
||||||
|
|
||||||
|
@ -271,7 +271,7 @@ class CertificatesList(AuthenticatedResource):
|
||||||
:statuscode 403: unauthenticated
|
:statuscode 403: unauthenticated
|
||||||
"""
|
"""
|
||||||
self.reqparse.add_argument('extensions', type=dict, location='json')
|
self.reqparse.add_argument('extensions', type=dict, location='json')
|
||||||
self.reqparse.add_argument('accounts', type=list, location='json')
|
self.reqparse.add_argument('destinations', type=list, default=[], location='json')
|
||||||
self.reqparse.add_argument('elbs', type=list, location='json')
|
self.reqparse.add_argument('elbs', type=list, location='json')
|
||||||
self.reqparse.add_argument('owner', type=str, location='json')
|
self.reqparse.add_argument('owner', type=str, location='json')
|
||||||
self.reqparse.add_argument('validityStart', type=str, location='json') # parse date
|
self.reqparse.add_argument('validityStart', type=str, location='json') # parse date
|
||||||
|
@ -330,7 +330,7 @@ class CertificatesUpload(AuthenticatedResource):
|
||||||
"publicCert": "---Begin Public...",
|
"publicCert": "---Begin Public...",
|
||||||
"intermediateCert": "---Begin Public...",
|
"intermediateCert": "---Begin Public...",
|
||||||
"privateKey": "---Begin Private..."
|
"privateKey": "---Begin Private..."
|
||||||
"accounts": []
|
"destinations": []
|
||||||
}
|
}
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
@ -364,19 +364,19 @@ class CertificatesUpload(AuthenticatedResource):
|
||||||
:arg publicCert: valid PEM public key for certificate
|
:arg publicCert: valid PEM public key for certificate
|
||||||
:arg intermediateCert valid PEM intermediate key for certificate
|
:arg intermediateCert valid PEM intermediate key for certificate
|
||||||
:arg privateKey: valid PEM private key for certificate
|
:arg privateKey: valid PEM private key for certificate
|
||||||
:arg accounts: list of aws accounts to upload the certificate to
|
:arg destinations: list of aws destinations to upload the certificate to
|
||||||
:reqheader Authorization: OAuth token to authenticate
|
:reqheader Authorization: OAuth token to authenticate
|
||||||
:statuscode 403: unauthenticated
|
:statuscode 403: unauthenticated
|
||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
"""
|
"""
|
||||||
self.reqparse.add_argument('owner', type=str, required=True, location='json')
|
self.reqparse.add_argument('owner', type=str, required=True, location='json')
|
||||||
self.reqparse.add_argument('publicCert', type=pem_str, required=True, dest='public_cert', location='json')
|
self.reqparse.add_argument('publicCert', type=pem_str, required=True, dest='public_cert', location='json')
|
||||||
self.reqparse.add_argument('accounts', type=list, dest='accounts', location='json')
|
self.reqparse.add_argument('destinations', type=list, default=[], dest='destinations', location='json')
|
||||||
self.reqparse.add_argument('intermediateCert', type=pem_str, dest='intermediate_cert', location='json')
|
self.reqparse.add_argument('intermediateCert', type=pem_str, dest='intermediate_cert', location='json')
|
||||||
self.reqparse.add_argument('privateKey', type=private_key_str, dest='private_key', location='json')
|
self.reqparse.add_argument('privateKey', type=private_key_str, dest='private_key', location='json')
|
||||||
|
|
||||||
args = self.reqparse.parse_args()
|
args = self.reqparse.parse_args()
|
||||||
if args.get('accounts'):
|
if args.get('destinations'):
|
||||||
if args.get('private_key'):
|
if args.get('private_key'):
|
||||||
return service.upload(**args)
|
return service.upload(**args)
|
||||||
else:
|
else:
|
||||||
|
@ -393,7 +393,7 @@ class CertificatesStats(AuthenticatedResource):
|
||||||
def get(self):
|
def get(self):
|
||||||
self.reqparse.add_argument('metric', type=str, location='args')
|
self.reqparse.add_argument('metric', type=str, location='args')
|
||||||
self.reqparse.add_argument('range', default=32, type=int, location='args')
|
self.reqparse.add_argument('range', default=32, type=int, location='args')
|
||||||
self.reqparse.add_argument('accountId', dest='account_id', location='args')
|
self.reqparse.add_argument('destinationId', dest='destination_id', location='args')
|
||||||
self.reqparse.add_argument('active', type=str, default='true', location='args')
|
self.reqparse.add_argument('active', type=str, default='true', location='args')
|
||||||
|
|
||||||
args = self.reqparse.parse_args()
|
args = self.reqparse.parse_args()
|
||||||
|
|
|
@ -41,7 +41,7 @@ class marshal_items(object):
|
||||||
return {'items': _filter_items(resp.items), 'total': resp.total}
|
return {'items': _filter_items(resp.items), 'total': resp.total}
|
||||||
|
|
||||||
if isinstance(resp, list):
|
if isinstance(resp, list):
|
||||||
return _filter_items(resp)
|
return {'items': _filter_items(resp), 'total': len(resp)}
|
||||||
|
|
||||||
return marshal(resp, self.fields)
|
return marshal(resp, self.fields)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.destinations.models
|
||||||
|
:platform: unix
|
||||||
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
|
"""
|
||||||
|
import copy
|
||||||
|
from sqlalchemy import Column, Integer, String, Text
|
||||||
|
from sqlalchemy_utils import JSONType
|
||||||
|
from lemur.database import db
|
||||||
|
|
||||||
|
from lemur.plugins.base import plugins
|
||||||
|
|
||||||
|
class Destination(db.Model):
|
||||||
|
__tablename__ = 'destinations'
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
label = Column(String(32))
|
||||||
|
options = Column(JSONType)
|
||||||
|
description = Column(Text())
|
||||||
|
plugin_name = Column(String(32))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def plugin(self):
|
||||||
|
p = plugins.get(self.plugin_name)
|
||||||
|
c = copy.deepcopy(p)
|
||||||
|
c.options = self.options
|
||||||
|
return c
|
|
@ -0,0 +1,110 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.destinations.service
|
||||||
|
:platform: Unix
|
||||||
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
|
"""
|
||||||
|
from lemur import database
|
||||||
|
from lemur.destinations.models import Destination
|
||||||
|
from lemur.certificates.models import Certificate
|
||||||
|
|
||||||
|
|
||||||
|
def create(label, plugin_name, options, description=None):
|
||||||
|
"""
|
||||||
|
Creates a new destination, that can then be used as a destination for certificates.
|
||||||
|
|
||||||
|
:param label: Destination common name
|
||||||
|
:param description:
|
||||||
|
:rtype : Destination
|
||||||
|
:return: New destination
|
||||||
|
"""
|
||||||
|
destination = Destination(label=label, options=options, plugin_name=plugin_name, description=description)
|
||||||
|
return database.create(destination)
|
||||||
|
|
||||||
|
|
||||||
|
def update(destination_id, label, options, description):
|
||||||
|
"""
|
||||||
|
Updates an existing destination.
|
||||||
|
|
||||||
|
:param destination_id: Lemur assigned ID
|
||||||
|
:param destination_number: AWS assigned ID
|
||||||
|
:param label: Destination common name
|
||||||
|
:param comments:
|
||||||
|
:rtype : Destination
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
destination = get(destination_id)
|
||||||
|
|
||||||
|
destination.label = label
|
||||||
|
description.options = options
|
||||||
|
destination.description = description
|
||||||
|
|
||||||
|
return database.update(destination)
|
||||||
|
|
||||||
|
|
||||||
|
def delete(destination_id):
|
||||||
|
"""
|
||||||
|
Deletes an destination.
|
||||||
|
|
||||||
|
:param destination_id: Lemur assigned ID
|
||||||
|
"""
|
||||||
|
database.delete(get(destination_id))
|
||||||
|
|
||||||
|
|
||||||
|
def get(destination_id):
|
||||||
|
"""
|
||||||
|
Retrieves an destination by it's lemur assigned ID.
|
||||||
|
|
||||||
|
:param destination_id: Lemur assigned ID
|
||||||
|
:rtype : Destination
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return database.get(Destination, destination_id)
|
||||||
|
|
||||||
|
|
||||||
|
def get_by_label(label):
|
||||||
|
"""
|
||||||
|
Retrieves a destination by it's label
|
||||||
|
|
||||||
|
:param label:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return database.get(Destination, label, field='label')
|
||||||
|
|
||||||
|
|
||||||
|
def get_all():
|
||||||
|
"""
|
||||||
|
Retrieves all destination currently known by Lemur.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
query = database.session_query(Destination)
|
||||||
|
return database.find_all(query, Destination, {}).all()
|
||||||
|
|
||||||
|
|
||||||
|
def render(args):
|
||||||
|
sort_by = args.pop('sort_by')
|
||||||
|
sort_dir = args.pop('sort_dir')
|
||||||
|
page = args.pop('page')
|
||||||
|
count = args.pop('count')
|
||||||
|
filt = args.pop('filter')
|
||||||
|
certificate_id = args.pop('certificate_id', None)
|
||||||
|
|
||||||
|
if certificate_id:
|
||||||
|
query = database.session_query(Destination).join(Certificate, Destination.certificate)
|
||||||
|
query = query.filter(Certificate.id == certificate_id)
|
||||||
|
else:
|
||||||
|
query = database.session_query(Destination)
|
||||||
|
|
||||||
|
if filt:
|
||||||
|
terms = filt.split(';')
|
||||||
|
query = database.filter(query, Destination, terms)
|
||||||
|
|
||||||
|
query = database.find_all(query, Destination, args)
|
||||||
|
|
||||||
|
if sort_by and sort_dir:
|
||||||
|
query = database.sort(query, Destination, sort_by, sort_dir)
|
||||||
|
|
||||||
|
return database.paginate(query, page, count)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
.. module: lemur.accounts.views
|
.. module: lemur.destinations.views
|
||||||
:platform: Unix
|
:platform: Unix
|
||||||
:synopsis: This module contains all of the accounts view code.
|
:synopsis: This module contains all of the accounts view code.
|
||||||
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
@ -8,35 +8,36 @@
|
||||||
"""
|
"""
|
||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
from flask.ext.restful import Api, reqparse, fields
|
from flask.ext.restful import Api, reqparse, fields
|
||||||
from lemur.accounts import service
|
from lemur.destinations import service
|
||||||
|
|
||||||
from lemur.auth.service import AuthenticatedResource
|
from lemur.auth.service import AuthenticatedResource
|
||||||
from lemur.auth.permissions import admin_permission
|
from lemur.auth.permissions import admin_permission
|
||||||
from lemur.common.utils import paginated_parser, marshal_items
|
from lemur.common.utils import paginated_parser, marshal_items
|
||||||
|
|
||||||
|
from lemur.plugins.views import FIELDS as PLUGIN_FIELDS
|
||||||
|
|
||||||
mod = Blueprint('accounts', __name__)
|
mod = Blueprint('destinations', __name__)
|
||||||
api = Api(mod)
|
api = Api(mod)
|
||||||
|
|
||||||
|
|
||||||
FIELDS = {
|
FIELDS = {
|
||||||
'accountNumber': fields.Integer(attribute='account_number'),
|
'description': fields.String,
|
||||||
|
'plugin': fields.Nested(PLUGIN_FIELDS, attribute='plugin'),
|
||||||
'label': fields.String,
|
'label': fields.String,
|
||||||
'comments': fields.String(attribute='notes'),
|
|
||||||
'id': fields.Integer,
|
'id': fields.Integer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class AccountsList(AuthenticatedResource):
|
class DestinationsList(AuthenticatedResource):
|
||||||
""" Defines the 'accounts' endpoint """
|
""" Defines the 'destinations' endpoint """
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.reqparse = reqparse.RequestParser()
|
self.reqparse = reqparse.RequestParser()
|
||||||
super(AccountsList, self).__init__()
|
super(DestinationsList, self).__init__()
|
||||||
|
|
||||||
@marshal_items(FIELDS)
|
@marshal_items(FIELDS)
|
||||||
def get(self):
|
def get(self):
|
||||||
"""
|
"""
|
||||||
.. http:get:: /accounts
|
.. http:get:: /destinations
|
||||||
|
|
||||||
The current account list
|
The current account list
|
||||||
|
|
||||||
|
@ -44,7 +45,7 @@ class AccountsList(AuthenticatedResource):
|
||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /accounts HTTP/1.1
|
GET /destinations HTTP/1.1
|
||||||
Host: example.com
|
Host: example.com
|
||||||
Accept: application/json, text/javascript
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
@ -90,7 +91,7 @@ class AccountsList(AuthenticatedResource):
|
||||||
@marshal_items(FIELDS)
|
@marshal_items(FIELDS)
|
||||||
def post(self):
|
def post(self):
|
||||||
"""
|
"""
|
||||||
.. http:post:: /accounts
|
.. http:post:: /destinations
|
||||||
|
|
||||||
Creates a new account
|
Creates a new account
|
||||||
|
|
||||||
|
@ -98,7 +99,7 @@ class AccountsList(AuthenticatedResource):
|
||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
POST /accounts HTTP/1.1
|
POST /destinations HTTP/1.1
|
||||||
Host: example.com
|
Host: example.com
|
||||||
Accept: application/json, text/javascript
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
@ -129,23 +130,23 @@ class AccountsList(AuthenticatedResource):
|
||||||
:reqheader Authorization: OAuth token to authenticate
|
:reqheader Authorization: OAuth token to authenticate
|
||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
"""
|
"""
|
||||||
self.reqparse.add_argument('accountNumber', type=int, dest="account_number", location='json', required=True)
|
|
||||||
self.reqparse.add_argument('label', type=str, location='json', required=True)
|
self.reqparse.add_argument('label', type=str, location='json', required=True)
|
||||||
self.reqparse.add_argument('comments', type=str, location='json')
|
self.reqparse.add_argument('plugin', type=dict, location='json', required=True)
|
||||||
|
self.reqparse.add_argument('description', type=str, location='json')
|
||||||
|
|
||||||
args = self.reqparse.parse_args()
|
args = self.reqparse.parse_args()
|
||||||
return service.create(args['account_number'], args['label'], args['comments'])
|
return service.create(args['label'], args['plugin']['slug'], args['plugin']['pluginOptions'], args['description'])
|
||||||
|
|
||||||
|
|
||||||
class Accounts(AuthenticatedResource):
|
class Destinations(AuthenticatedResource):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.reqparse = reqparse.RequestParser()
|
self.reqparse = reqparse.RequestParser()
|
||||||
super(Accounts, self).__init__()
|
super(Destinations, self).__init__()
|
||||||
|
|
||||||
@marshal_items(FIELDS)
|
@marshal_items(FIELDS)
|
||||||
def get(self, account_id):
|
def get(self, destination_id):
|
||||||
"""
|
"""
|
||||||
.. http:get:: /accounts/1
|
.. http:get:: /destinations/1
|
||||||
|
|
||||||
Get a specific account
|
Get a specific account
|
||||||
|
|
||||||
|
@ -153,7 +154,7 @@ class Accounts(AuthenticatedResource):
|
||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /accounts/1 HTTP/1.1
|
GET /destinations/1 HTTP/1.1
|
||||||
Host: example.com
|
Host: example.com
|
||||||
Accept: application/json, text/javascript
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
@ -175,13 +176,13 @@ class Accounts(AuthenticatedResource):
|
||||||
:reqheader Authorization: OAuth token to authenticate
|
:reqheader Authorization: OAuth token to authenticate
|
||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
"""
|
"""
|
||||||
return service.get(account_id)
|
return service.get(destination_id)
|
||||||
|
|
||||||
@admin_permission.require(http_exception=403)
|
@admin_permission.require(http_exception=403)
|
||||||
@marshal_items(FIELDS)
|
@marshal_items(FIELDS)
|
||||||
def put(self, account_id):
|
def put(self, destination_id):
|
||||||
"""
|
"""
|
||||||
.. http:put:: /accounts/1
|
.. http:put:: /destinations/1
|
||||||
|
|
||||||
Updates an account
|
Updates an account
|
||||||
|
|
||||||
|
@ -189,15 +190,10 @@ class Accounts(AuthenticatedResource):
|
||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
POST /accounts/1 HTTP/1.1
|
POST /destinations/1 HTTP/1.1
|
||||||
Host: example.com
|
Host: example.com
|
||||||
Accept: application/json, text/javascript
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
{
|
|
||||||
"accountNumber": 11111111111,
|
|
||||||
"label": "labelChanged,
|
|
||||||
"comments": "this is a thing"
|
|
||||||
}
|
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|
||||||
|
@ -220,29 +216,29 @@ class Accounts(AuthenticatedResource):
|
||||||
:reqheader Authorization: OAuth token to authenticate
|
:reqheader Authorization: OAuth token to authenticate
|
||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
"""
|
"""
|
||||||
self.reqparse.add_argument('accountNumber', type=int, dest="account_number", location='json', required=True)
|
|
||||||
self.reqparse.add_argument('label', type=str, location='json', required=True)
|
self.reqparse.add_argument('label', type=str, location='json', required=True)
|
||||||
self.reqparse.add_argument('comments', type=str, location='json')
|
self.reqparse.add_argument('pluginOptions', type=dict, location='json', required=True)
|
||||||
|
self.reqparse.add_argument('description', type=str, location='json')
|
||||||
|
|
||||||
args = self.reqparse.parse_args()
|
args = self.reqparse.parse_args()
|
||||||
return service.update(account_id, args['account_number'], args['label'], args['comments'])
|
return service.update(destination_id, args['label'], args['options'], args['description'])
|
||||||
|
|
||||||
@admin_permission.require(http_exception=403)
|
@admin_permission.require(http_exception=403)
|
||||||
def delete(self, account_id):
|
def delete(self, destination_id):
|
||||||
service.delete(account_id)
|
service.delete(destination_id)
|
||||||
return {'result': True}
|
return {'result': True}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CertificateAccounts(AuthenticatedResource):
|
class CertificateDestinations(AuthenticatedResource):
|
||||||
""" Defines the 'certificate/<int:certificate_id/accounts'' endpoint """
|
""" Defines the 'certificate/<int:certificate_id/destinations'' endpoint """
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(CertificateAccounts, self).__init__()
|
super(CertificateDestinations, self).__init__()
|
||||||
|
|
||||||
@marshal_items(FIELDS)
|
@marshal_items(FIELDS)
|
||||||
def get(self, certificate_id):
|
def get(self, certificate_id):
|
||||||
"""
|
"""
|
||||||
.. http:get:: /certificates/1/accounts
|
.. http:get:: /certificates/1/destinations
|
||||||
|
|
||||||
The current account list for a given certificates
|
The current account list for a given certificates
|
||||||
|
|
||||||
|
@ -250,7 +246,7 @@ class CertificateAccounts(AuthenticatedResource):
|
||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /certificates/1/accounts HTTP/1.1
|
GET /certificates/1/destinations HTTP/1.1
|
||||||
Host: example.com
|
Host: example.com
|
||||||
Accept: application/json, text/javascript
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
@ -262,24 +258,6 @@ class CertificateAccounts(AuthenticatedResource):
|
||||||
Vary: Accept
|
Vary: Accept
|
||||||
Content-Type: text/javascript
|
Content-Type: text/javascript
|
||||||
|
|
||||||
{
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"accountNumber": 222222222,
|
|
||||||
"label": "account2",
|
|
||||||
"comments": "this is a thing"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"accountNumber": 11111111111,
|
|
||||||
"label": "account1",
|
|
||||||
"comments": "this is a thing"
|
|
||||||
},
|
|
||||||
]
|
|
||||||
"total": 2
|
|
||||||
}
|
|
||||||
|
|
||||||
:query sortBy: field to sort on
|
:query sortBy: field to sort on
|
||||||
:query sortDir: acs or desc
|
:query sortDir: acs or desc
|
||||||
:query page: int. default is 1
|
:query page: int. default is 1
|
||||||
|
@ -294,7 +272,7 @@ class CertificateAccounts(AuthenticatedResource):
|
||||||
return service.render(args)
|
return service.render(args)
|
||||||
|
|
||||||
|
|
||||||
api.add_resource(AccountsList, '/accounts', endpoint='accounts')
|
api.add_resource(DestinationsList, '/destinations', endpoint='destinations')
|
||||||
api.add_resource(Accounts, '/accounts/<int:account_id>', endpoint='account')
|
api.add_resource(Destinations, '/destinations/<int:destination_id>', endpoint='account')
|
||||||
api.add_resource(CertificateAccounts, '/certificates/<int:certificate_id>/accounts', endpoint='certificateAccounts')
|
api.add_resource(CertificateDestinations, '/certificates/<int:certificate_id>/destinations', endpoint='certificateDestinations')
|
||||||
|
|
|
@ -16,7 +16,7 @@ from lemur.listeners.models import Listener
|
||||||
class ELB(db.Model):
|
class ELB(db.Model):
|
||||||
__tablename__ = 'elbs'
|
__tablename__ = 'elbs'
|
||||||
id = Column(BigInteger, primary_key=True)
|
id = Column(BigInteger, primary_key=True)
|
||||||
account_id = Column(BigInteger, ForeignKey("accounts.id"), index=True)
|
#account_id = Column(BigInteger, ForeignKey("accounts.id"), index=True)
|
||||||
region = Column(String(32))
|
region = Column(String(32))
|
||||||
name = Column(String(128))
|
name = Column(String(128))
|
||||||
vpc_id = Column(String(128))
|
vpc_id = Column(String(128))
|
||||||
|
|
|
@ -12,9 +12,9 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from lemur.accounts import service as account_service
|
#from lemur.accounts import service as account_service
|
||||||
from lemur.elbs import service as elb_service
|
from lemur.elbs import service as elb_service
|
||||||
from lemur.common.services.aws.elb import get_all_elbs, get_all_regions
|
#from lemur.common.services.aws.elb import get_all_elbs, get_all_regions
|
||||||
|
|
||||||
|
|
||||||
def create_new(known, aws, account):
|
def create_new(known, aws, account):
|
||||||
|
|
|
@ -18,7 +18,7 @@ from lemur.listeners.models import Listener
|
||||||
from lemur.elbs import service as elb_service
|
from lemur.elbs import service as elb_service
|
||||||
from lemur.certificates import service as certificate_service
|
from lemur.certificates import service as certificate_service
|
||||||
|
|
||||||
from lemur.common.services.aws.elb import update_listeners, create_new_listeners, delete_listeners
|
#from lemur.common.services.aws.elb import update_listeners, create_new_listeners, delete_listeners
|
||||||
|
|
||||||
|
|
||||||
def verify_attachment(certificate_id, elb_account_number):
|
def verify_attachment(certificate_id, elb_account_number):
|
||||||
|
|
|
@ -13,9 +13,11 @@ from flask_script.commands import ShowUrls, Clean, Server
|
||||||
from lemur import database
|
from lemur import database
|
||||||
from lemur.users import service as user_service
|
from lemur.users import service as user_service
|
||||||
from lemur.roles import service as role_service
|
from lemur.roles import service as role_service
|
||||||
from lemur.accounts import service as account_service
|
from lemur.destinations import service as destination_service
|
||||||
from lemur.certificates import service as cert_service
|
from lemur.certificates import service as cert_service
|
||||||
|
|
||||||
|
from lemur.plugins.base import plugins
|
||||||
|
|
||||||
from lemur.certificates.verify import verify_string
|
from lemur.certificates.verify import verify_string
|
||||||
from lemur.certificates import sync
|
from lemur.certificates import sync
|
||||||
from lemur.elbs.sync import sync_all_elbs
|
from lemur.elbs.sync import sync_all_elbs
|
||||||
|
@ -27,7 +29,7 @@ from lemur.users.models import User
|
||||||
from lemur.roles.models import Role
|
from lemur.roles.models import Role
|
||||||
from lemur.authorities.models import Authority
|
from lemur.authorities.models import Authority
|
||||||
from lemur.certificates.models import Certificate
|
from lemur.certificates.models import Certificate
|
||||||
from lemur.accounts.models import Account
|
from lemur.destinations.models import Destination
|
||||||
from lemur.domains.models import Domain
|
from lemur.domains.models import Domain
|
||||||
from lemur.elbs.models import ELB
|
from lemur.elbs.models import ELB
|
||||||
from lemur.listeners.models import Listener
|
from lemur.listeners.models import Listener
|
||||||
|
@ -96,12 +98,12 @@ SQLALCHEMY_DATABASE_URI = ''
|
||||||
## AWS ##
|
## AWS ##
|
||||||
#########
|
#########
|
||||||
|
|
||||||
# Lemur will need STS assume role access to every account you want to monitor
|
# Lemur will need STS assume role access to every destination you want to monitor
|
||||||
#AWS_ACCOUNT_MAPPINGS = {{
|
#AWS_ACCOUNT_MAPPINGS = {{
|
||||||
# '1111111111': 'myawsacount'
|
# '1111111111': 'myawsacount'
|
||||||
#}}
|
#}}
|
||||||
|
|
||||||
## This is useful if you know you only want to monitor one account
|
## This is useful if you know you only want to monitor one destination
|
||||||
#AWS_REGIONS = ['us-east-1']
|
#AWS_REGIONS = ['us-east-1']
|
||||||
|
|
||||||
#LEMUR_INSTANCE_PROFILE = 'Lemur'
|
#LEMUR_INSTANCE_PROFILE = 'Lemur'
|
||||||
|
@ -133,6 +135,11 @@ def create():
|
||||||
stamp(revision='head')
|
stamp(revision='head')
|
||||||
|
|
||||||
|
|
||||||
|
@MigrateCommand.command
|
||||||
|
def drop_all():
|
||||||
|
database.db.drop_all()
|
||||||
|
|
||||||
|
|
||||||
@manager.command
|
@manager.command
|
||||||
def check_revoked():
|
def check_revoked():
|
||||||
"""
|
"""
|
||||||
|
@ -227,7 +234,7 @@ class Sync(Command):
|
||||||
|
|
||||||
class InitializeApp(Command):
|
class InitializeApp(Command):
|
||||||
"""
|
"""
|
||||||
This command will bootstrap our database with any accounts as
|
This command will bootstrap our database with any destinations as
|
||||||
specified by our config.
|
specified by our config.
|
||||||
|
|
||||||
Additionally a Lemur user will be created as a default user
|
Additionally a Lemur user will be created as a default user
|
||||||
|
@ -262,15 +269,20 @@ class InitializeApp(Command):
|
||||||
sys.stdout.write("[-] Default user has already been created, skipping...!\n")
|
sys.stdout.write("[-] Default user has already been created, skipping...!\n")
|
||||||
|
|
||||||
if current_app.config.get('AWS_ACCOUNT_MAPPINGS'):
|
if current_app.config.get('AWS_ACCOUNT_MAPPINGS'):
|
||||||
for account_name, account_number in current_app.config.get('AWS_ACCOUNT_MAPPINGS').items():
|
if plugins.get('aws-destination'):
|
||||||
account = account_service.get_by_account_number(account_number)
|
for account_name, account_number in current_app.config.get('AWS_ACCOUNT_MAPPINGS').items():
|
||||||
|
|
||||||
if not account:
|
destination = destination_service.get_by_label(account_name)
|
||||||
account_service.create(account_number, label=account_name)
|
|
||||||
sys.stdout.write("[+] Added new account {0}:{1}!\n".format(account_number, account_name))
|
|
||||||
else:
|
|
||||||
sys.stdout.write("[-] Account already exists, skipping...!\n")
|
|
||||||
|
|
||||||
|
options = dict(account_number=account_number)
|
||||||
|
if not destination:
|
||||||
|
destination_service.create(account_name, 'aws-destination', options,
|
||||||
|
description="This is an auto-generated AWS destination.")
|
||||||
|
sys.stdout.write("[+] Added new destination {0}:{1}!\n".format(account_number, account_name))
|
||||||
|
else:
|
||||||
|
sys.stdout.write("[-] Account already exists, skipping...!\n")
|
||||||
|
else:
|
||||||
|
sys.stdout.write("[!] Skipping adding AWS destinations AWS plugin no available\n")
|
||||||
sys.stdout.write("[/] Done!\n")
|
sys.stdout.write("[/] Done!\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,8 @@ certificate_associations = db.Table('certificate_associations',
|
||||||
Column('certificate_id', Integer, ForeignKey('certificates.id'))
|
Column('certificate_id', Integer, ForeignKey('certificates.id'))
|
||||||
)
|
)
|
||||||
|
|
||||||
certificate_account_associations = db.Table('certificate_account_associations',
|
certificate_destination_associations = db.Table('certificate_destination_associations',
|
||||||
Column('account_id', Integer, ForeignKey('accounts.id', ondelete='cascade')),
|
Column('destination_id', Integer, ForeignKey('destinations.id', ondelete='cascade')),
|
||||||
Column('certificate_id', Integer, ForeignKey('certificates.id', ondelete='cascade'))
|
Column('certificate_id', Integer, ForeignKey('certificates.id', ondelete='cascade'))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from lemur.common.managers import InstanceManager
|
from lemur.common.managers import InstanceManager
|
||||||
|
|
||||||
|
|
||||||
# inspired by https://github.com/getsentry/sentry
|
# inspired by https://github.com/getsentry/sentry
|
||||||
class PluginManager(InstanceManager):
|
class PluginManager(InstanceManager):
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
|
@ -17,8 +16,10 @@ class PluginManager(InstanceManager):
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return sum(1 for i in self.all())
|
return sum(1 for i in self.all())
|
||||||
|
|
||||||
def all(self, version=1):
|
def all(self, version=1, plugin_type=None):
|
||||||
for plugin in sorted(super(PluginManager, self).all(), key=lambda x: x.get_title()):
|
for plugin in sorted(super(PluginManager, self).all(), key=lambda x: x.get_title()):
|
||||||
|
if not plugin.type == plugin_type and plugin_type:
|
||||||
|
continue
|
||||||
if not plugin.is_enabled():
|
if not plugin.is_enabled():
|
||||||
continue
|
continue
|
||||||
if version is not None and plugin.__version__ != version:
|
if version is not None and plugin.__version__ != version:
|
||||||
|
|
|
@ -47,12 +47,13 @@ class IPlugin(local):
|
||||||
# Configuration specifics
|
# Configuration specifics
|
||||||
conf_key = None
|
conf_key = None
|
||||||
conf_title = None
|
conf_title = None
|
||||||
|
options = {}
|
||||||
|
|
||||||
# Global enabled state
|
# Global enabled state
|
||||||
enabled = True
|
enabled = True
|
||||||
can_disable = True
|
can_disable = True
|
||||||
|
|
||||||
def is_enabled(self, project=None):
|
def is_enabled(self):
|
||||||
"""
|
"""
|
||||||
Returns a boolean representing if this plugin is enabled.
|
Returns a boolean representing if this plugin is enabled.
|
||||||
If ``project`` is passed, it will limit the scope to that project.
|
If ``project`` is passed, it will limit the scope to that project.
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
from .destination import DestinationPlugin # NOQA
|
from .destination import DestinationPlugin # NOQA
|
||||||
from .issuer import IssuerPlugin # NOQA
|
from .issuer import IssuerPlugin # NOQA
|
||||||
|
from .source import SourcePlugin
|
|
@ -9,5 +9,8 @@
|
||||||
from lemur.plugins.base import Plugin
|
from lemur.plugins.base import Plugin
|
||||||
|
|
||||||
class DestinationPlugin(Plugin):
|
class DestinationPlugin(Plugin):
|
||||||
pass
|
type = 'destination'
|
||||||
|
|
||||||
|
def upload(self):
|
||||||
|
raise NotImplemented
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@ class IssuerPlugin(Plugin):
|
||||||
This is the base class from which all of the supported
|
This is the base class from which all of the supported
|
||||||
issuers will inherit from.
|
issuers will inherit from.
|
||||||
"""
|
"""
|
||||||
|
type = 'issuer'
|
||||||
|
|
||||||
def create_certificate(self):
|
def create_certificate(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.bases.source
|
||||||
|
:platform: Unix
|
||||||
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
|
||||||
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
|
"""
|
||||||
|
from lemur.plugins.base import Plugin
|
||||||
|
|
||||||
|
class SourcePlugin(Plugin):
|
||||||
|
type = 'source'
|
||||||
|
|
||||||
|
def get_certificates(self):
|
||||||
|
raise NotImplemented
|
||||||
|
|
||||||
|
def get_options(self):
|
||||||
|
return {}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
try:
|
||||||
|
VERSION = __import__('pkg_resources') \
|
||||||
|
.get_distribution(__name__).version
|
||||||
|
except Exception, e:
|
||||||
|
VERSION = 'unknown'
|
|
@ -10,7 +10,7 @@ import boto.ec2
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from lemur.exceptions import InvalidListener
|
from lemur.exceptions import InvalidListener
|
||||||
from lemur.common.services.aws.sts import assume_service
|
from lemur.plugins.lemur_aws.sts import assume_service
|
||||||
|
|
||||||
|
|
||||||
def is_valid(listener_tuple):
|
def is_valid(listener_tuple):
|
|
@ -6,8 +6,17 @@
|
||||||
:license: Apache, see LICENSE for more details.
|
:license: Apache, see LICENSE for more details.
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
"""
|
"""
|
||||||
from flask import current_app
|
from lemur.plugins.lemur_aws.sts import assume_service
|
||||||
from lemur.common.services.aws.sts import assume_service
|
|
||||||
|
|
||||||
|
def get_name_from_arn(arn):
|
||||||
|
"""
|
||||||
|
Extract the certificate name from an arn.
|
||||||
|
|
||||||
|
:param arn: IAM SSL arn
|
||||||
|
:return: name of the certificate as uploaded to AWS
|
||||||
|
"""
|
||||||
|
return arn.split("/", 1)[1]
|
||||||
|
|
||||||
|
|
||||||
def ssl_split(param_string):
|
def ssl_split(param_string):
|
|
@ -0,0 +1,77 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.plugins.lemur_aws.aws
|
||||||
|
:platform: Unix
|
||||||
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
|
||||||
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
|
"""
|
||||||
|
from lemur.plugins.bases import DestinationPlugin, SourcePlugin
|
||||||
|
from lemur.plugins.lemur_aws import iam, elb
|
||||||
|
from lemur.plugins import lemur_aws as aws
|
||||||
|
|
||||||
|
|
||||||
|
def find_value(name, options):
|
||||||
|
for o in options:
|
||||||
|
if o.get(name):
|
||||||
|
return o['value']
|
||||||
|
|
||||||
|
|
||||||
|
class AWSDestinationPlugin(DestinationPlugin):
|
||||||
|
title = 'AWS'
|
||||||
|
slug = 'aws-destination'
|
||||||
|
description = 'Allow the uploading of certificates to AWS IAM'
|
||||||
|
version = aws.VERSION
|
||||||
|
|
||||||
|
author = 'Kevin Glisson'
|
||||||
|
author_url = 'https://github.com/netflix/lemur'
|
||||||
|
|
||||||
|
options = [
|
||||||
|
{
|
||||||
|
'name': 'accountNumber',
|
||||||
|
'type': 'int',
|
||||||
|
'required': True,
|
||||||
|
'validation': '/^[0-9]{12,12}$/',
|
||||||
|
'helpMessage': 'Must be a valid AWS account number!',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
#'elb': {
|
||||||
|
# 'name': {'type': 'name'},
|
||||||
|
# 'region': {'type': 'str'},
|
||||||
|
# 'port': {'type': 'int'}
|
||||||
|
#}
|
||||||
|
|
||||||
|
def upload(self, cert, private_key, cert_chain, options, **kwargs):
|
||||||
|
iam.upload_cert(find_value('accountNumber', options), cert, private_key, cert_chain=cert_chain)
|
||||||
|
|
||||||
|
e = find_value('elb', options)
|
||||||
|
if e:
|
||||||
|
elb.attach_certificate(kwargs['accountNumber'], ['region'], e['name'], e['port'], e['certificateId'])
|
||||||
|
|
||||||
|
|
||||||
|
class AWSSourcePlugin(SourcePlugin):
|
||||||
|
title = 'AWS'
|
||||||
|
slug = 'aws-source'
|
||||||
|
description = 'Discovers all SSL certificates in an AWS account'
|
||||||
|
version = aws.VERSION
|
||||||
|
|
||||||
|
author = 'Kevin Glisson'
|
||||||
|
author_url = 'https://github.com/netflix/lemur'
|
||||||
|
|
||||||
|
options = {
|
||||||
|
'accountNumber': {'type': 'int'},
|
||||||
|
'pollRate': {'type': 'int', 'default': '60'}
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_certificates(self, **kwargs):
|
||||||
|
certs = []
|
||||||
|
arns = elb.get_all_server_certs(kwargs['account_number'])
|
||||||
|
for arn in arns:
|
||||||
|
cert_body = iam.get_cert_from_arn(arn)
|
||||||
|
cert_name = iam.get_name_from_arn(arn)
|
||||||
|
cert = dict(
|
||||||
|
public_certificate=cert_body,
|
||||||
|
name=cert_name
|
||||||
|
)
|
||||||
|
certs.append(cert)
|
||||||
|
return certs
|
|
@ -18,12 +18,12 @@ from requests.adapters import HTTPAdapter
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from lemur.exceptions import LemurException
|
from lemur.exceptions import LemurException
|
||||||
from lemur.plugins.bases import IssuerPlugin
|
from lemur.plugins.bases import IssuerPlugin, SourcePlugin
|
||||||
from lemur.plugins import lemur_cloudca as cloudca
|
from lemur.plugins import lemur_cloudca as cloudca
|
||||||
|
|
||||||
from lemur.authorities import service as authority_service
|
from lemur.authorities import service as authority_service
|
||||||
|
|
||||||
API_ENDPOINT = '/v1/ca/netflix'
|
API_ENDPOINT = '/v1/ca/netflix' # TODO this should be configurable
|
||||||
|
|
||||||
|
|
||||||
class CloudCAException(LemurException):
|
class CloudCAException(LemurException):
|
||||||
|
@ -142,15 +142,7 @@ def get_auth_data(ca_name):
|
||||||
raise CloudCAException("You do not have the required role to issue certificates from {0}".format(ca_name))
|
raise CloudCAException("You do not have the required role to issue certificates from {0}".format(ca_name))
|
||||||
|
|
||||||
|
|
||||||
class CloudCAPlugin(IssuerPlugin):
|
class CloudCA(object):
|
||||||
title = 'CloudCA'
|
|
||||||
slug = 'cloudca'
|
|
||||||
description = 'Enables the creation of certificates from the cloudca API.'
|
|
||||||
version = cloudca.VERSION
|
|
||||||
|
|
||||||
author = 'Kevin Glisson'
|
|
||||||
author_url = 'https://github.com/netflix/lemur'
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
self.session.mount('https://', CloudCAHostNameCheckingAdapter())
|
self.session.mount('https://', CloudCAHostNameCheckingAdapter())
|
||||||
|
@ -162,7 +154,69 @@ class CloudCAPlugin(IssuerPlugin):
|
||||||
else:
|
else:
|
||||||
current_app.logger.warning("No CLOUDCA credentials found, lemur will be unable to request certificates from CLOUDCA")
|
current_app.logger.warning("No CLOUDCA credentials found, lemur will be unable to request certificates from CLOUDCA")
|
||||||
|
|
||||||
super(CloudCAPlugin, self).__init__(*args, **kwargs)
|
super(CloudCA, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def post(self, endpoint, data):
|
||||||
|
"""
|
||||||
|
HTTP POST to CloudCA
|
||||||
|
|
||||||
|
:param endpoint:
|
||||||
|
:param data:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
data = dumps(dict(data.items() + get_auth_data(data['caName']).items()))
|
||||||
|
|
||||||
|
# we set a low timeout, if cloudca is down it shouldn't bring down
|
||||||
|
# lemur
|
||||||
|
response = self.session.post(self.url + endpoint, data=data, timeout=10, verify=self.ca_bundle)
|
||||||
|
return process_response(response)
|
||||||
|
|
||||||
|
def get(self, endpoint):
|
||||||
|
"""
|
||||||
|
HTTP GET to CloudCA
|
||||||
|
|
||||||
|
:param endpoint:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
response = self.session.get(self.url + endpoint, timeout=10, verify=self.ca_bundle)
|
||||||
|
return process_response(response)
|
||||||
|
|
||||||
|
def random(self, length=10):
|
||||||
|
"""
|
||||||
|
Uses CloudCA as a decent source of randomness.
|
||||||
|
|
||||||
|
:param length:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
endpoint = '/v1/random/{0}'.format(length)
|
||||||
|
response = self.session.get(self.url + endpoint, verify=self.ca_bundle)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def get_authorities(self):
|
||||||
|
"""
|
||||||
|
Retrieves authorities that were made outside of Lemur.
|
||||||
|
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
endpoint = '{0}/listCAs'.format(API_ENDPOINT)
|
||||||
|
authorities = []
|
||||||
|
for ca in self.get(endpoint)['data']['caList']:
|
||||||
|
try:
|
||||||
|
authorities.append(ca['caName'])
|
||||||
|
except AttributeError as e:
|
||||||
|
current_app.logger.error("No authority has been defined for {}".format(ca['caName']))
|
||||||
|
|
||||||
|
return authorities
|
||||||
|
|
||||||
|
|
||||||
|
class CloudCAIssuerPlugin(IssuerPlugin, CloudCA):
|
||||||
|
title = 'CloudCA'
|
||||||
|
slug = 'cloudca-issuer'
|
||||||
|
description = 'Enables the creation of certificates from the cloudca API.'
|
||||||
|
version = cloudca.VERSION
|
||||||
|
|
||||||
|
author = 'Kevin Glisson'
|
||||||
|
author_url = 'https://github.com/netflix/lemur'
|
||||||
|
|
||||||
def create_authority(self, options):
|
def create_authority(self, options):
|
||||||
"""
|
"""
|
||||||
|
@ -205,22 +259,6 @@ class CloudCAPlugin(IssuerPlugin):
|
||||||
|
|
||||||
return cert, "".join(intermediates), roles,
|
return cert, "".join(intermediates), roles,
|
||||||
|
|
||||||
def get_authorities(self):
|
|
||||||
"""
|
|
||||||
Retrieves authorities that were made outside of Lemur.
|
|
||||||
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
endpoint = '{0}/listCAs'.format(API_ENDPOINT)
|
|
||||||
authorities = []
|
|
||||||
for ca in self.get(endpoint)['data']['caList']:
|
|
||||||
try:
|
|
||||||
authorities.append(ca['caName'])
|
|
||||||
except AttributeError as e:
|
|
||||||
current_app.logger.error("No authority has been defined for {}".format(ca['caName']))
|
|
||||||
|
|
||||||
return authorities
|
|
||||||
|
|
||||||
def create_certificate(self, csr, options):
|
def create_certificate(self, csr, options):
|
||||||
"""
|
"""
|
||||||
Creates a new certificate from cloudca
|
Creates a new certificate from cloudca
|
||||||
|
@ -259,16 +297,25 @@ class CloudCAPlugin(IssuerPlugin):
|
||||||
|
|
||||||
return cert, "".join(intermediates),
|
return cert, "".join(intermediates),
|
||||||
|
|
||||||
def random(self, length=10):
|
|
||||||
"""
|
|
||||||
Uses CloudCA as a decent source of randomness.
|
|
||||||
|
|
||||||
:param length:
|
class CloudCASourcePlugin(SourcePlugin, CloudCA):
|
||||||
:return:
|
title = 'CloudCA'
|
||||||
"""
|
slug = 'cloudca-source'
|
||||||
endpoint = '/v1/random/{0}'.format(length)
|
description = 'Discovers all SSL certificates in CloudCA'
|
||||||
response = self.session.get(self.url + endpoint, verify=self.ca_bundle)
|
version = cloudca.VERSION
|
||||||
return response
|
|
||||||
|
author = 'Kevin Glisson'
|
||||||
|
author_url = 'https://github.com/netflix/lemur'
|
||||||
|
|
||||||
|
options = {
|
||||||
|
'pollRate': {'type': 'int', 'default': '60'}
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_certificates(self, **kwargs):
|
||||||
|
certs = []
|
||||||
|
for authority in self.get_authorities():
|
||||||
|
certs += self.get_cert(ca_name=authority)
|
||||||
|
return
|
||||||
|
|
||||||
def get_cert(self, ca_name=None, cert_handle=None):
|
def get_cert(self, ca_name=None, cert_handle=None):
|
||||||
"""
|
"""
|
||||||
|
@ -297,29 +344,3 @@ class CloudCAPlugin(IssuerPlugin):
|
||||||
})
|
})
|
||||||
|
|
||||||
return certs
|
return certs
|
||||||
|
|
||||||
def post(self, endpoint, data):
|
|
||||||
"""
|
|
||||||
HTTP POST to CloudCA
|
|
||||||
|
|
||||||
:param endpoint:
|
|
||||||
:param data:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
data = dumps(dict(data.items() + get_auth_data(data['caName']).items()))
|
|
||||||
|
|
||||||
# we set a low timeout, if cloudca is down it shouldn't bring down
|
|
||||||
# lemur
|
|
||||||
response = self.session.post(self.url + endpoint, data=data, timeout=10, verify=self.ca_bundle)
|
|
||||||
return process_response(response)
|
|
||||||
|
|
||||||
def get(self, endpoint):
|
|
||||||
"""
|
|
||||||
HTTP GET to CloudCA
|
|
||||||
|
|
||||||
:param endpoint:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
response = self.session.get(self.url + endpoint, timeout=10, verify=self.ca_bundle)
|
|
||||||
return process_response(response)
|
|
||||||
|
|
||||||
|
|
|
@ -75,9 +75,9 @@ def handle_response(content):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
class VerisignPlugin(IssuerPlugin):
|
class VerisignIssuerPlugin(IssuerPlugin):
|
||||||
title = 'VeriSign'
|
title = 'Verisign'
|
||||||
slug = 'verisign'
|
slug = 'verisign-issuer'
|
||||||
description = 'Enables the creation of certificates by the VICE2.0 verisign API.'
|
description = 'Enables the creation of certificates by the VICE2.0 verisign API.'
|
||||||
version = verisign.VERSION
|
version = verisign.VERSION
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ class VerisignPlugin(IssuerPlugin):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
self.session.cert = current_app.config.get('VERISIGN_PEM_PATH')
|
self.session.cert = current_app.config.get('VERISIGN_PEM_PATH')
|
||||||
super(VerisignPlugin, self).__init__(*args, **kwargs)
|
super(VerisignIssuerPlugin, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def create_certificate(self, csr, issuer_options):
|
def create_certificate(self, csr, issuer_options):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.plugins.views
|
||||||
|
:platform: Unix
|
||||||
|
:synopsis: This module contains all of the accounts view code.
|
||||||
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
|
"""
|
||||||
|
from flask import Blueprint
|
||||||
|
from flask.ext.restful import Api, reqparse, fields
|
||||||
|
from lemur.auth.service import AuthenticatedResource
|
||||||
|
|
||||||
|
from lemur.common.utils import marshal_items
|
||||||
|
|
||||||
|
from lemur.plugins.base import plugins
|
||||||
|
|
||||||
|
mod = Blueprint('plugins', __name__)
|
||||||
|
api = Api(mod)
|
||||||
|
|
||||||
|
|
||||||
|
FIELDS = {
|
||||||
|
'title': fields.String,
|
||||||
|
'pluginOptions': fields.Raw(attribute='options'),
|
||||||
|
'description': fields.String,
|
||||||
|
'version': fields.String,
|
||||||
|
'author': fields.String,
|
||||||
|
'authorUrl': fields.String,
|
||||||
|
'type': fields.String,
|
||||||
|
'slug': fields.String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PluginsList(AuthenticatedResource):
|
||||||
|
""" Defines the 'plugins' endpoint """
|
||||||
|
def __init__(self):
|
||||||
|
self.reqparse = reqparse.RequestParser()
|
||||||
|
super(PluginsList, self).__init__()
|
||||||
|
|
||||||
|
@marshal_items(FIELDS)
|
||||||
|
def get(self):
|
||||||
|
"""
|
||||||
|
.. http:get:: /plugins
|
||||||
|
|
||||||
|
The current plugin list
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /plugins HTTP/1.1
|
||||||
|
Host: example.com
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: text/javascript
|
||||||
|
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"accountNumber": 222222222,
|
||||||
|
"label": "account2",
|
||||||
|
"comments": "this is a thing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"accountNumber": 11111111111,
|
||||||
|
"label": "account1",
|
||||||
|
"comments": "this is a thing"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"total": 2
|
||||||
|
}
|
||||||
|
|
||||||
|
:reqheader Authorization: OAuth token to authenticate
|
||||||
|
:statuscode 200: no error
|
||||||
|
"""
|
||||||
|
return plugins.all()
|
||||||
|
|
||||||
|
|
||||||
|
class PluginsTypeList(AuthenticatedResource):
|
||||||
|
""" Defines the 'plugins' endpoint """
|
||||||
|
def __init__(self):
|
||||||
|
self.reqparse = reqparse.RequestParser()
|
||||||
|
super(PluginsTypeList, self).__init__()
|
||||||
|
|
||||||
|
@marshal_items(FIELDS)
|
||||||
|
def get(self, plugin_type):
|
||||||
|
"""
|
||||||
|
.. http:get:: /plugins/issuer
|
||||||
|
|
||||||
|
The current plugin list
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /plugins/issuer HTTP/1.1
|
||||||
|
Host: example.com
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: text/javascript
|
||||||
|
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"accountNumber": 222222222,
|
||||||
|
"label": "account2",
|
||||||
|
"comments": "this is a thing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"accountNumber": 11111111111,
|
||||||
|
"label": "account1",
|
||||||
|
"comments": "this is a thing"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"total": 2
|
||||||
|
}
|
||||||
|
|
||||||
|
:reqheader Authorization: OAuth token to authenticate
|
||||||
|
:statuscode 200: no error
|
||||||
|
"""
|
||||||
|
return list(plugins.all(plugin_type=plugin_type))
|
||||||
|
|
||||||
|
api.add_resource(PluginsList, '/plugins', endpoint='plugins')
|
||||||
|
api.add_resource(PluginsTypeList, '/plugins/<plugin_type>', endpoint='pluginType')
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
angular.module('lemur')
|
|
||||||
|
|
||||||
.config(function config($routeProvider) {
|
|
||||||
$routeProvider.when('/accounts/create', {
|
|
||||||
templateUrl: '/angular/accounts/account/account.tpl.html',
|
|
||||||
controller: 'AccountsCreateController'
|
|
||||||
});
|
|
||||||
$routeProvider.when('/accounts/:id/edit', {
|
|
||||||
templateUrl: '/angular/accounts/account/account.tpl.html',
|
|
||||||
controller: 'AccountsEditController'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
.controller('AccountsCreateController', function ($scope, AccountService, LemurRestangular){
|
|
||||||
$scope.account = LemurRestangular.restangularizeElement(null, {}, 'accounts');
|
|
||||||
$scope.save = AccountService.create;
|
|
||||||
})
|
|
||||||
|
|
||||||
.controller('AccountsEditController', function ($scope, $routeParams, AccountService, AccountApi) {
|
|
||||||
AccountApi.get($routeParams.id).then(function (account) {
|
|
||||||
$scope.account = account;
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.save = AccountService.update;
|
|
||||||
});
|
|
|
@ -1,45 +0,0 @@
|
||||||
<h2 class="featurette-heading"><span ng-show="!account.fromServer">Create</span><span ng-show="account.fromServer">Edit</span> Account <span class="text-muted"><small>next in line please
|
|
||||||
</small></span></h2>
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<a href="/#/accounts/" class="btn btn-danger pull-right">Cancel</a>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<form name="createForm" class="form-horizontal" role="form" novalidate>
|
|
||||||
<div class="form-group"
|
|
||||||
ng-class="{'has-error': createForm.name.$invalid, 'has-success': !createForm.name.$invalid&&createForm.name.$dirty}">
|
|
||||||
<label class="control-label col-sm-2">
|
|
||||||
Name
|
|
||||||
</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input name="name" ng-model="account.label" placeholder="Name" class="form-control" required/>
|
|
||||||
<p ng-show="createForm.name.$invalid && !createForm.name.$pristine" class="help-block">You must enter an account name</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group"
|
|
||||||
ng-class="{'has-error': createForm.accountNumber.$invalid, 'has-success': !createForm.accountNumber.$invalid&&createForm.accountNumber.$dirty}">
|
|
||||||
<label class="control-label col-sm-2">
|
|
||||||
Account Number
|
|
||||||
</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input type="number" name="accountNumber" ng-model="account.accountNumber" placeholder="111111111111" class="form-control" ng-minlength="12" ng-maxlength="12" required/>
|
|
||||||
<p ng-show="createForm.accountNumber.$invalid && !createForm.accountNumber.$pristine" class="help-block">You must enter an account number</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-sm-2">
|
|
||||||
Comments
|
|
||||||
</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<textarea name="comments" ng-model="account.comments" placeholder="Something elegant" class="form-control" ></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="panel-footer">
|
|
||||||
<button ng-click="save(account)" type="submit" ng-disabled="createForm.$invalid" class="btn btn-primary pull-right">Save</button>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
'use strict';
|
|
||||||
angular.module('lemur')
|
|
||||||
.service('AccountApi', function (LemurRestangular) {
|
|
||||||
return LemurRestangular.all('accounts');
|
|
||||||
})
|
|
||||||
.service('AccountService', function ($location, AccountApi, toaster) {
|
|
||||||
var AccountService = this;
|
|
||||||
AccountService.findAccountsByName = function (filterValue) {
|
|
||||||
return AccountApi.getList({'filter[label]': filterValue})
|
|
||||||
.then(function (accounts) {
|
|
||||||
return accounts;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
AccountService.create = function (account) {
|
|
||||||
AccountApi.post(account).then(
|
|
||||||
function () {
|
|
||||||
toaster.pop({
|
|
||||||
type: 'success',
|
|
||||||
title: account.label,
|
|
||||||
body: 'Successfully created!'
|
|
||||||
});
|
|
||||||
$location.path('accounts');
|
|
||||||
},
|
|
||||||
function (response) {
|
|
||||||
toaster.pop({
|
|
||||||
type: 'error',
|
|
||||||
title: account.label,
|
|
||||||
body: 'Was not created! ' + response.data.message
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
AccountService.update = function (account) {
|
|
||||||
account.put().then(
|
|
||||||
function () {
|
|
||||||
toaster.pop({
|
|
||||||
type: 'success',
|
|
||||||
title: account.label,
|
|
||||||
body: 'Successfully updated!'
|
|
||||||
});
|
|
||||||
$location.path('accounts');
|
|
||||||
},
|
|
||||||
function (response) {
|
|
||||||
toaster.pop({
|
|
||||||
type: 'error',
|
|
||||||
title: account.label,
|
|
||||||
body: 'Was not updated! ' + response.data.message
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
return AccountService;
|
|
||||||
});
|
|
|
@ -1,52 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
angular.module('lemur')
|
|
||||||
|
|
||||||
.config(function config($routeProvider) {
|
|
||||||
$routeProvider.when('/accounts', {
|
|
||||||
templateUrl: '/angular/accounts/view/view.tpl.html',
|
|
||||||
controller: 'AccountsViewController'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
.controller('AccountsViewController', function ($scope, AccountApi, AccountService, ngTableParams, toaster) {
|
|
||||||
$scope.filter = {};
|
|
||||||
$scope.accountsTable = new ngTableParams({
|
|
||||||
page: 1, // show first page
|
|
||||||
count: 10, // count per page
|
|
||||||
sorting: {
|
|
||||||
id: 'desc' // initial sorting
|
|
||||||
},
|
|
||||||
filter: $scope.filter
|
|
||||||
}, {
|
|
||||||
total: 0, // length of data
|
|
||||||
getData: function ($defer, params) {
|
|
||||||
AccountApi.getList(params.url()).then(
|
|
||||||
function (data) {
|
|
||||||
params.total(data.total);
|
|
||||||
$defer.resolve(data);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.remove = function (account) {
|
|
||||||
account.remove().then(
|
|
||||||
function () {
|
|
||||||
$scope.accountsTable.reload();
|
|
||||||
},
|
|
||||||
function (response) {
|
|
||||||
toaster.pop({
|
|
||||||
type: 'error',
|
|
||||||
title: 'Opps',
|
|
||||||
body: 'I see what you did there' + response.data.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.toggleFilter = function (params) {
|
|
||||||
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,44 +0,0 @@
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<h2 class="featurette-heading">Accounts
|
|
||||||
<span class="text-muted"><small>next in line please</small></span></h2>
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<div class="btn-group pull-right">
|
|
||||||
<a href="#/accounts/create" class="btn btn-primary">Create</a>
|
|
||||||
</div>
|
|
||||||
<div class="btn-group">
|
|
||||||
<button ng-click="toggleFilter(accountsTable)" class="btn btn-default">Filter</button>
|
|
||||||
</div>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table ng-table="accountsTable" class="table table-striped" show-filter="false" template-pagination="angular/pager.html" >
|
|
||||||
<tbody>
|
|
||||||
<tr ng-repeat="account in $data track by $index">
|
|
||||||
<td data-title="'Name'" sortable="'label'" filter="{ 'label': 'text' }">
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li>{{ account.label }}</li>
|
|
||||||
<li><span class="text-muted">{{ account.comments }}</span></li>
|
|
||||||
</ul>
|
|
||||||
</td>
|
|
||||||
<td data-title="'Account Number'" sortable="'account_number'" filter="{ 'account_number': 'text' }">
|
|
||||||
{{ account.accountNumber }}
|
|
||||||
</td>
|
|
||||||
<td data-title="''">
|
|
||||||
<div class="btn-group-vertical pull-right">
|
|
||||||
<a tooltip="Edit Account" href="#/accounts/{{ account.id }}/edit" class="btn btn-sm btn-info">
|
|
||||||
Edit
|
|
||||||
</a>
|
|
||||||
<button tooltip="Delete Account" ng-click="remove(account)" type="button" class="btn btn-sm btn-danger pull-left">
|
|
||||||
Remove
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -2,17 +2,6 @@
|
||||||
|
|
||||||
angular.module('lemur')
|
angular.module('lemur')
|
||||||
|
|
||||||
.config(function config($routeProvider) {
|
|
||||||
$routeProvider.when('/authorities/create', {
|
|
||||||
templateUrl: '/angular/authorities/authority/authorityWizard.tpl.html',
|
|
||||||
controller: 'AuthorityCreateController'
|
|
||||||
});
|
|
||||||
$routeProvider.when('/authorities/:id/edit', {
|
|
||||||
templateUrl: '/angular/authorities/authority/authorityEdit.tpl.html',
|
|
||||||
controller: 'AuthorityEditController'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
.controller('AuthorityEditController', function ($scope, $routeParams, AuthorityApi, AuthorityService, RoleService){
|
.controller('AuthorityEditController', function ($scope, $routeParams, AuthorityApi, AuthorityService, RoleService){
|
||||||
AuthorityApi.get($routeParams.id).then(function (authority) {
|
AuthorityApi.get($routeParams.id).then(function (authority) {
|
||||||
AuthorityService.getRoles(authority);
|
AuthorityService.getRoles(authority);
|
||||||
|
@ -24,16 +13,21 @@ angular.module('lemur')
|
||||||
$scope.roleService = RoleService;
|
$scope.roleService = RoleService;
|
||||||
})
|
})
|
||||||
|
|
||||||
.controller('AuthorityCreateController', function ($scope, $modal, AuthorityService, LemurRestangular, RoleService) {
|
.controller('AuthorityCreateController', function ($scope, $modalInstance, AuthorityService, LemurRestangular, RoleService, PluginService, WizardHandler) {
|
||||||
$scope.authority = LemurRestangular.restangularizeElement(null, {}, 'authorities');
|
$scope.authority = LemurRestangular.restangularizeElement(null, {}, 'authorities');
|
||||||
|
|
||||||
$scope.save = function (authority) {
|
$scope.loading = false;
|
||||||
var loadingModal = $modal.open({backdrop: 'static', template: '<wave-spinner></wave-spinner>', windowTemplateUrl: 'angular/loadingModal.html', size: 'large'});
|
$scope.create = function (authority) {
|
||||||
return AuthorityService.create(authority).then(function (response) {
|
WizardHandler.wizard().context.loading = true;
|
||||||
loadingModal.close();
|
AuthorityService.create(authority).then(function (resposne) {
|
||||||
});
|
WizardHandler.wizard().context.loading = false;
|
||||||
|
$modalInstance.close();
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PluginService.get('issuer').then(function (plugins) {
|
||||||
|
$scope.plugins = plugins;
|
||||||
|
});
|
||||||
|
|
||||||
$scope.roleService = RoleService;
|
$scope.roleService = RoleService;
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
<h2 class="featurette-heading"><span ng-show="!authority.id">Create</span><span ng-show="authority.id">Edit</span> Authority <span class="text-muted"><small>The nail that sticks out farthest gets hammered the hardest
|
<div class="modal-header">
|
||||||
<div>
|
<h3 class="modal-title"><span ng-show="!authority.id">Create</span><span ng-show="authority.id">Edit</span> Authority <span class="text-muted"><small>The nail that sticks out farthest gets hammered the hardest</small></span></h3>
|
||||||
<wizard on-finish="save(authority)" template="angular/wizard.html">
|
</div>
|
||||||
<wz-step title="Tracking" canexit="exitTracking">
|
<div class="modal-body">
|
||||||
<ng-include src="'angular/authorities/authority/tracking.tpl.html'"></ng-include>
|
<div>
|
||||||
</wz-step>
|
<wizard on-finish="create(authority)" template="angular/wizard.html">
|
||||||
<wz-step title="Distinguished Name" canenter="exitTracking" canexit="exitDN">
|
<wz-step title="Tracking" canexit="exitTracking">
|
||||||
<ng-include src="'angular/authorities/authority/distinguishedName.tpl.html'"></ng-include>
|
<ng-include src="'angular/authorities/authority/tracking.tpl.html'"></ng-include>
|
||||||
</wz-step>
|
</wz-step>
|
||||||
<wz-step title="Options" canenter="exitDN" canexit="exitOptions">
|
<wz-step title="Options" canenter="exitDN" canexit="exitOptions">
|
||||||
<ng-include src="'angular/authorities/authority/options.tpl.html'"></ng-include>
|
<ng-include src="'angular/authorities/authority/options.tpl.html'"></ng-include>
|
||||||
</wz-step>
|
</wz-step>
|
||||||
<wz-step title="Extensions" canenter="exitOptions" canexit="exitExtensions">
|
<wz-step title="Distinguished Name" canenter="exitTracking" canexit="exitDN">
|
||||||
<ng-include src="'angular/authorities/authority/extensions.tpl.html'"></ng-include>
|
<ng-include src="'angular/authorities/authority/distinguishedName.tpl.html'"></ng-include>
|
||||||
</wz-step>
|
</wz-step>
|
||||||
</wizard>
|
<wz-step title="Extensions" canenter="exitOptions" canexit="exitExtensions">
|
||||||
|
<ng-include src="'angular/authorities/authority/extensions.tpl.html'"></ng-include>
|
||||||
|
</wz-step>
|
||||||
|
</wizard>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<input type="text" ng-model="authority.caParent" placeholder="Parent Authority Name"
|
<input type="text" ng-model="authority.caParent" placeholder="Parent Authority Name"
|
||||||
typeahead="authority.name for authority in authorityService.findAuthorityByName($viewValue)" typeahead-loading="loadingAuthorities"
|
typeahead="authority.name for authority in authorityService.findAuthorityByName($viewValue)" typeahead-loading="loadingAuthorities"
|
||||||
class="form-control input-md" typeahead-min-wait="50"
|
class="form-control input-md" typeahead-min-wait="50"
|
||||||
tooltip="When you specifiy a subordinate certificate authority you must specific the parent authority"
|
tooltip="When you specify a subordinate certificate authority you must specific the parent authority"
|
||||||
tooltip-trigger="focus" tooltip-placement="top">
|
tooltip-trigger="focus" tooltip-placement="top">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -69,10 +69,10 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Plugin Name
|
Plugin
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<select class="form-control" ng-model="authority.pluginName" ng-options="option for option in ['cloudca', 'verisign']" ng-init="authority.pluginName = 'cloudca'" required></select>
|
<select class="form-control" ng-model="authority.pluginName" ng-options="plugin.slug as plugin.title for plugin in plugins" ng-init="authority.pluginName = 'cloudca-issuer'" required></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -31,37 +31,40 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group"
|
<div class="form-group"
|
||||||
ng-class="{'has-error': trackingForm.commonName.$invalid, 'has-success': !trackingForm.$invalid&&trackingForm.commanName.$dirty}">
|
ng-class="{'has-error': trackingForm.commonName.$invalid, 'has-success': !trackingForm.$invalid&&trackingForm.commonName.$dirty}">
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Common Name
|
Common Name
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input name="commonName" ng-model="authority.caDN.commonName" placeholder="Common Name" class="form-control" required/>
|
<input name="commonName" ng-model="authority.caDN.commonName" placeholder="Common Name" class="form-control" required/>
|
||||||
<p ng-show="trackingForm.commandName.$invalid && !trackingForm.commonName.$pristine" class="help-block">You must enter a common name</p>
|
<p ng-show="trackingForm.commonName.$invalid && !trackingForm.commonName.$pristine" class="help-block">You must enter a common name</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group"
|
||||||
|
ng-class="{'has-error': trackingForm.validityEnd.$invalid || trackingForm.validityStart.$invalid, 'has-success': !trackingForm.$invalid&&trackingForm.validityEnd.$dirty&&trackingForm.validityStart.$dirty}">
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Validity Range
|
Validity Range
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<div>
|
<div>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input tooltip="Starting Date" class="form-control" datepicker-popup="yyyy/MM/dd" is-open="opened1" ng-model="authority.validityStart" />
|
<input name="validityStart" tooltip="Starting Date" class="form-control" datepicker-popup="yyyy/MM/dd" is-open="opened1" ng-model="authority.validityStart" required/>
|
||||||
<span class="input-group-btn">
|
<p ng-show="trackingForm.validityStart.$invalid && !trackingForm.validityStart.$pristine" class="help-block">A start date is required!</p>
|
||||||
<button class="btn btn-default" ng-click="open($event)"><i class="glyphicon glyphicon-calendar"></i></button>
|
<span class="input-group-btn">
|
||||||
</span>
|
<button class="btn btn-default" ng-click="open($event)"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-center col-sm-2"><label><span class="glyphicon glyphicon-resize-horizontal"></span></label></span>
|
<span style="padding-top: 15px" class="text-center col-sm-2"><label><span class="glyphicon glyphicon-resize-horizontal"></span></label></span>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<div>
|
<div>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input tooltip="Ending Date" class="form-control" datepicker-popup="yyyy/MM/dd" is-open="opened2" ng-model="authority.validityEnd" />
|
<input name="validityEnd" tooltip="Ending Date" class="form-control" datepicker-popup="yyyy/MM/dd" is-open="opened2" ng-model="authority.validityEnd" required/>
|
||||||
<span class="input-group-btn">
|
<p ng-show="trackingForm.validityEnd.$invalid && !trackingForm.validityEnd.$pristine" class="help-block">A end date is required!</p>
|
||||||
<button class="btn btn-default" ng-click="open2($event)"><i class="glyphicon glyphicon-calendar"></i></button>
|
<span class="input-group-btn">
|
||||||
</span>
|
<button class="btn btn-default" ng-click="open2($event)"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,7 +9,7 @@ angular.module('lemur')
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
.controller('AuthoritiesViewController', function ($scope, $q, AuthorityApi, AuthorityService, ngTableParams) {
|
.controller('AuthoritiesViewController', function ($scope, $q, $modal, AuthorityApi, AuthorityService, ngTableParams) {
|
||||||
$scope.filter = {};
|
$scope.filter = {};
|
||||||
$scope.authoritiesTable = new ngTableParams({
|
$scope.authoritiesTable = new ngTableParams({
|
||||||
page: 1, // show first page
|
page: 1, // show first page
|
||||||
|
@ -43,4 +43,36 @@ angular.module('lemur')
|
||||||
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.edit = function (authorityId) {
|
||||||
|
var modalInstance = $modal.open({
|
||||||
|
animation: true,
|
||||||
|
templateUrl: '/angular/authorities/authority/authorityWizard.tpl.html',
|
||||||
|
controller: 'AuthorityEditController',
|
||||||
|
size: 'lg',
|
||||||
|
resolve: {
|
||||||
|
editId: function () {
|
||||||
|
return authorityId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
modalInstance.result.then(function () {
|
||||||
|
$scope.authoritiesTable.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.create = function () {
|
||||||
|
var modalInstance = $modal.open({
|
||||||
|
animation: true,
|
||||||
|
controller: 'AuthorityCreateController',
|
||||||
|
templateUrl: '/angular/authorities/authority/authorityWizard.tpl.html',
|
||||||
|
size: 'lg'
|
||||||
|
});
|
||||||
|
|
||||||
|
modalInstance.result.then(function () {
|
||||||
|
$scope.authoritiesTable.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<div class="btn-group pull-right">
|
<div class="btn-group pull-right">
|
||||||
<a href="#/authorities/create" class="btn btn-primary">Create</a>
|
<button class="btn btn-primary" ng-click="create()">Create</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button ng-click="toggleFilter(authoritiesTable)" class="btn btn-default">Filter</button>
|
<button ng-click="toggleFilter(authoritiesTable)" class="btn btn-default">Filter</button>
|
||||||
|
@ -36,9 +36,9 @@
|
||||||
</td>
|
</td>
|
||||||
<td data-title="''">
|
<td data-title="''">
|
||||||
<div class="btn-group-vertical pull-right">
|
<div class="btn-group-vertical pull-right">
|
||||||
<a tooltip="Edit Authority" href="#/authorities/{{ authority.id }}/edit" class="btn btn-sm btn-info">
|
<button tooltip="Edit Authority" ng-click="edit(authority.id)" class="btn btn-sm btn-info">
|
||||||
Edit
|
Edit
|
||||||
</a>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,18 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('lemur')
|
angular.module('lemur')
|
||||||
.config(function config($routeProvider) {
|
|
||||||
$routeProvider.when('/certificates/create', {
|
|
||||||
templateUrl: '/angular/certificates/certificate/certificateWizard.tpl.html',
|
|
||||||
controller: 'CertificateCreateController'
|
|
||||||
});
|
|
||||||
|
|
||||||
$routeProvider.when('/certificates/:id/edit', {
|
|
||||||
templateUrl: '/angular/certificates/certificate/edit.tpl.html',
|
|
||||||
controller: 'CertificateEditController'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
.controller('CertificateEditController', function ($scope, $routeParams, CertificateApi, CertificateService, MomentService) {
|
.controller('CertificateEditController', function ($scope, $routeParams, CertificateApi, CertificateService, MomentService) {
|
||||||
CertificateApi.get($routeParams.id).then(function (certificate) {
|
CertificateApi.get($routeParams.id).then(function (certificate) {
|
||||||
$scope.certificate = certificate;
|
$scope.certificate = certificate;
|
||||||
|
@ -23,13 +11,14 @@ angular.module('lemur')
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
.controller('CertificateCreateController', function ($scope, $modal, CertificateApi, CertificateService, AccountService, ELBService, AuthorityService, MomentService, LemurRestangular) {
|
.controller('CertificateCreateController', function ($scope, $modalInstance, CertificateApi, CertificateService, DestinationService, ELBService, AuthorityService, PluginService, MomentService, WizardHandler, LemurRestangular) {
|
||||||
$scope.certificate = LemurRestangular.restangularizeElement(null, {}, 'certificates');
|
$scope.certificate = LemurRestangular.restangularizeElement(null, {}, 'certificates');
|
||||||
|
|
||||||
$scope.save = function (certificate) {
|
$scope.create = function (certificate) {
|
||||||
var loadingModal = $modal.open({backdrop: 'static', template: '<wave-spinner></wave-spinner>', windowTemplateUrl: 'angular/loadingModal.html', size: 'large'});
|
WizardHandler.wizard().context.loading = true;
|
||||||
CertificateService.create(certificate).then(function (response) {
|
CertificateService.create(certificate).then(function (response) {
|
||||||
loadingModal.close();
|
WizardHandler.wizard().context.loading = false;
|
||||||
|
$modalInstance.close();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,7 +77,11 @@ angular.module('lemur')
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PluginService.get('destination').then(function (plugins) {
|
||||||
|
$scope.plugins = plugins;
|
||||||
|
});
|
||||||
|
|
||||||
$scope.elbService = ELBService;
|
$scope.elbService = ELBService;
|
||||||
$scope.authorityService = AuthorityService;
|
$scope.authorityService = AuthorityService;
|
||||||
$scope.accountService = AccountService;
|
$scope.destinationService = DestinationService;
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
<h2 class="featurette-heading">Create a certificate <span class="text-muted"><small>encrypt all the things
|
|
||||||
</small></span></h2>
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<a href="/#/certificates/" class="btn btn-danger pull-right">Cancel</a>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<form name="createForm" class="form-horizontal" role="form" ng-submit="submitCreate()" novalidate>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="panel-footer">
|
|
||||||
<button type="submit" ng-disabled="createForm.$invalid" class="btn btn-success pull-right"><span
|
|
||||||
ng-show="!loading"> Create </span><span ng-show="loading">Creating <i ng-show="loading"
|
|
||||||
class="fa fa-cog fa-spin"></i></span>
|
|
||||||
</button>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,17 +1,23 @@
|
||||||
<h2 class="featurette-heading"><span ng-show="!certificate.id">Create</span><span ng-show="certificate.id">Edit</span> Certificate <span class="text-muted"><small>encrypt all the things
|
<div class="modal-header">
|
||||||
<div>
|
<h3 class="modal-title"><span ng-show="!certificate.id">Create</span><span ng-show="certificate.id">Edit</span> Certificate <span class="text-muted"><small>encrypt all the things</small></h3>
|
||||||
<wizard on-finish="save(certificate)" template="angular/wizard.html">
|
</div>
|
||||||
<wz-step title="Tracking" canexit="trackingForm.$valid">
|
<div class="modal-body">
|
||||||
<ng-include src="'angular/certificates/certificate/tracking.tpl.html'"></ng-include>
|
<div>
|
||||||
</wz-step>
|
<wizard on-finish="create(certificate)" template="angular/wizard.html">
|
||||||
<wz-step title="Distinguished Name" canenter="exitTracking" canexit="exitDN">
|
<wz-step title="Tracking" canexit="trackingForm.$valid">
|
||||||
<ng-include src="'angular/certificates/certificate/distinguishedName.tpl.html'"></ng-include>
|
<ng-include src="'angular/certificates/certificate/tracking.tpl.html'"></ng-include>
|
||||||
</wz-step>
|
</wz-step>
|
||||||
<wz-step title="Options" canenter="enterValidation">
|
<wz-step title="Options" canenter="enterValidation">
|
||||||
<ng-include src="'angular/certificates/certificate/options.tpl.html'"></ng-include>
|
<ng-include src="'angular/certificates/certificate/options.tpl.html'"></ng-include>
|
||||||
</wz-step>
|
</wz-step>
|
||||||
<wz-step title="Destinations" canenter="enterValidation">
|
<wz-step title="Distinguished Name" canenter="exitTracking" canexit="exitDN">
|
||||||
<ng-include src="'angular/certificates/certificate/destinations.tpl.html'"></ng-include>
|
<ng-include src="'angular/certificates/certificate/distinguishedName.tpl.html'"></ng-include>
|
||||||
</wz-step>
|
</wz-step>
|
||||||
</wizard>
|
<wz-step title="Destinations" canenter="enterValidation">
|
||||||
</div>
|
<ng-include src="'angular/certificates/certificate/destinations.tpl.html'"></ng-include>
|
||||||
|
</wz-step>
|
||||||
|
</wizard>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,62 +1,28 @@
|
||||||
<p>Destinations are purely optional, if you think the created certificate will be used in AWS select one or more accounts and Lemur will upload it for you.</p>
|
<div class="form-group">
|
||||||
<div class="form-horizontal">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Accounts
|
Destinations
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" ng-model="certificate.selectedAccount" placeholder="Account Name"
|
<input type="text" ng-model="certificate.selectedDestination" placeholder="AWS, TheSecret..."
|
||||||
typeahead="account.label for account in accountService.findAccountsByName($viewValue)" typeahead-loading="loadingAccounts"
|
typeahead="destination.label for destination in destinationService.findDestinationsByName($viewValue)" typeahead-loading="loadingDestinations"
|
||||||
class="form-control input-md" typeahead-on-select="certificate.attachAccount($item)" typeahead-min-wait="50"
|
class="form-control input-md" typeahead-on-select="certificate.attachDestination($item)" typeahead-min-wait="50"
|
||||||
tooltip="Lemur can upload the certificate to any AWS account."
|
tooltip="Lemur can upload certificates to any pre-defined destination"
|
||||||
tooltip-trigger="focus" tooltip-placement="top">
|
tooltip-trigger="focus" tooltip-placement="top">
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<button ng-model="accounts.show" class="btn btn-md btn-default" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
|
<button ng-model="destinations.show" class="btn btn-md btn-default" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
|
||||||
<span class="badge">{{ certificate.accounts.length || 0 }}</span>
|
<span class="badge">{{ certificate.destinations.length || 0 }}</span>
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tr ng-repeat="account in certificate.accounts track by $index">
|
<tr ng-repeat="destination in certificate.destinations track by $index">
|
||||||
<td><a class="btn btn-sm btn-info" href="#/accounts/{{ account.id }}/certificates">{{ account.label }}</a></td>
|
<td><a class="btn btn-sm btn-info" href="#/destinations/{{ destination.id }}/certificates">{{ destination.label }}</a></td>
|
||||||
<td><span class="text-muted">{{ account.comments }}</span></td>
|
<td><span class="text-muted">{{ destination.description }}</span></td>
|
||||||
<td>
|
<td>
|
||||||
<button type="button" ng-click="certificate.removeAccount($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
<button type="button" ng-click="certificate.removeDestination($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<!--<div ng-show="certificate.accounts" class="form-group">
|
|
||||||
<label class="control-label col-sm-2">
|
|
||||||
ELBs
|
|
||||||
</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" ng-model="certificate.selectedELB" placeholder="ELB Name"
|
|
||||||
typeahead="elb.name for elb in elbService.findELBByName($viewValue)" typeahead-loading="loadingAccounts"
|
|
||||||
class="form-control col-md-4" typeahead-min-wait="50"
|
|
||||||
tooltip="Lemur can upload a certificate to one or more ELBs"
|
|
||||||
tooltip-trigger="focus" tooltip-placement="top"/>
|
|
||||||
<input class="form-control col-md-2" type="integer" ng-model="certificate.selectedPort" placeholder="Port"/>
|
|
||||||
<select class="form-control col-md-2" ng-options="item for item in ['https', 'tcp']"></select>
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button ng-click="certificate.attachELB()" class="btn btn-info">Add</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<table class="table">
|
|
||||||
<tr ng-repeat="elb in certificate.elbs track by $index">
|
|
||||||
<td><a class="btn btn-sm btn-info" href="#/accounts/{{ elb.id }}/elbs">{{ elb.name }}</a></td>
|
|
||||||
<td>{{ elb.region }}</td>
|
|
||||||
<td>{{ elb.scheme }}</td>
|
|
||||||
<td>{{ elb.vpcId }}</td>
|
|
||||||
<td>{{ elb.listener.scheme }}</td>
|
|
||||||
<td>{{ elb.listener.port }}</td>
|
|
||||||
<td>
|
|
||||||
<button type="button" ng-click="certificate.removeELB($index)" class="btn btn-danger btn-sm pull-right">remove</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>-->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
Common Name
|
Common Name
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input name="commonName" tooltip="If you need a certificate with multiple domains enter your primary domain here and the rest under 'Subject Alternate Names' in the options panel" ng-model="certificate.commonName" placeholder="Common Name" class="form-control" required/>
|
<input name="commonName" tooltip="If you need a certificate with multiple domains enter your primary domain here and the rest under 'Subject Alternate Names' in the next panel" ng-model="certificate.commonName" placeholder="Common Name" class="form-control" required/>
|
||||||
<p ng-show="trackingForm.commonName.$invalid && !trackingForm.commonName.$pristine" class="help-block">You must enter a common name</p>
|
<p ng-show="trackingForm.commonName.$invalid && !trackingForm.commonName.$pristine" class="help-block">You must enter a common name</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-center col-sm-2"><label><span class="glyphicon glyphicon-resize-horizontal"></span></label></span>
|
<span style="padding-top: 15px" class="text-center col-sm-2"><label><span class="glyphicon glyphicon-resize-horizontal"></span></label></span>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<div>
|
<div>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
|
|
@ -2,20 +2,16 @@
|
||||||
|
|
||||||
angular.module('lemur')
|
angular.module('lemur')
|
||||||
|
|
||||||
.config(function config($routeProvider) {
|
.controller('CertificateUploadController', function ($scope, $modalInstance, CertificateService, LemurRestangular, DestinationService, ELBService, PluginService) {
|
||||||
$routeProvider.when('/certificates/upload', {
|
|
||||||
templateUrl: '/angular/certificates/certificate/upload.tpl.html',
|
|
||||||
controller: 'CertificatesUploadController'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
.controller('CertificatesUploadController', function ($scope, CertificateService, LemurRestangular, AccountService, ELBService) {
|
|
||||||
$scope.certificate = LemurRestangular.restangularizeElement(null, {}, 'certificates');
|
$scope.certificate = LemurRestangular.restangularizeElement(null, {}, 'certificates');
|
||||||
$scope.upload = CertificateService.upload;
|
$scope.upload = CertificateService.upload;
|
||||||
|
|
||||||
$scope.accountService = AccountService;
|
$scope.destinationService = DestinationService;
|
||||||
$scope.elbService = ELBService;
|
$scope.elbService = ELBService;
|
||||||
|
|
||||||
|
PluginService.get('destination').then(function (plugins) {
|
||||||
|
$scope.plugins = plugins;
|
||||||
|
});
|
||||||
|
|
||||||
$scope.attachELB = function (elb) {
|
$scope.attachELB = function (elb) {
|
||||||
$scope.certificate.attachELB(elb);
|
$scope.certificate.attachELB(elb);
|
||||||
|
@ -23,4 +19,9 @@ angular.module('lemur')
|
||||||
$scope.certificate.elb.listeners = listeners;
|
$scope.certificate.elb.listeners = listeners;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.cancel = function () {
|
||||||
|
$modalInstance.dismiss('cancel');
|
||||||
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,136 +1,71 @@
|
||||||
<h2 class="featurette-heading">Upload a certificate <span class="text-muted"><small>encrypt all the things
|
<div class="modal-header">
|
||||||
</small></span></h2>
|
<div class="modal-title">
|
||||||
<div class="panel panel-default">
|
<h3 class="modal-header">Upload a certificate <span class="text-muted"><small>encrypt all the things</small></span></h3>
|
||||||
<div class="panel-heading">
|
</div>
|
||||||
<a href="/#/certificates/" class="btn btn-danger pull-right">Cancel</a>
|
<div class="modal-body">
|
||||||
<div class="clearfix"></div>
|
<form name="uploadForm" class="form-horizontal" role="form" novalidate>
|
||||||
</div>
|
<div class="form-group"
|
||||||
<div class="panel-body">
|
ng-class="{'has-error': uploadForm.owner.$invalid, 'has-success': !uploadForm.owner.$invalid&&uploadForm.owner.$dirty}">
|
||||||
<form name="uploadForm" class="form-horizontal" role="form" novalidate>
|
<label class="control-label col-sm-2">
|
||||||
<div class="form-group"
|
Owner
|
||||||
ng-class="{'has-error': uploadForm.owner.$invalid, 'has-success': !uploadForm.owner.$invalid&&uploadForm.owner.$dirty}">
|
</label>
|
||||||
<label class="control-label col-sm-2">
|
|
||||||
Owner
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="email" name="owner" ng-model="certificate.owner" placeholder="owner@netflix.com"
|
<input type="email" name="owner" ng-model="certificate.owner" placeholder="owner@netflix.com"
|
||||||
class="form-control" required/>
|
class="form-control" required/>
|
||||||
|
|
||||||
<p ng-show="uploadForm.owner.$invalid && !uploadForm.owner.$pristine" class="help-block">Enter a valid
|
<p ng-show="uploadForm.owner.$invalid && !uploadForm.owner.$pristine" class="help-block">Enter a valid
|
||||||
email.</p>
|
email.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group"
|
<div class="form-group"
|
||||||
ng-class="{'has-error': uploadForm.publicCert.$invalid, 'has-success': !uploadForm.publicCert.$invalid&&uploadForm.publicCert.$dirty}">
|
ng-class="{'has-error': uploadForm.publicCert.$invalid, 'has-success': !uploadForm.publicCert.$invalid&&uploadForm.publicCert.$dirty}">
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Public Certificate
|
Public Certificate
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea name="publicCert" ng-model="certificate.publicCert" placeholder="PEM encoded string..."
|
<textarea name="publicCert" ng-model="certificate.publicCert" placeholder="PEM encoded string..."
|
||||||
class="form-control" ng-pattern="/^-----BEGIN CERTIFICATE-----/" required></textarea>
|
class="form-control" ng-pattern="/^-----BEGIN CERTIFICATE-----/" required></textarea>
|
||||||
|
|
||||||
<p ng-show="uploadForm.publicCert.$invalid && !uploadForm.publicCert.$pristine" class="help-block">Enter
|
<p ng-show="uploadForm.publicCert.$invalid && !uploadForm.publicCert.$pristine" class="help-block">Enter
|
||||||
a valid certificate.</p>
|
a valid certificate.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group"
|
<div class="form-group"
|
||||||
ng-class="{'has-error': uploadForm.privateKey.$invalid&&uploadForm.privateKey.$dirty, 'has-success': !uploadForm.privateKey.$invalid&&uploadForm.privateKey.$dirty}">
|
ng-class="{'has-error': uploadForm.privateKey.$invalid&&uploadForm.privateKey.$dirty, 'has-success': !uploadForm.privateKey.$invalid&&uploadForm.privateKey.$dirty}">
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Private Key
|
Private Key
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea name="privateKey" ng-model="certificate.privateKey" placeholder="PEM encoded string..."
|
<textarea name="privateKey" ng-model="certificate.privateKey" placeholder="PEM encoded string..."
|
||||||
class="form-control" ng-pattern="/^-----BEGIN RSA PRIVATE KEY-----/"></textarea>
|
class="form-control" ng-pattern="/^-----BEGIN RSA PRIVATE KEY-----/"></textarea>
|
||||||
|
|
||||||
<p ng-show="uploadForm.privateKey.$invalid && !uploadForm.privateKey.$pristine" class="help-block">Enter
|
<p ng-show="uploadForm.privateKey.$invalid && !uploadForm.privateKey.$pristine" class="help-block">Enter
|
||||||
a valid certificate.</p>
|
a valid certificate.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group"
|
<div class="form-group"
|
||||||
ng-class="{'has-error': uploadForm.owner.$invalid&&uploadform.intermediateCert.$dirty, 'has-success': !uploadForm.intermediateCert.$invalid&&uploadForm.intermediateCert.$dirty}">
|
ng-class="{'has-error': uploadForm.owner.$invalid&&uploadform.intermediateCert.$dirty, 'has-success': !uploadForm.intermediateCert.$invalid&&uploadForm.intermediateCert.$dirty}">
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Intermediate Certificate
|
Intermediate Certificate
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<textarea name="intermediateCert" ng-model="certificate.intermediateCert"
|
<textarea name="intermediateCert" ng-model="certificate.intermediateCert"
|
||||||
placeholder="PEM encoded string..." class="form-control"
|
placeholder="PEM encoded string..." class="form-control"
|
||||||
ng-pattern="/^-----BEGIN CERTIFICATE-----/"></textarea>
|
ng-pattern="/^-----BEGIN CERTIFICATE-----/"></textarea>
|
||||||
|
|
||||||
<p ng-show="uploadForm.intermediateCert.$invalid && !uploadForm.intemediateCert.$pristine"
|
<p ng-show="uploadForm.intermediateCert.$invalid && !uploadForm.intemediateCert.$pristine"
|
||||||
class="help-block">Enter a valid certificate.</p>
|
class="help-block">Enter a valid certificate.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-sm-2">
|
|
||||||
Accounts
|
|
||||||
</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" ng-model="certificate.selectedAccount" placeholder="Account Name"
|
|
||||||
typeahead="account.label for account in accountService.findAccountsByName($viewValue)" typeahead-loading="loadingAccounts"
|
|
||||||
class="form-control" typeahead-on-select="certificate.attachAccount($item)" typeahead-min-wait="50"
|
|
||||||
tooltip="Lemur can upload the certificate to any AWS account."
|
|
||||||
tooltip-trigger="focus" tooltip-placement="top">
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button ng-model="certificate.accounts" class="btn btn-default" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
|
|
||||||
<span class="badge">{{ certificate.accounts.length || 0 }}</span>
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<table ng-show="certificate.accounts" class="table">
|
|
||||||
<tr ng-repeat="account in certificate.accounts track by $index">
|
|
||||||
<td><a class="btn btn-sm btn-info" href="#/accounts/{{ account.id }}/certificates">{{ account.label }}</a></td>
|
|
||||||
<td><span class="text-muted">{{ account.comments }}</span></td>
|
|
||||||
<td>
|
|
||||||
<button type="button" ng-click="certificate.removeAccount($index)" class="btn btn-danger btn-sm pull-right">
|
|
||||||
Remove
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--<div class="form-group">
|
|
||||||
<label class="control-label col-sm-2">
|
|
||||||
ELBs
|
|
||||||
</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" ng-model="certificate.selectedELB" placeholder="ELB Name"
|
|
||||||
typeahead="elb.name for elb in elbService.findELBByName($viewValue)" typeahead-loading="loadingAccounts"
|
|
||||||
class="form-control" typeahead-on-select="attachELB($item)" typeahead-min-wait="50"
|
|
||||||
tooltip="Lemur can upload a certificate to one or more ELBs searching will be limited to the accounts selected above"
|
|
||||||
tooltip-trigger="focus" tooltip-placement="top">
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button ng-model="certificate.elbs" class="btn btn-default" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
|
|
||||||
<span class="badge">{{ certificate.elbs.length || 0 }}</span>
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<table ng-show="certificate.elbs" class="table">
|
<div ng-include="'angular/certificates/certificate/destinations.tpl.html'"></div>
|
||||||
<tr ng-repeat="elb in certificate.elbs track by $index">
|
</form>
|
||||||
<td><a class="btn btn-sm btn-info" href="#/accounts/{{ elb.id }}/elbs">{{ elb.name }}</a></td>
|
</div>
|
||||||
<td>{{ elb.region }}</td>
|
<div class="modal-footer">
|
||||||
<td>{{ elb.scheme }}</td>
|
<button type="submit" ng-click="upload(certificate)" ng-disabled="uploadForm.$invalid" class="btn btn-success">Import</button>
|
||||||
<td>{{ elb.vpcId }}</td>
|
<button ng-click="cancel()" class="btn btn-danger">Cancel</button>
|
||||||
<td><a class="btn btn-info" ng-model="elb.showListeners" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">View Listeners</a></td>
|
</div>
|
||||||
<td>
|
|
||||||
<button type="button" ng-click="certificate.removeAccount($index)" class="btn btn-danger btn-sm pull-right">
|
|
||||||
Remove
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>-->
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="panel-footer">
|
|
||||||
<button type="submit" ng-click="upload(certificate)" ng-disabled="uploadForm.$invalid" class="btn btn-success pull-right">Upload</button>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -58,15 +58,15 @@ angular.module('lemur')
|
||||||
removeCustom: function (index) {
|
removeCustom: function (index) {
|
||||||
this.extensions.custom.splice(index, 1);
|
this.extensions.custom.splice(index, 1);
|
||||||
},
|
},
|
||||||
attachAccount: function (account) {
|
attachDestination: function (destination) {
|
||||||
this.selectedAccount = null;
|
this.selectedDestination = null;
|
||||||
if (this.accounts === undefined) {
|
if (this.destinations === undefined) {
|
||||||
this.accounts = [];
|
this.destinations = [];
|
||||||
}
|
}
|
||||||
this.accounts.push(account);
|
this.destinations.push(destination);
|
||||||
},
|
},
|
||||||
removeAccount: function (index) {
|
removeDestination: function (index) {
|
||||||
this.accounts.splice(index, 1);
|
this.destinations.splice(index, 1);
|
||||||
},
|
},
|
||||||
attachELB: function (elb) {
|
attachELB: function (elb) {
|
||||||
this.selectedELB = null;
|
this.selectedELB = null;
|
||||||
|
@ -99,13 +99,6 @@ angular.module('lemur')
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
CertificateService.getARNs = function (certificate) {
|
|
||||||
certificate.arns = [];
|
|
||||||
_.each(certificate.accounts, function (account) {
|
|
||||||
certificate.arns.push('arn:aws:iam::' + account.accountNumber + ':server-certificate/' + certificate.name);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
CertificateService.create = function (certificate) {
|
CertificateService.create = function (certificate) {
|
||||||
certificate.attachSubAltName();
|
certificate.attachSubAltName();
|
||||||
return CertificateApi.post(certificate).then(
|
return CertificateApi.post(certificate).then(
|
||||||
|
@ -191,10 +184,9 @@ angular.module('lemur')
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
CertificateService.getAccounts = function (certificate) {
|
CertificateService.getDestinations = function (certificate) {
|
||||||
certificate.getList('accounts').then(function (accounts) {
|
certificate.getList('destinations').then(function (destinations) {
|
||||||
certificate.accounts = accounts;
|
certificate.destinations = destinations;
|
||||||
CertificateService.getARNs(certificate);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ angular.module('lemur')
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
.controller('CertificatesViewController', function ($q, $scope, CertificateApi, CertificateService, MomentService, ngTableParams) {
|
.controller('CertificatesViewController', function ($q, $scope, $modal, CertificateApi, CertificateService, MomentService, ngTableParams) {
|
||||||
$scope.filter = {};
|
$scope.filter = {};
|
||||||
$scope.certificateTable = new ngTableParams({
|
$scope.certificateTable = new ngTableParams({
|
||||||
page: 1, // show first page
|
page: 1, // show first page
|
||||||
|
@ -26,7 +26,7 @@ angular.module('lemur')
|
||||||
// TODO we should attempt to resolve all of these in parallel
|
// TODO we should attempt to resolve all of these in parallel
|
||||||
_.each(data, function (certificate) {
|
_.each(data, function (certificate) {
|
||||||
CertificateService.getDomains(certificate);
|
CertificateService.getDomains(certificate);
|
||||||
CertificateService.getAccounts(certificate);
|
CertificateService.getDestinations(certificate);
|
||||||
CertificateService.getListeners(certificate);
|
CertificateService.getListeners(certificate);
|
||||||
CertificateService.getAuthority(certificate);
|
CertificateService.getAuthority(certificate);
|
||||||
CertificateService.getCreator(certificate);
|
CertificateService.getCreator(certificate);
|
||||||
|
@ -60,4 +60,30 @@ angular.module('lemur')
|
||||||
$scope.toggleFilter = function (params) {
|
$scope.toggleFilter = function (params) {
|
||||||
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.create = function () {
|
||||||
|
var modalInstance = $modal.open({
|
||||||
|
animation: true,
|
||||||
|
controller: 'CertificateCreateController',
|
||||||
|
templateUrl: '/angular/certificates/certificate/certificateWizard.tpl.html',
|
||||||
|
size: 'lg'
|
||||||
|
});
|
||||||
|
|
||||||
|
modalInstance.result.then(function () {
|
||||||
|
$scope.certificateTable.reload();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.import = function () {
|
||||||
|
var modalInstance = $modal.open({
|
||||||
|
animation: true,
|
||||||
|
controller: 'CertificateUploadController',
|
||||||
|
templateUrl: '/angular/certificates/certificate/upload.tpl.html',
|
||||||
|
size: 'lg'
|
||||||
|
});
|
||||||
|
|
||||||
|
modalInstance.result.then(function () {
|
||||||
|
$scope.certificateTable.reload();
|
||||||
|
});
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,14 +5,14 @@
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<div class="btn-group pull-right">
|
<div class="btn-group pull-right">
|
||||||
<a data-placement="left" data-title="Create Certificate" bs-tooltip href="#/certificates/create"
|
<button data-placement="left" data-title="Create Certificate" bs-tooltip ng-click="create()"
|
||||||
class="btn btn-primary">
|
class="btn btn-primary">
|
||||||
Create
|
Create
|
||||||
</a>
|
</button>
|
||||||
<a data-placement="left" data-title="Upload Certificate" bs-tooltip href="#/certificates/upload"
|
<button data-placement="left" data-title="Import Certificate" bs-tooltip ng-click="import()"
|
||||||
class="btn btn-info">
|
class="btn btn-info">
|
||||||
Upload
|
Import
|
||||||
</a>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button ng-click="toggleFilter(certificateTable)" class="btn btn-default">Filter</button>
|
<button ng-click="toggleFilter(certificateTable)" class="btn btn-default">Filter</button>
|
||||||
|
@ -30,9 +30,9 @@
|
||||||
<li><span class="text-muted">{{ certificate.owner }}</span></li>
|
<li><span class="text-muted">{{ certificate.owner }}</span></li>
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
<td data-title="'Accounts'" filter="{ 'account': 'select' }" filter-date="getAccountDropDown()">
|
<td data-title="'Destinations'" filter="{ 'destination': 'select' }" filter-date="getDestinationDropDown()">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<a href="#/accounts/{{ account.id }}/edit" class="btn btn-sm btn-default" ng-repeat="account in certificate.accounts">{{ account.label }}</a>
|
<a href="#/destinations/{{ destination.id }}/edit" class="btn btn-sm btn-default" ng-repeat="account in certificate.destinations">{{ destination.label }}</a>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td data-title="'Active'" filter="{ 'active': 'select' }" filter-data="getCertificateStatus()">
|
<td data-title="'Active'" filter="{ 'active': 'select' }" filter-data="getCertificateStatus()">
|
||||||
|
@ -106,7 +106,7 @@
|
||||||
<div class="list-group">
|
<div class="list-group">
|
||||||
<a href="#/domains/{{ domain.id }}" class="list-group-item" ng-repeat="domain in certificate.domains">{{ domain.name }}</a>
|
<a href="#/domains/{{ domain.id }}" class="list-group-item" ng-repeat="domain in certificate.domains">{{ domain.name }}</a>
|
||||||
</div>
|
</div>
|
||||||
<h4 ng-show="certificate.accounts.total">ARNs</h4>
|
<h4 ng-show="certificate.destinations.total">ARNs</h4>
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
<li class="list-group-item" ng-repeat="arn in certificate.arns">{{ arn }}</li>
|
<li class="list-group-item" ng-repeat="arn in certificate.arns">{{ arn }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
angular.module('lemur').
|
angular.module('lemur').
|
||||||
filter('titleCase', function () {
|
filter('titleCase', function () {
|
||||||
return function (str) {
|
return function (str) {
|
||||||
return (str === undefined || str === null) ? '' : str.replace(/_|-/, ' ').replace(/\w\S*/g, function (txt) {
|
return (str === undefined || str === null) ? '' : str.replace(/([A-Z])/g, ' $1').replace(/^./, function (txt) {
|
||||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
return txt.toUpperCase();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('lemur')
|
||||||
|
|
||||||
|
.controller('DestinationsCreateController', function ($scope, $modalInstance, PluginService, DestinationService, LemurRestangular){
|
||||||
|
$scope.destination = LemurRestangular.restangularizeElement(null, {}, 'destinations');
|
||||||
|
|
||||||
|
PluginService.get('destination').then(function (plugins) {
|
||||||
|
$scope.plugins = plugins;
|
||||||
|
});
|
||||||
|
$scope.save = function (destination) {
|
||||||
|
DestinationService.create(destination).then(function () {
|
||||||
|
$modalInstance.close();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.cancel = function () {
|
||||||
|
$modalInstance.dismiss('cancel');
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
.controller('DestinationsEditController', function ($scope, $modalInstance, DestinationService, DestinationApi, PluginService, editId) {
|
||||||
|
DestinationApi.get(editId).then(function (destination) {
|
||||||
|
$scope.destination = destination;
|
||||||
|
});
|
||||||
|
|
||||||
|
PluginService.get('destination').then(function (plugins) {
|
||||||
|
$scope.plugins = plugins;
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.save = function (destination) {
|
||||||
|
DestinationService.update(destination).then(function () {
|
||||||
|
$modalInstance.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.cancel = function () {
|
||||||
|
$modalInstance.dismiss('cancel');
|
||||||
|
};
|
||||||
|
});
|
|
@ -0,0 +1,56 @@
|
||||||
|
<div class="modal-header">
|
||||||
|
<div class="modal-title">
|
||||||
|
<h3 class="modal-header"><span ng-show="!destination.fromServer">Create</span><span ng-show="destination.fromServer">Edit</span> Destination <span class="text-muted"><small>oh the places you will go!</small></span></h3>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form name="createForm" class="form-horizontal" role="form" novalidate>
|
||||||
|
<div class="form-group"
|
||||||
|
ng-class="{'has-error': createForm.label.$invalid, 'has-success': !createForm.label.$invalid&&createForm.label.$dirty}">
|
||||||
|
<label class="control-label col-sm-2">
|
||||||
|
Label
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input name="label" ng-model="destination.label" placeholder="Label" class="form-control" required/>
|
||||||
|
<p ng-show="createForm.label.$invalid && !createForm.label.$pristine" class="help-block">You must enter an destination label</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">
|
||||||
|
Description
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<textarea name="comments" ng-model="destination.description" placeholder="Something elegant" class="form-control" ></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">
|
||||||
|
Plugin
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<select class="form-control" ng-model="destination.plugin" ng-options="plugin.title for plugin in plugins" required></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" ng-repeat="item in destination.plugin.pluginOptions">
|
||||||
|
<ng-form name="subForm" class="form-horizontal" role="form" novalidate>
|
||||||
|
<div ng-class="{'has-error': subForm.sub.$invalid, 'has-success': !subForm.sub.$invalid&&subForm.sub.$dirty}">
|
||||||
|
<label class="control-label col-sm-2">
|
||||||
|
{{ item.name | titleCase }}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input name="sub" ng-if="item.type == 'int'" type="number" ng-pattern="/^[0-9]{12,12}$/" class="form-control" ng-model="item.value"/>
|
||||||
|
<select name="sub" ng-if="item.type == 'select'" class="form-control" ng-options="i for i in item.available" ng-model="item.value"></select>
|
||||||
|
<input name="sub" ng-if="item.type == 'bool'" class="form-control" type="checkbox" ng-model="item.value">
|
||||||
|
<input name="sub" ng-if="item.type == 'str'" type="text" class="form-control" ng-model="item.value"/>
|
||||||
|
<p ng-show="subForm.sub.$invalid && !subForm.sub.$pristine" class="help-block">{{ item.helpMessage }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-form>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button ng-click="save(destination)" type="submit" ng-disabled="createForm.$invalid" class="btn btn-primary">Save</button>
|
||||||
|
<button ng-click="cancel()" class="btn btn-danger">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
'use strict';
|
||||||
|
angular.module('lemur')
|
||||||
|
.service('DestinationApi', function (LemurRestangular) {
|
||||||
|
return LemurRestangular.all('destinations');
|
||||||
|
})
|
||||||
|
.service('DestinationService', function ($location, DestinationApi, toaster) {
|
||||||
|
var DestinationService = this;
|
||||||
|
DestinationService.findDestinationsByName = function (filterValue) {
|
||||||
|
return DestinationApi.getList({'filter[label]': filterValue})
|
||||||
|
.then(function (destinations) {
|
||||||
|
return destinations;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
DestinationService.create = function (destination) {
|
||||||
|
return DestinationApi.post(destination).then(
|
||||||
|
function () {
|
||||||
|
toaster.pop({
|
||||||
|
type: 'success',
|
||||||
|
title: destination.label,
|
||||||
|
body: 'Successfully created!'
|
||||||
|
});
|
||||||
|
$location.path('destinations');
|
||||||
|
},
|
||||||
|
function (response) {
|
||||||
|
toaster.pop({
|
||||||
|
type: 'error',
|
||||||
|
title: destination.label,
|
||||||
|
body: 'Was not created! ' + response.data.message
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
DestinationService.update = function (destination) {
|
||||||
|
return destination.put().then(
|
||||||
|
function () {
|
||||||
|
toaster.pop({
|
||||||
|
type: 'success',
|
||||||
|
title: destination.label,
|
||||||
|
body: 'Successfully updated!'
|
||||||
|
});
|
||||||
|
$location.path('destinations');
|
||||||
|
},
|
||||||
|
function (response) {
|
||||||
|
toaster.pop({
|
||||||
|
type: 'error',
|
||||||
|
title: destination.label,
|
||||||
|
body: 'Was not updated! ' + response.data.message
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return DestinationService;
|
||||||
|
});
|
|
@ -0,0 +1,85 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('lemur')
|
||||||
|
|
||||||
|
.config(function config($routeProvider) {
|
||||||
|
$routeProvider.when('/destinations', {
|
||||||
|
templateUrl: '/angular/destinations/view/view.tpl.html',
|
||||||
|
controller: 'DestinationsViewController'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.controller('DestinationsViewController', function ($scope, $modal, DestinationApi, DestinationService, ngTableParams, toaster) {
|
||||||
|
$scope.filter = {};
|
||||||
|
$scope.destinationsTable = new ngTableParams({
|
||||||
|
page: 1, // show first page
|
||||||
|
count: 10, // count per page
|
||||||
|
sorting: {
|
||||||
|
id: 'desc' // initial sorting
|
||||||
|
},
|
||||||
|
filter: $scope.filter
|
||||||
|
}, {
|
||||||
|
total: 0, // length of data
|
||||||
|
getData: function ($defer, params) {
|
||||||
|
DestinationApi.getList(params.url()).then(
|
||||||
|
function (data) {
|
||||||
|
params.total(data.total);
|
||||||
|
$defer.resolve(data);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.remove = function (destination) {
|
||||||
|
destination.remove().then(
|
||||||
|
function () {
|
||||||
|
$scope.destinationsTable.reload();
|
||||||
|
},
|
||||||
|
function (response) {
|
||||||
|
toaster.pop({
|
||||||
|
type: 'error',
|
||||||
|
title: 'Opps',
|
||||||
|
body: 'I see what you did there' + response.data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.edit = function (destinationId) {
|
||||||
|
var modalInstance = $modal.open({
|
||||||
|
animation: true,
|
||||||
|
templateUrl: '/angular/destinations/destination/destination.tpl.html',
|
||||||
|
controller: 'DestinationsEditController',
|
||||||
|
size: 'lg',
|
||||||
|
resolve: {
|
||||||
|
editId: function () {
|
||||||
|
return destinationId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
modalInstance.result.then(function () {
|
||||||
|
$scope.destinationsTable.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.create = function () {
|
||||||
|
var modalInstance = $modal.open({
|
||||||
|
animation: true,
|
||||||
|
controller: 'DestinationsCreateController',
|
||||||
|
templateUrl: '/angular/destinations/destination/destination.tpl.html',
|
||||||
|
size: 'lg'
|
||||||
|
});
|
||||||
|
|
||||||
|
modalInstance.result.then(function () {
|
||||||
|
$scope.destinationsTable.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.toggleFilter = function (params) {
|
||||||
|
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,47 @@
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<h2 class="featurette-heading">Destinations
|
||||||
|
<span class="text-muted"><small>oh the places you will go</small></span></h2>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<div class="btn-group pull-right">
|
||||||
|
<button ng-click="create()" class="btn btn-primary">Create</button>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group">
|
||||||
|
<button ng-click="toggleFilter(destinationsTable)" class="btn btn-default">Filter</button>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table ng-table="destinationsTable" class="table table-striped" show-filter="false" template-pagination="angular/pager.html" >
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="destination in $data track by $index">
|
||||||
|
<td data-title="'Label'" sortable="'label'" filter="{ 'label': 'text' }">
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li>{{ destination.label }}</li>
|
||||||
|
<li><span class="text-muted">{{ destination.description }}</span></li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
<td data-title="'Plugin'">
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li>{{ destination.plugin.title }}</li>
|
||||||
|
<li><span class="text-muted">{{ destination.plugin.description }}</span></li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
<td data-title="''">
|
||||||
|
<div class="btn-group-vertical pull-right">
|
||||||
|
<button tooltip="Edit Destination" ng-click="edit(destination.id)" class="btn btn-sm btn-info">
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
<button tooltip="Delete Destination" ng-click="remove(destination)" type="button" class="btn btn-sm btn-danger pull-left">
|
||||||
|
Remove
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,6 +0,0 @@
|
||||||
<div tabindex="-1" role="dialog" class="modal fade" ng-class="{in: animate}" ng-style="{'z-index': 1050 + index*10, display: 'block'}">
|
|
||||||
<div class="modal-dialog-center">
|
|
||||||
<div modal-transclude>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -2,29 +2,37 @@
|
||||||
|
|
||||||
angular.module('lemur')
|
angular.module('lemur')
|
||||||
|
|
||||||
.config(function config($routeProvider) {
|
.controller('RolesEditController', function ($scope, $modalInstance, RoleApi, RoleService, UserService, editId) {
|
||||||
$routeProvider.when('/roles/create', {
|
RoleApi.get(editId).then(function (role) {
|
||||||
templateUrl: '/angular/roles/role/role.tpl.html',
|
|
||||||
controller: 'RoleCreateController'
|
|
||||||
});
|
|
||||||
$routeProvider.when('/roles/:id/edit', {
|
|
||||||
templateUrl: '/angular/roles/role/role.tpl.html',
|
|
||||||
controller: 'RoleEditController'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.controller('RoleEditController', function ($scope, $routeParams, RoleApi, RoleService, UserService) {
|
|
||||||
RoleApi.get($routeParams.id).then(function (role) {
|
|
||||||
$scope.role = role;
|
$scope.role = role;
|
||||||
RoleService.getUsers(role);
|
RoleService.getUsers(role);
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.save = RoleService.update;
|
$scope.save = function (role) {
|
||||||
|
RoleService.update(role).then(function () {
|
||||||
|
$modalInstance.close();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.cancel = function () {
|
||||||
|
$modalInstance.dismiss('cancel');
|
||||||
|
};
|
||||||
|
|
||||||
$scope.userService = UserService;
|
$scope.userService = UserService;
|
||||||
$scope.roleService = RoleService;
|
$scope.roleService = RoleService;
|
||||||
})
|
})
|
||||||
|
|
||||||
.controller('RoleCreateController', function ($scope, RoleApi, RoleService, UserService, LemurRestangular ) {
|
.controller('RolesCreateController', function ($scope,$modalInstance, RoleApi, RoleService, UserService, LemurRestangular) {
|
||||||
$scope.role = LemurRestangular.restangularizeElement(null, {}, 'roles');
|
$scope.role = LemurRestangular.restangularizeElement(null, {}, 'roles');
|
||||||
$scope.userService = UserService;
|
$scope.userService = UserService;
|
||||||
$scope.save = RoleService.create;
|
|
||||||
|
$scope.save = function (role) {
|
||||||
|
RoleService.create(role).then(function () {
|
||||||
|
$modalInstance.close();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.cancel = function () {
|
||||||
|
$modalInstance.dismiss('cancel');
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,85 +1,82 @@
|
||||||
<h2 class="featurette-heading"><span ng-show="!role.fromServer">Create</span><span ng-show="role.fromServer">Edit</span> Role <span class="text-muted"><small>The nail that sticks out farthest gets hammered the hardest
|
<div class="modal-header">
|
||||||
</small></span></h2>
|
<div class="modal-title">
|
||||||
<div class="panel panel-default">
|
<h3 class="modal-header"><span ng-show="!role.fromServer">Create</span><span ng-show="role.fromServer">Edit</span> Role <span class="text-muted"><small>The nail that sticks out farthest gets hammered the hardest</small></span></h3>
|
||||||
<div class="panel-heading">
|
</div>
|
||||||
<button ng-click="roleService.loadPassword(role)" class="btn btn-warning">Show Credentials</button>
|
<div class="modal-body">
|
||||||
<a href="/#/roles" class="btn btn-danger pull-right">Cancel</a>
|
<form name="createForm" class="form-horizontal" ng-submit="save(role)" role="form" novalidate>
|
||||||
<div class="clearfix"></div>
|
<div class="form-group"
|
||||||
</div>
|
ng-class="{'has-error': createForm.name.$invalid, 'has-success': !createForm.name.$invalid&&createForm.name.$dirty}">
|
||||||
<div class="panel-body">
|
<label class="control-label col-sm-2">
|
||||||
<form name="createForm" class="form-horizontal" ng-submit="save(role)" role="form" novalidate>
|
Name
|
||||||
<div class="form-group"
|
</label>
|
||||||
ng-class="{'has-error': createForm.name.$invalid, 'has-success': !createForm.name.$invalid&&createForm.name.$dirty}">
|
<div class="col-sm-10">
|
||||||
<label class="control-label col-sm-2">
|
<input name="name" ng-model="role.name" placeholder="Name" class="form-control" required/>
|
||||||
Name
|
<p ng-show="createForm.name.$invalid && !createForm.name.$pristine" class="help-block">You must enter an role name</p>
|
||||||
</label>
|
</div>
|
||||||
<div class="col-sm-10">
|
</div>
|
||||||
<input name="name" ng-model="role.name" placeholder="Name" class="form-control" required/>
|
<div class="form-group">
|
||||||
<p ng-show="createForm.name.$invalid && !createForm.name.$pristine" class="help-block">You must enter an role name</p>
|
<label class="control-label col-sm-2">
|
||||||
</div>
|
Description
|
||||||
</div>
|
</label>
|
||||||
<div class="form-group">
|
<div class="col-sm-10">
|
||||||
<label class="control-label col-sm-2">
|
<textarea name="description" ng-model="role.description" placeholder="Something elegant" class="form-control" ></textarea>
|
||||||
Description
|
</div>
|
||||||
</label>
|
</div>
|
||||||
<div class="col-sm-10">
|
<div class="form-group">
|
||||||
<textarea name="description" ng-model="role.description" placeholder="Something elegant" class="form-control" ></textarea>
|
<label class="control-label col-sm-2">
|
||||||
</div>
|
Username
|
||||||
</div>
|
</label>
|
||||||
<div class="form-group">
|
<div class="col-sm-10">
|
||||||
<label class="control-label col-sm-2">
|
<input ng-show="!role.fromServer" name="username" ng-model="role.username" placeholder="Username" class="form-control" required/>
|
||||||
Username
|
<div class="well">
|
||||||
</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input ng-show="!role.fromServer" name="username" ng-model="role.username" placeholder="Username" class="form-control" required/>
|
|
||||||
<div class="well">
|
|
||||||
<span ng-show="role.password">
|
<span ng-show="role.password">
|
||||||
{{ role.username }}
|
{{ role.username }}
|
||||||
</span>
|
</span>
|
||||||
<span ng-show="!role.password">
|
<span ng-show="!role.password">
|
||||||
******************
|
******************
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2">
|
<label class="control-label col-sm-2">
|
||||||
Password
|
Password
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input ng-show="!role.fromServer" type="password" name="password" ng-model="role.password" placeholder="hunter2" class="form-control" required/>
|
<input ng-show="!role.fromServer" type="password" name="password" ng-model="role.password" placeholder="hunter2" class="form-control" required/>
|
||||||
<p ng-show="createForm.password.$invalid && !createForm.password.$pristine" class="help-block">You must enter an password</p>
|
<p ng-show="createForm.password.$invalid && !createForm.password.$pristine" class="help-block">You must enter an password</p>
|
||||||
<div class="well">
|
<div class="well">
|
||||||
<span ng-show="role.password">
|
<span ng-show="role.password">
|
||||||
{{ role.password }}
|
{{ role.password }}
|
||||||
</span>
|
</span>
|
||||||
<span ng-show="!role.password">
|
<span ng-show="!role.password">
|
||||||
*****************
|
*****************
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">User(s)</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input tooltip="You can attach any user to this role, once attached they will have access as defined by this role"
|
||||||
|
typeahead="user.username for user in userService.findUserByName($viewValue)" typeahead-loading="loadingUsers"
|
||||||
|
typeahead-min-wait="100" typeahead-on-select="role.addUser($item)"
|
||||||
|
type="text" name="user" ng-model="role.selectedUser" placeholder="Username..." class="form-control" required/>
|
||||||
|
<table ng-show="role.users" class="table">
|
||||||
|
<tr ng-repeat="user in role.users track by $index">
|
||||||
|
<td>{{ user.username }}</td>
|
||||||
|
<td>
|
||||||
|
<button type="button" ng-click="role.removeUser($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button ng-click="roleService.loadPassword(role)" class="btn btn-warning pull-left">Show Credentials</button>
|
||||||
|
<button ng-click="save(role)" type="submit" ng-disabled="createForm.$invalid" class="btn btn-primary">Save</button>
|
||||||
|
<button ng-click="cancel()" class="btn btn-danger">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-sm-2">User(s)</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input tooltip="You can attach any user to this role, once attached they will have access as defined by this role"
|
|
||||||
typeahead="user.username for user in userService.findUserByName($viewValue)" typeahead-loading="loadingUsers"
|
|
||||||
typeahead-min-wait="100" typeahead-on-select="role.addUser($item)"
|
|
||||||
type="text" name="user" ng-model="role.selectedUser" placeholder="Username..." class="form-control" required/>
|
|
||||||
<table ng-show="role.users" class="table">
|
|
||||||
<tr ng-repeat="user in role.users track by $index">
|
|
||||||
<td>{{ user.username }}</td>
|
|
||||||
<td>
|
|
||||||
<button type="button" ng-click="role.removeUser($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="panel-footer">
|
|
||||||
<button ng-click="save(role)" class="btn btn-success pull-right">Save</button>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -38,7 +38,7 @@ angular.module('lemur')
|
||||||
};
|
};
|
||||||
|
|
||||||
RoleService.create = function (role) {
|
RoleService.create = function (role) {
|
||||||
RoleApi.post(role).then(
|
return RoleApi.post(role).then(
|
||||||
function () {
|
function () {
|
||||||
toaster.pop({
|
toaster.pop({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
@ -57,7 +57,7 @@ angular.module('lemur')
|
||||||
};
|
};
|
||||||
|
|
||||||
RoleService.update = function (role) {
|
RoleService.update = function (role) {
|
||||||
role.put().then(
|
return role.put().then(
|
||||||
function () {
|
function () {
|
||||||
toaster.pop({
|
toaster.pop({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
|
|
@ -9,7 +9,7 @@ angular.module('lemur')
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
.controller('RolesViewController', function ($scope, RoleApi, RoleService, ngTableParams) {
|
.controller('RolesViewController', function ($scope, $modal, RoleApi, RoleService, ngTableParams) {
|
||||||
$scope.filter = {};
|
$scope.filter = {};
|
||||||
$scope.rolesTable = new ngTableParams({
|
$scope.rolesTable = new ngTableParams({
|
||||||
page: 1, // show first page
|
page: 1, // show first page
|
||||||
|
@ -39,4 +39,38 @@ angular.module('lemur')
|
||||||
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
$scope.edit = function (roleId) {
|
||||||
|
var modalInstance = $modal.open({
|
||||||
|
animation: true,
|
||||||
|
templateUrl: '/angular/roles/role/role.tpl.html',
|
||||||
|
controller: 'RolesEditController',
|
||||||
|
size: 'lg',
|
||||||
|
resolve: {
|
||||||
|
editId: function () {
|
||||||
|
return roleId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
modalInstance.result.then(function () {
|
||||||
|
$scope.rolesTable.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.create = function () {
|
||||||
|
var modalInstance = $modal.open({
|
||||||
|
animation: true,
|
||||||
|
controller: 'RolesCreateController',
|
||||||
|
templateUrl: '/angular/roles/role/role.tpl.html',
|
||||||
|
size: 'lg'
|
||||||
|
});
|
||||||
|
|
||||||
|
modalInstance.result.then(function () {
|
||||||
|
$scope.rolesTable.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<div class="btn-group pull-right">
|
<div class="btn-group pull-right">
|
||||||
<a data-placement="left" data-title="Create Role" bs-tooltip href="#/roles/create" class="btn btn-primary">Create</a>
|
<button ng-click="create()" class="btn btn-primary">Create</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button ng-click="toggleFilter(rolesTable)" class="btn btn-default">Filter</button>
|
<button ng-click="toggleFilter(rolesTable)" class="btn btn-default">Filter</button>
|
||||||
|
@ -24,9 +24,9 @@
|
||||||
</td>
|
</td>
|
||||||
<td data-title="''">
|
<td data-title="''">
|
||||||
<div class="btn-group-vertical pull-right">
|
<div class="btn-group-vertical pull-right">
|
||||||
<a href="#/roles/{{ role.id }}/edit" class="btn btn-sm btn-info">
|
<button ng-click="edit(role.id)" class="btn btn-sm btn-info">
|
||||||
Edit
|
Edit
|
||||||
</a>
|
</button>
|
||||||
<a ng-click="remove(role)" class="btn btn-sm btn-danger">
|
<a ng-click="remove(role)" class="btn btn-sm btn-danger">
|
||||||
Remove
|
Remove
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -50,7 +50,7 @@ angular.module('lemur')
|
||||||
};
|
};
|
||||||
|
|
||||||
UserService.create = function (user) {
|
UserService.create = function (user) {
|
||||||
UserApi.post(user).then(
|
return UserApi.post(user).then(
|
||||||
function () {
|
function () {
|
||||||
toaster.pop({
|
toaster.pop({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
@ -69,7 +69,7 @@ angular.module('lemur')
|
||||||
};
|
};
|
||||||
|
|
||||||
UserService.update = function (user) {
|
UserService.update = function (user) {
|
||||||
user.put().then(
|
return user.put().then(
|
||||||
function () {
|
function () {
|
||||||
toaster.pop({
|
toaster.pop({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
|
|
@ -2,19 +2,8 @@
|
||||||
|
|
||||||
angular.module('lemur')
|
angular.module('lemur')
|
||||||
|
|
||||||
.config(function config($routeProvider) {
|
.controller('UsersEditController', function ($scope, $modalInstance, UserApi, UserService, RoleService, editId) {
|
||||||
$routeProvider.when('/users/create', {
|
UserApi.get(editId).then(function (user) {
|
||||||
templateUrl: '/angular/users/user/user.tpl.html',
|
|
||||||
controller: 'UsersCreateController'
|
|
||||||
});
|
|
||||||
$routeProvider.when('/users/:id/edit', {
|
|
||||||
templateUrl: '/angular/users/user/user.tpl.html',
|
|
||||||
controller: 'UsersEditController'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
.controller('UsersEditController', function ($scope, $routeParams, UserApi, UserService, RoleService) {
|
|
||||||
UserApi.get($routeParams.id).then(function (user) {
|
|
||||||
UserService.getRoles(user);
|
UserService.getRoles(user);
|
||||||
$scope.user = user;
|
$scope.user = user;
|
||||||
});
|
});
|
||||||
|
@ -24,15 +13,36 @@ angular.module('lemur')
|
||||||
|
|
||||||
$scope.rolePage = 1;
|
$scope.rolePage = 1;
|
||||||
|
|
||||||
|
|
||||||
|
$scope.save = function (user) {
|
||||||
|
UserService.update(user).then(function () {
|
||||||
|
$modalInstance.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.cancel = function () {
|
||||||
|
$modalInstance.dismiss('cancel');
|
||||||
|
};
|
||||||
|
|
||||||
$scope.loadMoreRoles = function () {
|
$scope.loadMoreRoles = function () {
|
||||||
$scope.rolePage += 1;
|
$scope.rolePage += 1;
|
||||||
UserService.loadMoreRoles($scope.user, $scope.rolePage);
|
UserService.loadMoreRoles($scope.user, $scope.rolePage);
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
.controller('UsersCreateController', function ($scope, UserService, LemurRestangular, RoleService) {
|
.controller('UsersCreateController', function ($scope, $modalInstance, UserService, LemurRestangular, RoleService) {
|
||||||
$scope.user = LemurRestangular.restangularizeElement(null, {}, 'users');
|
$scope.user = LemurRestangular.restangularizeElement(null, {}, 'users');
|
||||||
$scope.save = UserService.create;
|
$scope.save = UserService.create;
|
||||||
$scope.roleService = RoleService;
|
$scope.roleService = RoleService;
|
||||||
|
|
||||||
|
$scope.create = function (user) {
|
||||||
|
UserService.create(user).then(function () {
|
||||||
|
$modalInstance.close();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.cancel = function () {
|
||||||
|
$modalInstance.dismiss('cancel');
|
||||||
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,89 +1,86 @@
|
||||||
<h2 class="featurette-heading"><span ng-show="!user.fromServer">Create</span><span ng-show="user.fromServer">Edit</span> User <span class="text-muted"><small>what was your name again?
|
<div class="modal-header">
|
||||||
</small></span></h2>
|
<div class="modal-title">
|
||||||
<div class="panel panel-default">
|
<h3 class="modal-header"><span ng-show="!user.fromServer">Create</span><span ng-show="user.fromServer">Edit</span> User <span class="text-muted"><small>what was your name again?</small></span></h3>
|
||||||
<div class="panel-heading">
|
</div>
|
||||||
<a href="#/users" class="btn btn-danger pull-right">Cancel</a>
|
<div class="modal-body">
|
||||||
<div class="clearfix"></div>
|
<form name="createForm" class="form-horizontal" role="form" novalidate>
|
||||||
</div>
|
<div class="form-group"
|
||||||
<div class="panel-body">
|
ng-class="{'has-error': createForm.username.$invalid, 'has-success': !createForm.username.$invalid&&createForm.username.$dirty}">
|
||||||
<form name="createForm" class="form-horizontal" role="form" novalidate>
|
<label class="control-label col-sm-2">
|
||||||
<div class="form-group"
|
Name
|
||||||
ng-class="{'has-error': createForm.username.$invalid, 'has-success': !createForm.username.$invalid&&createForm.username.$dirty}">
|
</label>
|
||||||
<label class="control-label col-sm-2">
|
<div class="col-sm-10">
|
||||||
Name
|
<input name="username" ng-model="user.username" placeholder="Name" class="form-control" required/>
|
||||||
</label>
|
<p ng-show="createForm.username.$invalid && !createForm.username.$pristine" class="help-block">You must enter a username</p>
|
||||||
<div class="col-sm-10">
|
</div>
|
||||||
<input name="username" ng-model="user.username" placeholder="Name" class="form-control" required/>
|
</div>
|
||||||
<p ng-show="createForm.username.$invalid && !createForm.username.$pristine" class="help-block">You must enter a username</p>
|
<div class="form-group"
|
||||||
</div>
|
ng-class="{'has-error': createForm.email.$invalid, 'has-success': !createForm.email.$invalid&&createForm.email.$dirty}">
|
||||||
</div>
|
<label class="control-label col-sm-2">
|
||||||
<div class="form-group"
|
Email
|
||||||
ng-class="{'has-error': createForm.email.$invalid, 'has-success': !createForm.email.$invalid&&createForm.email.$dirty}">
|
</label>
|
||||||
<label class="control-label col-sm-2">
|
<div class="col-sm-10">
|
||||||
Email
|
<input type="email" name="email" ng-model="user.email" placeholder="hi@example.com" class="form-control" required/>
|
||||||
</label>
|
<p ng-show="createForm.email.$invalid && !createForm.email.$pristine" class="help-block">You must enter an email</p>
|
||||||
<div class="col-sm-10">
|
</div>
|
||||||
<input type="email" name="email" ng-model="user.email" placeholder="hi@example.com" class="form-control" required/>
|
</div>
|
||||||
<p ng-show="createForm.email.$invalid && !createForm.email.$pristine" class="help-block">You must enter an email</p>
|
<div ng-if="!user.id" class="form-group"
|
||||||
</div>
|
ng-class="{'has-error': createForm.password.$invalid, 'has-success': !createForm.password.$invalid&&createForm.password.$dirty}">
|
||||||
</div>
|
<label class="control-label col-sm-2">
|
||||||
<div ng-if="!user.id" class="form-group"
|
Password
|
||||||
ng-class="{'has-error': createForm.password.$invalid, 'has-success': !createForm.password.$invalid&&createForm.password.$dirty}">
|
</label>
|
||||||
<label class="control-label col-sm-2">
|
<div class="col-sm-10">
|
||||||
Password
|
<input type="password" name="password" ng-model="user.password" placeholder="hunter2" class="form-control" required/>
|
||||||
</label>
|
<p ng-show="createForm.password.$invalid && !createForm.password.$pristine" class="help-block">You must enter an password</p>
|
||||||
<div class="col-sm-10">
|
</div>
|
||||||
<input type="password" name="password" ng-model="user.password" placeholder="hunter2" class="form-control" required/>
|
</div>
|
||||||
<p ng-show="createForm.password.$invalid && !createForm.password.$pristine" class="help-block">You must enter an password</p>
|
<div class="form-group">
|
||||||
</div>
|
<label class="control-label col-sm-2">
|
||||||
</div>
|
Active
|
||||||
<div class="form-group">
|
</label>
|
||||||
<label class="control-label col-sm-2">
|
<div class="col-sm-10">
|
||||||
Active
|
<div class="checkbox col-sm-10">
|
||||||
</label>
|
<switch ng-model="user.active" id="active" name="active" ng-init="user.active=true" class="green small"></switch>
|
||||||
<div class="col-sm-10">
|
</div>
|
||||||
<div class="checkbox col-sm-10">
|
</div>
|
||||||
<input ng-model="user.active" type="checkbox" value="true">
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
</div>
|
<label class="control-label col-sm-2">
|
||||||
</div>
|
Roles
|
||||||
<div class="form-group">
|
</label>
|
||||||
<label class="control-label col-sm-2">
|
<div class="col-sm-10">
|
||||||
Roles
|
<div class="input-group">
|
||||||
</label>
|
<input type="text" ng-model="user.selectedRole" placeholder="Role Name"
|
||||||
<div class="col-sm-10">
|
typeahead="role.name for role in roleService.findRoleByName($viewValue)" typeahead-loading="loadingRoles"
|
||||||
<div class="input-group">
|
class="form-control input-md" typeahead-on-select="user.attachRole($item)" typeahead-min-wait="50"
|
||||||
<input type="text" ng-model="user.selectedRole" placeholder="Role Name"
|
tooltip="Roles control which authorities a user can issue certificates from"
|
||||||
typeahead="role.name for role in roleService.findRoleByName($viewValue)" typeahead-loading="loadingRoles"
|
tooltip-trigger="focus" tooltip-placement="top">
|
||||||
class="form-control input-md" typeahead-on-select="user.attachRole($item)" typeahead-min-wait="50"
|
|
||||||
tooltip="Roles control which authorities a user can issue certificates from"
|
|
||||||
tooltip-trigger="focus" tooltip-placement="top">
|
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<button ng-model="roles.show" class="btn btn-md btn-default" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
|
<button ng-model="roles.show" class="btn btn-md btn-default" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
|
||||||
<span class="badge">{{ user.roles.total || 0 }}</span>
|
<span class="badge">{{ user.roles.total || 0 }}</span>
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<table ng-show="user.roles" class="table">
|
<table ng-show="user.roles" class="table">
|
||||||
<tr ng-repeat="role in user.roles track by $index">
|
<tr ng-repeat="role in user.roles track by $index">
|
||||||
<td><a class="btn btn-sm btn-info" href="#/roles/{{ role.id }}/edit">{{ role.name }}</a></td>
|
<td><a class="btn btn-sm btn-info" href="#/roles/{{ role.id }}/edit">{{ role.name }}</a></td>
|
||||||
<td><span class="text-muted">{{ role.description }}</span></td>
|
<td><span class="text-muted">{{ role.description }}</span></td>
|
||||||
<td>
|
<td>
|
||||||
<button type="button" ng-click="user.removeRole($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
<button type="button" ng-click="user.removeRole($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td><a class="pull-right" ng-click="loadMoreRoles()"><strong>More</strong></a></td>
|
<td><a class="pull-right" ng-click="loadMoreRoles()"><strong>More</strong></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button ng-click="save(user)" type="submit" ng-disabled="createForm.$invalid" class="btn btn-primary">Save</button>
|
||||||
|
<button ng-click="cancel()" class="btn btn-danger">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="panel-footer">
|
|
||||||
<button ng-click="save(user)" class="btn btn-success pull-right">Save</button>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,7 +9,7 @@ angular.module('lemur')
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
.controller('UsersViewController', function ($scope, UserApi, UserService, ngTableParams) {
|
.controller('UsersViewController', function ($scope, $modal, UserApi, UserService, ngTableParams) {
|
||||||
$scope.filter = {};
|
$scope.filter = {};
|
||||||
$scope.usersTable = new ngTableParams({
|
$scope.usersTable = new ngTableParams({
|
||||||
page: 1, // show first page
|
page: 1, // show first page
|
||||||
|
@ -36,6 +36,39 @@ angular.module('lemur')
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.edit = function (userId) {
|
||||||
|
var modalInstance = $modal.open({
|
||||||
|
animation: true,
|
||||||
|
templateUrl: '/angular/users/user/user.tpl.html',
|
||||||
|
controller: 'UsersEditController',
|
||||||
|
size: 'lg',
|
||||||
|
resolve: {
|
||||||
|
editId: function () {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
modalInstance.result.then(function () {
|
||||||
|
$scope.usersTable.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.create = function () {
|
||||||
|
var modalInstance = $modal.open({
|
||||||
|
animation: true,
|
||||||
|
controller: 'UsersCreateController',
|
||||||
|
templateUrl: '/angular/users/user/user.tpl.html',
|
||||||
|
size: 'lg'
|
||||||
|
});
|
||||||
|
|
||||||
|
modalInstance.result.then(function () {
|
||||||
|
$scope.usersTable.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
$scope.toggleFilter = function (params) {
|
$scope.toggleFilter = function (params) {
|
||||||
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<div class="btn-group pull-right">
|
<div class="btn-group pull-right">
|
||||||
<a href="#/users/create" class="btn btn-primary">Create</a>
|
<button ng-click="create()" class="btn btn-primary">Create</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button ng-click="toggleFilter(usersTable)" class="btn btn-default">Filter</button>
|
<button ng-click="toggleFilter(usersTable)" class="btn btn-default">Filter</button>
|
||||||
|
@ -29,9 +29,9 @@
|
||||||
</td>
|
</td>
|
||||||
<td data-title="''">
|
<td data-title="''">
|
||||||
<div class="btn-group-vertical pull-right">
|
<div class="btn-group-vertical pull-right">
|
||||||
<a tooltip="Edit User" href="#/users/{{ user.id }}/edit" class="btn btn-sm btn-info">
|
<button tooltip="Edit User" ng-click="edit(user.id)" class="btn btn-sm btn-info">
|
||||||
Edit
|
Edit
|
||||||
</a>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,23 +1,12 @@
|
||||||
<div>
|
<div>
|
||||||
<div class="panel panel-default">
|
<div class="modal-body">
|
||||||
<div class="panel-heading">
|
|
||||||
<ul class="steps-indicator steps-{{steps.length}}" ng-if="!hideIndicators">
|
|
||||||
<li ng-class="{default: !step.completed && !step.selected, current: step.selected && !step.completed, done: step.completed && !step.selected, editing: step.selected && step.completed}" ng-repeat="step in steps">
|
|
||||||
<a ng-click="goTo(step)">{{step.title || step.wzTitle}}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div class="steps" ng-transclude></div>
|
<div class="steps" ng-transclude></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-footer">
|
<div class="modal-footer">
|
||||||
<input ng-hide="currentStepNumber() == 1" class="btn btn-default pull-left" type="submit" wz-previous value="Previous" />
|
<input ng-hide="currentStepNumber() == 1" class="btn btn-default pull-left" type="submit" wz-previous value="Previous" />
|
||||||
<input ng-show="currentStepNumber() != steps.length" class="btn btn-default pull-right" type="submit" wz-next value="Next" />
|
<input ng-show="currentStepNumber() != steps.length" class="btn btn-default pull-right" type="submit" wz-next value="Next" />
|
||||||
<button ng-show="currentStepNumber() == steps.length" class="btn btn-success pull-right" type="submit" wz-next>
|
<input ng-show="!context.loading" class="btn btn-success pull-right" type="submit" wz-finish value="Create" />
|
||||||
Create
|
<button ng-show="context.loading" class="btn btn-success pull-right disabled"><wave-spinner></wave-spinner></button>
|
||||||
</button>
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
<li data-match-route="/domains"><a href="/#/domains">Domains</a></li>
|
<li data-match-route="/domains"><a href="/#/domains">Domains</a></li>
|
||||||
<li><a href="/#/roles">Roles</a></li>
|
<li><a href="/#/roles">Roles</a></li>
|
||||||
<li><a href="/#/users">Users</a></li>
|
<li><a href="/#/users">Users</a></li>
|
||||||
<li><a href="/#/accounts">Accounts</a></li>
|
<li><a href="/#/destinations">Destinations</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul ng-show="!currentUser.username" class="nav navbar-nav navbar-right">
|
<ul ng-show="!currentUser.username" class="nav navbar-nav navbar-right">
|
||||||
<li><a href="/#/login">Login</a></li>
|
<li><a href="/#/login">Login</a></li>
|
||||||
|
|
|
@ -159,3 +159,13 @@ a {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wave-spinner {
|
||||||
|
margin: 5px auto !important;
|
||||||
|
width: 40px !important;
|
||||||
|
height: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wave-spinner>div {
|
||||||
|
background-color: #FFFFFF !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
from lemur.accounts.service import *
|
from lemur.destinations.service import *
|
||||||
from lemur.accounts.views import *
|
from lemur.destinations.views import *
|
||||||
|
|
||||||
from json import dumps
|
from json import dumps
|
||||||
|
|
||||||
|
|
||||||
def test_crud(session):
|
def test_crud(session):
|
||||||
account = create('111111', 'account1')
|
destination = create('111111', 'destination1')
|
||||||
assert account.id > 0
|
assert destination.id > 0
|
||||||
|
|
||||||
account = update(account.id, 11111, 'account2')
|
destination = update(destination.id, 11111, 'destination2')
|
||||||
assert account.label == 'account2'
|
assert destination.label == 'destination2'
|
||||||
|
|
||||||
assert len(get_all()) == 1
|
assert len(get_all()) == 1
|
||||||
|
|
||||||
|
@ -17,116 +17,116 @@ def test_crud(session):
|
||||||
assert len(get_all()) == 0
|
assert len(get_all()) == 0
|
||||||
|
|
||||||
|
|
||||||
def test_account_get(client):
|
def test_destination_get(client):
|
||||||
assert client.get(api.url_for(Accounts, account_id=1)).status_code == 401
|
assert client.get(api.url_for(Destinations, destination_id=1)).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_account_post(client):
|
def test_destination_post(client):
|
||||||
assert client.post(api.url_for(Accounts, account_id=1), data={}).status_code == 405
|
assert client.post(api.url_for(Destinations, destination_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_account_put(client):
|
def test_destination_put(client):
|
||||||
assert client.put(api.url_for(Accounts, account_id=1), data={}).status_code == 401
|
assert client.put(api.url_for(Destinations, destination_id=1), data={}).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_account_delete(client):
|
def test_destination_delete(client):
|
||||||
assert client.delete(api.url_for(Accounts, account_id=1)).status_code == 401
|
assert client.delete(api.url_for(Destinations, destination_id=1)).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_account_patch(client):
|
def test_destination_patch(client):
|
||||||
assert client.patch(api.url_for(Accounts, account_id=1), data={}).status_code == 405
|
assert client.patch(api.url_for(Destinations, destination_id=1), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
VALID_USER_HEADER_TOKEN = {
|
VALID_USER_HEADER_TOKEN = {
|
||||||
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyMzMzNjksInN1YiI6MSwiZXhwIjoxNTIxNTQ2OTY5fQ.1qCi0Ip7mzKbjNh0tVd3_eJOrae3rNa_9MCVdA4WtQI'}
|
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyMzMzNjksInN1YiI6MSwiZXhwIjoxNTIxNTQ2OTY5fQ.1qCi0Ip7mzKbjNh0tVd3_eJOrae3rNa_9MCVdA4WtQI'}
|
||||||
|
|
||||||
def test_auth_account_get(client):
|
def test_auth_destination_get(client):
|
||||||
assert client.get(api.url_for(Accounts, account_id=1), headers=VALID_USER_HEADER_TOKEN).status_code == 200
|
assert client.get(api.url_for(Destinations, destination_id=1), headers=VALID_USER_HEADER_TOKEN).status_code == 200
|
||||||
|
|
||||||
|
|
||||||
def test_auth_account_post_(client):
|
def test_auth_destination_post_(client):
|
||||||
assert client.post(api.url_for(Accounts, account_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(Destinations, destination_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_account_put(client):
|
def test_auth_destination_put(client):
|
||||||
assert client.put(api.url_for(Accounts, account_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 403
|
assert client.put(api.url_for(Destinations, destination_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 403
|
||||||
|
|
||||||
|
|
||||||
def test_auth_account_delete(client):
|
def test_auth_destination_delete(client):
|
||||||
assert client.delete(api.url_for(Accounts, account_id=1), headers=VALID_USER_HEADER_TOKEN).status_code == 403
|
assert client.delete(api.url_for(Destinations, destination_id=1), headers=VALID_USER_HEADER_TOKEN).status_code == 403
|
||||||
|
|
||||||
|
|
||||||
def test_auth_account_patch(client):
|
def test_auth_destination_patch(client):
|
||||||
assert client.patch(api.url_for(Accounts, account_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(Destinations, destination_id=1), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
VALID_ADMIN_HEADER_TOKEN = {
|
VALID_ADMIN_HEADER_TOKEN = {
|
||||||
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyNTAyMTgsInN1YiI6MiwiZXhwIjoxNTIxNTYzODE4fQ.6mbq4-Ro6K5MmuNiTJBB153RDhlM5LGJBjI7GBKkfqA'}
|
'Authorization': 'Basic ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MzUyNTAyMTgsInN1YiI6MiwiZXhwIjoxNTIxNTYzODE4fQ.6mbq4-Ro6K5MmuNiTJBB153RDhlM5LGJBjI7GBKkfqA'}
|
||||||
|
|
||||||
def test_admin_account_get(client):
|
def test_admin_destination_get(client):
|
||||||
assert client.get(api.url_for(Accounts, account_id=1), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 200
|
assert client.get(api.url_for(Destinations, destination_id=1), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 200
|
||||||
|
|
||||||
|
|
||||||
def test_admin_account_post(client):
|
def test_admin_destination_post(client):
|
||||||
assert client.post(api.url_for(Accounts, account_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.post(api.url_for(Destinations, destination_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_admin_account_put(client):
|
def test_admin_destination_put(client):
|
||||||
assert client.put(api.url_for(Accounts, account_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 400
|
assert client.put(api.url_for(Destinations, destination_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 400
|
||||||
|
|
||||||
|
|
||||||
def test_admin_account_delete(client):
|
def test_admin_destination_delete(client):
|
||||||
assert client.delete(api.url_for(Accounts, account_id=1), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 500
|
assert client.delete(api.url_for(Destinations, destination_id=1), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 500
|
||||||
|
|
||||||
|
|
||||||
def test_admin_account_patch(client):
|
def test_admin_destination_patch(client):
|
||||||
assert client.patch(api.url_for(Accounts, account_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
assert client.patch(api.url_for(Destinations, destination_id=1), data={}, headers=VALID_ADMIN_HEADER_TOKEN).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_accounts_get(client):
|
def test_destinations_get(client):
|
||||||
assert client.get(api.url_for(AccountsList)).status_code == 401
|
assert client.get(api.url_for(DestinationsList)).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_accounts_post(client):
|
def test_destinations_post(client):
|
||||||
assert client.post(api.url_for(AccountsList), data={}).status_code == 401
|
assert client.post(api.url_for(DestinationsList), data={}).status_code == 401
|
||||||
|
|
||||||
|
|
||||||
def test_accounts_put(client):
|
def test_destinations_put(client):
|
||||||
assert client.put(api.url_for(AccountsList), data={}).status_code == 405
|
assert client.put(api.url_for(DestinationsList), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_accounts_delete(client):
|
def test_destinations_delete(client):
|
||||||
assert client.delete(api.url_for(AccountsList)).status_code == 405
|
assert client.delete(api.url_for(DestinationsList)).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_accounts_patch(client):
|
def test_destinations_patch(client):
|
||||||
assert client.patch(api.url_for(AccountsList), data={}).status_code == 405
|
assert client.patch(api.url_for(DestinationsList), data={}).status_code == 405
|
||||||
|
|
||||||
|
|
||||||
def test_auth_accounts_get(client):
|
def test_auth_destinations_get(client):
|
||||||
assert client.get(api.url_for(AccountsList), headers=VALID_USER_HEADER_TOKEN).status_code == 200
|
assert client.get(api.url_for(DestinationsList), headers=VALID_USER_HEADER_TOKEN).status_code == 200
|
||||||
|
|
||||||
|
|
||||||
def test_auth_accounts_post(client):
|
def test_auth_destinations_post(client):
|
||||||
assert client.post(api.url_for(AccountsList), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 403
|
assert client.post(api.url_for(DestinationsList), data={}, headers=VALID_USER_HEADER_TOKEN).status_code == 403
|
||||||
|
|
||||||
|
|
||||||
def test_admin_accounts_get(client):
|
def test_admin_destinations_get(client):
|
||||||
resp = client.get(api.url_for(AccountsList), headers=VALID_ADMIN_HEADER_TOKEN)
|
resp = client.get(api.url_for(DestinationsList), headers=VALID_ADMIN_HEADER_TOKEN)
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert resp.json == {'items': [], 'total': 0}
|
assert resp.json == {'items': [], 'total': 0}
|
||||||
|
|
||||||
|
|
||||||
def test_admin_accounts_crud(client):
|
def test_admin_destinations_crud(client):
|
||||||
assert client.post(api.url_for(AccountsList), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 400
|
assert client.post(api.url_for(DestinationsList), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 400
|
||||||
data = {'accountNumber': 111, 'label': 'test', 'comments': 'test'}
|
data = {'destinationNumber': 111, 'label': 'test', 'comments': 'test'}
|
||||||
resp = client.post(api.url_for(AccountsList), data=dumps(data), content_type='application/json', headers=VALID_ADMIN_HEADER_TOKEN)
|
resp = client.post(api.url_for(DestinationsList), data=dumps(data), content_type='application/json', headers=VALID_ADMIN_HEADER_TOKEN)
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert client.get(api.url_for(Accounts, account_id=resp.json['id']), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 200
|
assert client.get(api.url_for(Destinations, destination_id=resp.json['id']), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 200
|
||||||
resp = client.get(api.url_for(AccountsList), headers=VALID_ADMIN_HEADER_TOKEN)
|
resp = client.get(api.url_for(DestinationsList), headers=VALID_ADMIN_HEADER_TOKEN)
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert resp.json == {'items': [{'accountNumber': 111, 'label': 'test', 'comments': 'test', 'id': 2}], 'total': 1}
|
assert resp.json == {'items': [{'destinationNumber': 111, 'label': 'test', 'comments': 'test', 'id': 2}], 'total': 1}
|
||||||
assert client.delete(api.url_for(Accounts, account_id=2), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 200
|
assert client.delete(api.url_for(Destinations, destination_id=2), headers=VALID_ADMIN_HEADER_TOKEN).status_code == 200
|
||||||
resp = client.get(api.url_for(AccountsList), headers=VALID_ADMIN_HEADER_TOKEN)
|
resp = client.get(api.url_for(DestinationsList), headers=VALID_ADMIN_HEADER_TOKEN)
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert resp.json == {'items': [], 'total': 0}
|
assert resp.json == {'items': [], 'total': 0}
|
||||||
|
|
7
setup.py
7
setup.py
|
@ -106,8 +106,11 @@ setup(
|
||||||
'lemur = lemur.manage:main',
|
'lemur = lemur.manage:main',
|
||||||
],
|
],
|
||||||
'lemur.plugins': [
|
'lemur.plugins': [
|
||||||
'verisign = lemur.plugins.lemur_verisign.plugin:VerisignPlugin',
|
'verisign_issuer = lemur.plugins.lemur_verisign.plugin:VerisignIssuerPlugin',
|
||||||
'cloudca = lemur.plugins.lemur_cloudca.plugin:CloudCAPlugin',
|
'cloudca_issuer = lemur.plugins.lemur_cloudca.plugin:CloudCAIssuerPlugin',
|
||||||
|
'cloudca_source = lemur.plugins.lemur_cloudca.plugin:CloudCASourcePlugin'
|
||||||
|
'aws_destination = lemur.plugins.lemur_aws.plugin:AWSDestinationPlugin',
|
||||||
|
'aws_source = lemur.plugins.lemur_aws.plugin:AWSSourcePlugin'
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
classifiers=[
|
classifiers=[
|
||||||
|
|
Loading…
Reference in New Issue