Merge branch 'master' into git

This commit is contained in:
Jimmy Zelinskie 2015-04-16 17:38:35 -04:00
commit ba2cb08904
268 changed files with 7008 additions and 1535 deletions

View file

@ -9,7 +9,7 @@ from data import model
from util.cache import cache_control_flask_restful
def image_view(image, image_map):
def image_view(image, image_map, include_locations=True, include_ancestors=True):
extended_props = image
if image.storage and image.storage.id:
extended_props = image.storage
@ -20,24 +20,35 @@ def image_view(image, image_map):
if not aid or not aid in image_map:
return ''
return image_map[aid]
return image_map[aid].docker_image_id
# Calculate the ancestors string, with the DBID's replaced with the docker IDs.
ancestors = [docker_id(a) for a in image.ancestors.split('/')]
ancestors_string = '/'.join(ancestors)
return {
image_data = {
'id': image.docker_image_id,
'created': format_date(extended_props.created),
'comment': extended_props.comment,
'command': json.loads(command) if command else None,
'size': extended_props.image_size,
'locations': list(image.storage.locations),
'uploading': image.storage.uploading,
'ancestors': ancestors_string,
'sort_index': len(image.ancestors)
'sort_index': len(image.ancestors),
}
if include_locations:
image_data['locations'] = list(image.storage.locations)
if include_ancestors:
# Calculate the ancestors string, with the DBID's replaced with the docker IDs.
ancestors = [docker_id(a) for a in image.ancestors.split('/')]
image_data['ancestors'] = '/'.join(ancestors)
return image_data
def historical_image_view(image, image_map):
ancestors = [image_map[a] for a in image.ancestors.split('/')[1:-1]]
normal_view = image_view(image, image_map)
normal_view['history'] = [image_view(parent, image_map, False, False) for parent in ancestors]
return normal_view
@resource('/v1/repository/<repopath:repository>/image/')
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
@ -62,7 +73,7 @@ class RepositoryImageList(RepositoryParamResource):
filtered_images = []
for image in all_images:
if str(image.id) in found_image_ids:
image_map[str(image.id)] = image.docker_image_id
image_map[str(image.id)] = image
filtered_images.append(image)
def add_tags(image_json):
@ -90,9 +101,9 @@ class RepositoryImage(RepositoryParamResource):
# Lookup all the ancestor images for the image.
image_map = {}
for current_image in model.get_parent_images(namespace, repository, image):
image_map[str(current_image.id)] = image.docker_image_id
image_map[str(current_image.id)] = current_image
return image_view(image, image_map)
return historical_image_view(image, image_map)
@resource('/v1/repository/<repopath:repository>/image/<image_id>/changes')

View file

@ -24,16 +24,22 @@ logger = logging.getLogger(__name__)
def org_view(o, teams):
admin_org = AdministerOrganizationPermission(o.username)
is_admin = admin_org.can()
is_admin = AdministerOrganizationPermission(o.username).can()
is_member = OrganizationMemberPermission(o.username).can()
view = {
'name': o.username,
'email': o.email if is_admin else '',
'avatar': avatar.compute_hash(o.email, name=o.username),
'teams': {t.name : team_view(o.username, t) for t in teams},
'is_admin': is_admin
'avatar': avatar.get_data_for_user(o),
'is_admin': is_admin,
'is_member': is_member
}
if teams is not None:
teams = sorted(teams, key=lambda team:team.id)
view['teams'] = {t.name : team_view(o.username, t) for t in teams}
view['ordered_teams'] = [team.name for team in teams]
if is_admin:
view['invoice_email'] = o.invoice_email
@ -129,17 +135,17 @@ class Organization(ApiResource):
@nickname('getOrganization')
def get(self, orgname):
""" Get the details for the specified organization """
permission = OrganizationMemberPermission(orgname)
if permission.can():
try:
org = model.get_organization(orgname)
except model.InvalidOrganizationException:
raise NotFound()
try:
org = model.get_organization(orgname)
except model.InvalidOrganizationException:
raise NotFound()
teams = None
if OrganizationMemberPermission(orgname).can():
teams = model.get_teams_within_org(org)
return org_view(org, teams)
raise Unauthorized()
return org_view(org, teams)
@require_scope(scopes.ORG_ADMIN)
@nickname('changeOrganizationDetails')
@ -218,7 +224,7 @@ class OrgPrivateRepositories(ApiResource):
@path_param('orgname', 'The name of the organization')
class OrgnaizationMemberList(ApiResource):
""" Resource for listing the members of an organization. """
@require_scope(scopes.ORG_ADMIN)
@nickname('getOrganizationMembers')
def get(self, orgname):
@ -297,16 +303,14 @@ class ApplicationInformation(ApiResource):
if not application:
raise NotFound()
org_hash = avatar.compute_hash(application.organization.email,
name=application.organization.username)
app_hash = (avatar.compute_hash(application.avatar_email, name=application.name) if
application.avatar_email else org_hash)
app_email = application.avatar_email or application.organization.email
app_data = avatar.get_data(application.name, app_email, 'app')
return {
'name': application.name,
'description': application.description,
'uri': application.application_uri,
'avatar': app_hash,
'avatar': app_data,
'organization': org_view(application.organization, [])
}

View file

@ -2,6 +2,7 @@ import logging
from flask import request
from app import avatar
from endpoints.api import (resource, nickname, require_repo_admin, RepositoryParamResource,
log_action, request_error, validate_json_request, path_param)
from data import model
@ -16,7 +17,10 @@ def role_view(repo_perm_obj):
}
def wrap_role_view_user(role_json, user):
role_json['name'] = user.username
role_json['is_robot'] = user.robot
if not user.robot:
role_json['avatar'] = avatar.get_data_for_user(user)
return role_json
@ -25,6 +29,12 @@ def wrap_role_view_org(role_json, user, org_members):
return role_json
def wrap_role_view_team(role_json, team):
role_json['name'] = team.name
role_json['avatar'] = avatar.get_data_for_team(team)
return role_json
@resource('/v1/repository/<repopath:repository>/permissions/team/')
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
class RepositoryTeamPermissionList(RepositoryParamResource):
@ -35,8 +45,11 @@ class RepositoryTeamPermissionList(RepositoryParamResource):
""" List all team permission. """
repo_perms = model.get_all_repo_teams(namespace, repository)
def wrapped_role_view(repo_perm):
return wrap_role_view_team(role_view(repo_perm), repo_perm.team)
return {
'permissions': {repo_perm.team.name: role_view(repo_perm)
'permissions': {repo_perm.team.name: wrapped_role_view(repo_perm)
for repo_perm in repo_perms}
}
@ -232,7 +245,7 @@ class RepositoryTeamPermission(RepositoryParamResource):
'role': new_permission['role']},
repo=model.get_repository(namespace, repository))
return role_view(perm), 200
return wrap_role_view_team(role_view(perm), perm.team), 200
@require_repo_admin
@nickname('deleteTeamPermissions')

View file

@ -7,6 +7,7 @@ from auth.permissions import AdministerOrganizationPermission
from auth.auth_context import get_authenticated_user
from auth import scopes
from data import model
from app import avatar
def prototype_view(proto, org_members):
@ -16,6 +17,7 @@ def prototype_view(proto, org_members):
'is_robot': user.robot,
'kind': 'user',
'is_org_member': user.robot or user.username in org_members,
'avatar': avatar.get_data_for_user(user)
}
if proto.delegate_user:
@ -24,6 +26,7 @@ def prototype_view(proto, org_members):
delegate_view = {
'name': proto.delegate_team.name,
'kind': 'team',
'avatar': avatar.get_data_for_team(proto.delegate_team)
}
return {

View file

@ -109,6 +109,8 @@ class RepositoryList(ApiResource):
@query_param('sort', 'Whether to sort the results.', type=truthy_bool, default=False)
@query_param('count', 'Whether to include a count of the total number of results available.',
type=truthy_bool, default=False)
@query_param('namespace_only', 'Whether to limit only to the given namespace.',
type=truthy_bool, default=False)
def get(self, args):
"""Fetch the list of repositories under a variety of situations."""
username = None
@ -129,7 +131,8 @@ class RepositoryList(ApiResource):
repo_query = model.get_visible_repositories(username, limit=args['limit'], page=args['page'],
include_public=args['public'], sort=args['sort'],
namespace=args['namespace'])
namespace=args['namespace'],
namespace_only=args['namespace_only'])
def repo_view(repo_obj):
repo = {
'namespace': repo_obj.namespace_user.username,

View file

@ -5,16 +5,63 @@ from auth.permissions import AdministerOrganizationPermission, OrganizationMembe
from auth.auth_context import get_authenticated_user
from auth import scopes
from data import model
from data.database import User, Team, Repository, FederatedLogin
from util.names import format_robot_username
from flask import abort
from app import avatar
def robot_view(name, token):
return {
'name': name,
'token': token,
'token': token
}
def permission_view(permission):
return {
'repository': {
'name': permission.repository.name,
'is_public': permission.repository.visibility.name == 'public'
},
'role': permission.role.name
}
def robots_list(prefix):
tuples = model.list_entity_robot_permission_teams(prefix)
robots = {}
robot_teams = set()
for robot_tuple in tuples:
robot_name = robot_tuple.get(User.username)
if not robot_name in robots:
robots[robot_name] = {
'name': robot_name,
'token': robot_tuple.get(FederatedLogin.service_ident),
'teams': [],
'repositories': []
}
team_name = robot_tuple.get(Team.name)
repository_name = robot_tuple.get(Repository.name)
if team_name is not None:
check_key = robot_name + ':' + team_name
if not check_key in robot_teams:
robot_teams.add(check_key)
robots[robot_name]['teams'].append({
'name': team_name,
'avatar': avatar.get_data(team_name, team_name, 'team')
})
if repository_name is not None:
if not repository_name in robots[robot_name]['repositories']:
robots[robot_name]['repositories'].append(repository_name)
return {'robots': robots.values()}
@resource('/v1/user/robots')
@internal_only
class UserRobotList(ApiResource):
@ -24,10 +71,7 @@ class UserRobotList(ApiResource):
def get(self):
""" List the available robots for the user. """
user = get_authenticated_user()
robots = model.list_entity_robot_tuples(user.username)
return {
'robots': [robot_view(name, password) for name, password in robots]
}
return robots_list(user.username)
@resource('/v1/user/robots/<robot_shortname>')
@ -73,10 +117,7 @@ class OrgRobotList(ApiResource):
""" List the organization's robots. """
permission = OrganizationMemberPermission(orgname)
if permission.can():
robots = model.list_entity_robot_tuples(orgname)
return {
'robots': [robot_view(name, password) for name, password in robots]
}
return robots_list(orgname)
raise Unauthorized()
@ -125,6 +166,47 @@ class OrgRobot(ApiResource):
raise Unauthorized()
@resource('/v1/user/robots/<robot_shortname>/permissions')
@path_param('robot_shortname', 'The short name for the robot, without any user or organization prefix')
@internal_only
class UserRobotPermissions(ApiResource):
""" Resource for listing the permissions a user's robot has in the system. """
@require_user_admin
@nickname('getUserRobotPermissions')
def get(self, robot_shortname):
""" Returns the list of repository permissions for the user's robot. """
parent = get_authenticated_user()
robot, password = model.get_robot(robot_shortname, parent)
permissions = model.list_robot_permissions(robot.username)
return {
'permissions': [permission_view(permission) for permission in permissions]
}
@resource('/v1/organization/<orgname>/robots/<robot_shortname>/permissions')
@path_param('orgname', 'The name of the organization')
@path_param('robot_shortname', 'The short name for the robot, without any user or organization prefix')
@related_user_resource(UserRobotPermissions)
class OrgRobotPermissions(ApiResource):
""" Resource for listing the permissions an org's robot has in the system. """
@require_user_admin
@nickname('getOrgRobotPermissions')
def get(self, orgname, robot_shortname):
""" Returns the list of repository permissions for the org's robot. """
permission = AdministerOrganizationPermission(orgname)
if permission.can():
parent = model.get_organization(orgname)
robot, password = model.get_robot(robot_shortname, parent)
permissions = model.list_robot_permissions(robot.username)
return {
'permissions': [permission_view(permission) for permission in permissions]
}
abort(403)
@resource('/v1/user/robots/<robot_shortname>/regenerate')
@path_param('robot_shortname', 'The short name for the robot, without any user or organization prefix')
@internal_only

View file

@ -3,11 +3,15 @@ from endpoints.api import (ApiResource, parse_args, query_param, truthy_bool, ni
from data import model
from auth.permissions import (OrganizationMemberPermission, ViewTeamPermission,
ReadRepositoryPermission, UserAdminPermission,
AdministerOrganizationPermission)
AdministerOrganizationPermission, ReadRepositoryPermission)
from auth.auth_context import get_authenticated_user
from auth import scopes
from app import avatar
from app import avatar, get_app_url
from operator import itemgetter
from stringscore import liquidmetal
from util.names import parse_robot_username
import math
@resource('/v1/entities/<prefix>')
class EntitySearch(ApiResource):
@ -45,7 +49,7 @@ class EntitySearch(ApiResource):
'name': namespace_name,
'kind': 'org',
'is_org_member': True,
'avatar': avatar.compute_hash(organization.email, name=organization.username),
'avatar': avatar.get_data_for_org(organization),
}]
except model.InvalidOrganizationException:
@ -63,7 +67,8 @@ class EntitySearch(ApiResource):
result = {
'name': team.name,
'kind': 'team',
'is_org_member': True
'is_org_member': True,
'avatar': avatar.get_data_for_team(team)
}
return result
@ -71,11 +76,12 @@ class EntitySearch(ApiResource):
user_json = {
'name': user.username,
'kind': 'user',
'is_robot': user.is_robot,
'is_robot': user.robot,
'avatar': avatar.get_data_for_user(user)
}
if organization is not None:
user_json['is_org_member'] = user.is_robot or user.is_org_member
user_json['is_org_member'] = user.robot or user.is_org_member
return user_json
@ -128,3 +134,154 @@ class FindRepositories(ApiResource):
if (repo.visibility.name == 'public' or
ReadRepositoryPermission(repo.namespace_user.username, repo.name).can())]
}
def search_entity_view(username, entity):
kind = 'user'
avatar_data = avatar.get_data_for_user(entity)
href = '/user/' + entity.username
if entity.organization:
kind = 'organization'
avatar_data = avatar.get_data_for_org(entity)
href = '/organization/' + entity.username
elif entity.robot:
parts = parse_robot_username(entity.username)
if parts[0] == username:
href = '/user/' + username + '?tab=robots&showRobot=' + entity.username
else:
href = '/organization/' + parts[0] + '?tab=robots&showRobot=' + entity.username
kind = 'robot'
avatar_data = None
return {
'kind': kind,
'avatar': avatar_data,
'name': entity.username,
'score': 1,
'href': href
}
def conduct_team_search(username, query, encountered_teams, results):
""" Finds the matching teams where the user is a member. """
matching_teams = model.get_matching_user_teams(query, get_authenticated_user(), limit=5)
for team in matching_teams:
if team.id in encountered_teams:
continue
encountered_teams.add(team.id)
results.append({
'kind': 'team',
'name': team.name,
'organization': search_entity_view(username, team.organization),
'avatar': avatar.get_data_for_team(team),
'score': 2,
'href': '/organization/' + team.organization.username + '/teams/' + team.name
})
def conduct_admined_team_search(username, query, encountered_teams, results):
""" Finds matching teams in orgs admined by the user. """
matching_teams = model.get_matching_admined_teams(query, get_authenticated_user(), limit=5)
for team in matching_teams:
if team.id in encountered_teams:
continue
encountered_teams.add(team.id)
results.append({
'kind': 'team',
'name': team.name,
'organization': search_entity_view(username, team.organization),
'avatar': avatar.get_data_for_team(team),
'score': 2,
'href': '/organization/' + team.organization.username + '/teams/' + team.name
})
def conduct_repo_search(username, query, results):
""" Finds matching repositories. """
def can_read(repository):
if repository.is_public:
return True
return ReadRepositoryPermission(repository.namespace_user.username, repository.name).can()
only_public = username is None
matching_repos = model.get_sorted_matching_repositories(query, only_public, can_read, limit=5)
for repo in matching_repos:
repo_score = math.log(repo.count or 1, 10) or 1
# If the repository is under the user's namespace, give it 20% more weight.
namespace = repo.namespace_user.username
if OrganizationMemberPermission(namespace).can() or namespace == username:
repo_score = repo_score * 1.2
results.append({
'kind': 'repository',
'namespace': search_entity_view(username, repo.namespace_user),
'name': repo.name,
'description': repo.description,
'is_public': repo.is_public,
'score': repo_score,
'href': '/repository/' + repo.namespace_user.username + '/' + repo.name
})
def conduct_namespace_search(username, query, results):
""" Finds matching users and organizations. """
matching_entities = model.get_matching_user_namespaces(query, username, limit=5)
for entity in matching_entities:
results.append(search_entity_view(username, entity))
def conduct_robot_search(username, query, results):
""" Finds matching robot accounts. """
matching_robots = model.get_matching_robots(query, username, limit=5)
for robot in matching_robots:
results.append(search_entity_view(username, robot))
@resource('/v1/find/all')
class ConductSearch(ApiResource):
""" Resource for finding users, repositories, teams, etc. """
@parse_args
@query_param('query', 'The search query.', type=str, default='')
@require_scope(scopes.READ_REPO)
@nickname('conductSearch')
def get(self, args):
""" Get a list of entities and resources that match the specified query. """
query = args['query']
if not query:
return {'results': []}
username = None
results = []
if get_authenticated_user():
username = get_authenticated_user().username
# Search for teams.
encountered_teams = set()
conduct_team_search(username, query, encountered_teams, results)
conduct_admined_team_search(username, query, encountered_teams, results)
# Search for robot accounts.
conduct_robot_search(username, query, results)
# Search for repos.
conduct_repo_search(username, query, results)
# Search for users and orgs.
conduct_namespace_search(username, query, results)
# Modify the results' scores via how close the query term is to each result's name.
for result in results:
result['score'] = result['score'] * liquidmetal.score(result['name'], query)
return {'results': sorted(results, key=itemgetter('score'), reverse=True)}

View file

@ -15,6 +15,7 @@ from auth.permissions import SuperUserPermission
from auth.auth_context import get_authenticated_user
from data.database import User
from util.config.configutil import add_enterprise_config_defaults
from util.config.provider import CannotWriteConfigException
from util.config.validator import validate_service_for_config, SSL_FILENAMES
from data.runmigration import run_alembic_migration

View file

@ -108,7 +108,7 @@ def user_view(user):
'username': user.username,
'email': user.email,
'verified': user.verified,
'avatar': avatar.compute_hash(user.email, name=user.username),
'avatar': avatar.get_data_for_user(user),
'super_user': superusers.is_superuser(user.username)
}

View file

@ -1,12 +1,45 @@
from flask import request
from flask import request, abort
from endpoints.api import (resource, nickname, require_repo_read, require_repo_write,
RepositoryParamResource, log_action, NotFound, validate_json_request,
path_param)
path_param, format_date)
from endpoints.api.image import image_view
from data import model
from auth.auth_context import get_authenticated_user
from datetime import datetime
@resource('/v1/repository/<repopath:repository>/tag/')
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
class ListRepositoryTags(RepositoryParamResource):
""" Resource for listing repository tags. """
@require_repo_write
@nickname('listRepoTags')
def get(self, namespace, repository):
repo = model.get_repository(namespace, repository)
if not repo:
abort(404)
def tag_view(tag):
tag_info = {
'name': tag.name,
'docker_image_id': tag.image.docker_image_id,
}
if tag.lifetime_start_ts > 0:
tag_info['start_ts'] = tag.lifetime_start_ts
if tag.lifetime_end_ts > 0:
tag_info['end_ts'] = tag.lifetime_end_ts
return tag_info
tags = model.list_repository_tag_history(repo, limit=100)
return {'tags': [tag_view(tag) for tag in tags]}
@resource('/v1/repository/<repopath:repository>/tag/<tag>')
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
@ -92,7 +125,7 @@ class RepositoryTagImages(RepositoryParamResource):
parent_images = model.get_parent_images(namespace, repository, tag_image)
image_map = {}
for image in parent_images:
image_map[str(image.id)] = image.docker_image_id
image_map[str(image.id)] = image
parents = list(parent_images)
parents.reverse()

View file

@ -52,11 +52,11 @@ def team_view(orgname, team):
view_permission = ViewTeamPermission(orgname, team.name)
role = model.get_team_org_role(team).name
return {
'id': team.id,
'name': team.name,
'description': team.description,
'can_view': view_permission.can(),
'role': role
'role': role,
'avatar': avatar.get_data_for_team(team)
}
def member_view(member, invited=False):
@ -64,7 +64,7 @@ def member_view(member, invited=False):
'name': member.username,
'kind': 'user',
'is_robot': member.robot,
'avatar': avatar.compute_hash(member.email, name=member.username) if not member.robot else None,
'avatar': avatar.get_data_for_user(member),
'invited': invited,
}
@ -76,7 +76,7 @@ def invite_view(invite):
return {
'email': invite.email,
'kind': 'invite',
'avatar': avatar.compute_hash(invite.email),
'avatar': avatar.get_data(invite.email, invite.email, 'user'),
'invited': True
}

View file

@ -1,7 +1,8 @@
import logging
import json
from flask import request
from random import SystemRandom
from flask import request, abort
from flask.ext.login import logout_user
from flask.ext.principal import identity_changed, AnonymousIdentity
from peewee import IntegrityError
@ -35,7 +36,7 @@ def user_view(user):
admin_org = AdministerOrganizationPermission(o.username)
return {
'name': o.username,
'avatar': avatar.compute_hash(o.email, name=o.username),
'avatar': avatar.get_data_for_org(o),
'is_org_admin': admin_org.can(),
'can_create_repo': admin_org.can() or CreateRepositoryPermission(o.username).can(),
'preferred_namespace': not (o.stripe_id is None)
@ -58,15 +59,16 @@ def user_view(user):
logins = model.list_federated_logins(user)
user_response = {
'verified': user.verified,
'anonymous': False,
'username': user.username,
'avatar': avatar.compute_hash(user.email, name=user.username),
'avatar': avatar.get_data_for_user(user)
}
user_admin = UserAdminPermission(user.username)
if user_admin.can():
user_response.update({
'is_me': True,
'verified': user.verified,
'email': user.email,
'organizations': [org_view(o) for o in organizations],
'logins': [login_view(login) for login in logins],
@ -76,7 +78,7 @@ def user_view(user):
'tag_expiration': user.removed_tag_expiration_s,
})
if features.SUPER_USERS:
if features.SUPER_USERS and SuperUserPermission().can():
user_response.update({
'super_user': user and user == get_authenticated_user() and SuperUserPermission().can()
})
@ -175,8 +177,8 @@ class User(ApiResource):
'description': 'The user\'s email address',
},
'avatar': {
'type': 'string',
'description': 'Avatar hash representing the user\'s icon'
'type': 'object',
'description': 'Avatar data representing the user\'s icon'
},
'organizations': {
'type': 'array',
@ -224,8 +226,13 @@ class User(ApiResource):
if 'password' in user_data:
logger.debug('Changing password for user: %s', user.username)
log_action('account_change_password', user.username)
# Change the user's password.
model.change_password(user, user_data['password'])
# Login again to reset their session cookie.
common_login(user)
if features.MAILING:
send_password_changed(user.username, user.email)
@ -335,13 +342,51 @@ class PrivateRepositories(ApiResource):
}
@resource('/v1/user/clientkey')
@internal_only
class ClientKey(ApiResource):
""" Operations for returning an encrypted key which can be used in place of a password
for the Docker client. """
schemas = {
'GenerateClientKey': {
'id': 'GenerateClientKey',
'type': 'object',
'required': [
'password',
],
'properties': {
'password': {
'type': 'string',
'description': 'The user\'s password',
},
}
}
}
@require_user_admin
@nickname('generateUserClientKey')
@validate_json_request('GenerateClientKey')
def post(self):
""" Return's the user's private client key. """
username = get_authenticated_user().username
password = request.get_json()['password']
(result, error_message) = authentication.verify_user(username, password)
if not result:
raise request_error(message=error_message)
return {
'key': authentication.encrypt_user_password(password)
}
def conduct_signin(username_or_email, password):
needs_email_verification = False
invalid_credentials = False
verified = None
try:
verified = authentication.verify_user(username_or_email, password)
(verified, error_message) = authentication.verify_user(username_or_email, password)
except model.TooManyUsersException as ex:
raise license_error(exception=ex)
@ -407,7 +452,7 @@ class ConvertToOrganization(ApiResource):
# Ensure that the sign in credentials work.
admin_password = convert_data['adminPassword']
admin_user = authentication.verify_user(admin_username, admin_password)
(admin_user, error_message) = authentication.verify_user(admin_username, admin_password)
if not admin_user:
raise request_error(reason='invaliduser',
message='The admin user credentials are not valid')
@ -621,17 +666,16 @@ class UserNotification(ApiResource):
def authorization_view(access_token):
oauth_app = access_token.application
app_email = oauth_app.avatar_email or oauth_app.organization.email
return {
'application': {
'name': oauth_app.name,
'description': oauth_app.description,
'url': oauth_app.application_uri,
'avatar': avatar.compute_hash(oauth_app.avatar_email or oauth_app.organization.email,
name=oauth_app.name),
'avatar': avatar.get_data(oauth_app.name, app_email, 'app'),
'organization': {
'name': oauth_app.organization.username,
'avatar': avatar.compute_hash(oauth_app.organization.email,
name=oauth_app.organization.username)
'avatar': avatar.get_data_for_org(oauth_app.organization)
}
},
'scopes': scopes.get_scope_information(access_token.scope),
@ -745,6 +789,7 @@ class StarredRepositoryList(ApiResource):
'repository': repository,
}, 201
@resource('/v1/user/starred/<repopath:repository>')
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
class StarredRepository(RepositoryParamResource):
@ -759,3 +804,17 @@ class StarredRepository(RepositoryParamResource):
if repo:
model.unstar_repository(user, repo)
return 'Deleted', 204
@resource('/v1/users/<username>')
class Users(ApiResource):
""" Operations related to retrieving information about other users. """
@nickname('getUserInformation')
def get(self, username):
""" Get user information for the specified user. """
user = model.get_user(username)
if user is None or user.organization or user.robot:
abort(404)
return user_view(user)