Add ability to regenerate robot account credentials
This commit is contained in:
parent
837630359c
commit
a129aac94b
11 changed files with 307 additions and 8 deletions
|
@ -0,0 +1,36 @@
|
||||||
|
"""add log kind for regenerating robot tokens
|
||||||
|
|
||||||
|
Revision ID: 43e943c0639f
|
||||||
|
Revises: 82297d834ad
|
||||||
|
Create Date: 2014-08-25 17:14:42.784518
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '43e943c0639f'
|
||||||
|
down_revision = '82297d834ad'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
from data.model.sqlalchemybridge import gen_sqlalchemy_metadata
|
||||||
|
from data.database import all_models
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
schema = gen_sqlalchemy_metadata(all_models)
|
||||||
|
|
||||||
|
op.bulk_insert(schema.tables['logentrykind'],
|
||||||
|
[
|
||||||
|
{'id': 41, 'name':'regenerate_robot_token'},
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
schema = gen_sqlalchemy_metadata(all_models)
|
||||||
|
|
||||||
|
op.execute(
|
||||||
|
(logentrykind.delete()
|
||||||
|
.where(logentrykind.c.name == op.inline_literal('regenerate_robot_token')))
|
||||||
|
|
||||||
|
)
|
|
@ -180,6 +180,19 @@ def create_robot(robot_shortname, parent):
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
raise DataModelException(ex.message)
|
raise DataModelException(ex.message)
|
||||||
|
|
||||||
|
def get_robot(robot_shortname, parent):
|
||||||
|
robot_username = format_robot_username(parent.username, robot_shortname)
|
||||||
|
robot = lookup_robot(robot_username)
|
||||||
|
|
||||||
|
if not robot:
|
||||||
|
msg = ('Could not find robot with username: %s' %
|
||||||
|
robot_username)
|
||||||
|
raise InvalidRobotException(msg)
|
||||||
|
|
||||||
|
service = LoginService.get(name='quayrobot')
|
||||||
|
login = FederatedLogin.get(FederatedLogin.user == robot, FederatedLogin.service == service)
|
||||||
|
|
||||||
|
return robot, login.service_ident
|
||||||
|
|
||||||
def lookup_robot(robot_username):
|
def lookup_robot(robot_username):
|
||||||
joined = User.select().join(FederatedLogin).join(LoginService)
|
joined = User.select().join(FederatedLogin).join(LoginService)
|
||||||
|
@ -190,7 +203,6 @@ def lookup_robot(robot_username):
|
||||||
|
|
||||||
return found[0]
|
return found[0]
|
||||||
|
|
||||||
|
|
||||||
def verify_robot(robot_username, password):
|
def verify_robot(robot_username, password):
|
||||||
joined = User.select().join(FederatedLogin).join(LoginService)
|
joined = User.select().join(FederatedLogin).join(LoginService)
|
||||||
found = list(joined.where(FederatedLogin.service_ident == password,
|
found = list(joined.where(FederatedLogin.service_ident == password,
|
||||||
|
@ -203,6 +215,25 @@ def verify_robot(robot_username, password):
|
||||||
|
|
||||||
return found[0]
|
return found[0]
|
||||||
|
|
||||||
|
def regenerate_robot_token(robot_shortname, parent):
|
||||||
|
robot_username = format_robot_username(parent.username, robot_shortname)
|
||||||
|
|
||||||
|
robot = lookup_robot(robot_username)
|
||||||
|
if not robot:
|
||||||
|
raise InvalidRobotException('Could not find robot with username: %s' %
|
||||||
|
robot_username)
|
||||||
|
|
||||||
|
password = random_string_generator(length=64)()
|
||||||
|
robot.email = password
|
||||||
|
|
||||||
|
service = LoginService.get(name='quayrobot')
|
||||||
|
login = FederatedLogin.get(FederatedLogin.user == robot, FederatedLogin.service == service)
|
||||||
|
login.service_ident = password
|
||||||
|
|
||||||
|
login.save()
|
||||||
|
robot.save()
|
||||||
|
|
||||||
|
return robot, password
|
||||||
|
|
||||||
def delete_robot(robot_username):
|
def delete_robot(robot_username):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -35,6 +35,14 @@ class UserRobotList(ApiResource):
|
||||||
@internal_only
|
@internal_only
|
||||||
class UserRobot(ApiResource):
|
class UserRobot(ApiResource):
|
||||||
""" Resource for managing a user's robots. """
|
""" Resource for managing a user's robots. """
|
||||||
|
@require_user_admin
|
||||||
|
@nickname('getUserRobot')
|
||||||
|
def get(self, robot_shortname):
|
||||||
|
""" Returns the user's robot with the specified name. """
|
||||||
|
parent = get_authenticated_user()
|
||||||
|
robot, password = model.get_robot(robot_shortname, parent)
|
||||||
|
return robot_view(robot.username, password)
|
||||||
|
|
||||||
@require_user_admin
|
@require_user_admin
|
||||||
@nickname('createUserRobot')
|
@nickname('createUserRobot')
|
||||||
def put(self, robot_shortname):
|
def put(self, robot_shortname):
|
||||||
|
@ -79,6 +87,18 @@ class OrgRobotList(ApiResource):
|
||||||
@related_user_resource(UserRobot)
|
@related_user_resource(UserRobot)
|
||||||
class OrgRobot(ApiResource):
|
class OrgRobot(ApiResource):
|
||||||
""" Resource for managing an organization's robots. """
|
""" Resource for managing an organization's robots. """
|
||||||
|
@require_scope(scopes.ORG_ADMIN)
|
||||||
|
@nickname('getOrgRobot')
|
||||||
|
def get(self, orgname, robot_shortname):
|
||||||
|
""" Returns the organization's robot with the specified name. """
|
||||||
|
permission = AdministerOrganizationPermission(orgname)
|
||||||
|
if permission.can():
|
||||||
|
parent = model.get_organization(orgname)
|
||||||
|
robot, password = model.get_robot(robot_shortname, parent)
|
||||||
|
return robot_view(robot.username, password)
|
||||||
|
|
||||||
|
raise Unauthorized()
|
||||||
|
|
||||||
@require_scope(scopes.ORG_ADMIN)
|
@require_scope(scopes.ORG_ADMIN)
|
||||||
@nickname('createOrgRobot')
|
@nickname('createOrgRobot')
|
||||||
def put(self, orgname, robot_shortname):
|
def put(self, orgname, robot_shortname):
|
||||||
|
@ -103,3 +123,38 @@ class OrgRobot(ApiResource):
|
||||||
return 'Deleted', 204
|
return 'Deleted', 204
|
||||||
|
|
||||||
raise Unauthorized()
|
raise Unauthorized()
|
||||||
|
|
||||||
|
|
||||||
|
@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
|
||||||
|
class RegenerateUserRobot(ApiResource):
|
||||||
|
""" Resource for regenerate an organization's robot's token. """
|
||||||
|
@require_user_admin
|
||||||
|
@nickname('regenerateUserRobotToken')
|
||||||
|
def post(self, robot_shortname):
|
||||||
|
""" Regenerates the token for a user's robot. """
|
||||||
|
parent = get_authenticated_user()
|
||||||
|
robot, password = model.regenerate_robot_token(robot_shortname, parent)
|
||||||
|
log_action('regenerate_robot_token', parent.username, {'robot': robot_shortname})
|
||||||
|
return robot_view(robot.username, password)
|
||||||
|
|
||||||
|
|
||||||
|
@resource('/v1/organization/<orgname>/robots/<robot_shortname>/regenerate')
|
||||||
|
@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(RegenerateUserRobot)
|
||||||
|
class RegenerateOrgRobot(ApiResource):
|
||||||
|
""" Resource for regenerate an organization's robot's token. """
|
||||||
|
@require_scope(scopes.ORG_ADMIN)
|
||||||
|
@nickname('regenerateOrgRobotToken')
|
||||||
|
def post(self, orgname, robot_shortname):
|
||||||
|
""" Regenerates the token for an organization robot. """
|
||||||
|
permission = AdministerOrganizationPermission(orgname)
|
||||||
|
if permission.can():
|
||||||
|
parent = model.get_organization(orgname)
|
||||||
|
robot, password = model.regenerate_robot_token(robot_shortname, parent)
|
||||||
|
log_action('regenerate_robot_token', orgname, {'robot': robot_shortname})
|
||||||
|
return robot_view(robot.username, password)
|
||||||
|
|
||||||
|
raise Unauthorized()
|
||||||
|
|
|
@ -229,13 +229,15 @@ def initialize_database():
|
||||||
LogEntryKind.create(name='delete_application')
|
LogEntryKind.create(name='delete_application')
|
||||||
LogEntryKind.create(name='reset_application_client_secret')
|
LogEntryKind.create(name='reset_application_client_secret')
|
||||||
|
|
||||||
# Note: These are deprecated.
|
# Note: These next two are deprecated.
|
||||||
LogEntryKind.create(name='add_repo_webhook')
|
LogEntryKind.create(name='add_repo_webhook')
|
||||||
LogEntryKind.create(name='delete_repo_webhook')
|
LogEntryKind.create(name='delete_repo_webhook')
|
||||||
|
|
||||||
LogEntryKind.create(name='add_repo_notification')
|
LogEntryKind.create(name='add_repo_notification')
|
||||||
LogEntryKind.create(name='delete_repo_notification')
|
LogEntryKind.create(name='delete_repo_notification')
|
||||||
|
|
||||||
|
LogEntryKind.create(name='regenerate_robot_token')
|
||||||
|
|
||||||
ImageStorageLocation.create(name='local_eu')
|
ImageStorageLocation.create(name='local_eu')
|
||||||
ImageStorageLocation.create(name='local_us')
|
ImageStorageLocation.create(name='local_us')
|
||||||
|
|
||||||
|
|
|
@ -464,6 +464,22 @@ i.toggle-icon:hover {
|
||||||
|
|
||||||
.docker-auth-dialog .token-dialog-body .well {
|
.docker-auth-dialog .token-dialog-body .well {
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
|
position: relative;
|
||||||
|
padding-right: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docker-auth-dialog .token-dialog-body .well i.fa-refresh {
|
||||||
|
position: absolute;
|
||||||
|
top: 9px;
|
||||||
|
right: 9px;
|
||||||
|
font-size: 20px;
|
||||||
|
color: gray;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docker-auth-dialog .token-dialog-body .well i.fa-refresh:hover {
|
||||||
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.docker-auth-dialog .token-view {
|
.docker-auth-dialog .token-view {
|
||||||
|
|
|
@ -10,11 +10,24 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body token-dialog-body">
|
<div class="modal-body token-dialog-body">
|
||||||
<div class="alert alert-info">The docker <u>username</u> is <b>{{ username }}</b> and the <u>password</u> is the token below. You may use any value for email.</div>
|
<div class="alert alert-info">The docker <u>username</u> is <b>{{ username }}</b> and the <u>password</u> is the token below. You may use any value for email.</div>
|
||||||
<div class="well well-sm">
|
|
||||||
|
<div class="well well-sm" ng-show="regenerating">
|
||||||
|
Regenerating Token...
|
||||||
|
<i class="fa fa-refresh fa-spin"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="well well-sm" ng-show="!regenerating">
|
||||||
<input id="token-view" class="token-view" type="text" value="{{ token }}" onClick="this.select();" readonly>
|
<input id="token-view" class="token-view" type="text" value="{{ token }}" onClick="this.select();" readonly>
|
||||||
|
<i class="fa fa-refresh" ng-show="supportsRegenerate" ng-click="askRegenerate()"
|
||||||
|
data-title="Regenerate Token"
|
||||||
|
data-placement="left"
|
||||||
|
bs-tooltip></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer" ng-show="regenerating">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer" ng-show="!regenerating">
|
||||||
<span class="download-cfg" ng-show="isDownloadSupported()">
|
<span class="download-cfg" ng-show="isDownloadSupported()">
|
||||||
<i class="fa fa-download"></i>
|
<i class="fa fa-download"></i>
|
||||||
<a href="javascript:void(0)" ng-click="downloadCfg(shownRobot)">Download .dockercfg file</a>
|
<a href="javascript:void(0)" ng-click="downloadCfg(shownRobot)">Download .dockercfg file</a>
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="docker-auth-dialog" username="shownRobot.name" token="shownRobot.token"
|
<div class="docker-auth-dialog" username="shownRobot.name" token="shownRobot.token"
|
||||||
shown="!!shownRobot" counter="showRobotCounter">
|
shown="!!shownRobot" counter="showRobotCounter" supports-regenerate="true" regenerate="regenerateToken(username)">
|
||||||
<i class="fa fa-wrench"></i> {{ shownRobot.name }}
|
<i class="fa fa-wrench"></i> {{ shownRobot.name }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2385,7 +2385,9 @@ quayApp.directive('dockerAuthDialog', function (Config) {
|
||||||
'username': '=username',
|
'username': '=username',
|
||||||
'token': '=token',
|
'token': '=token',
|
||||||
'shown': '=shown',
|
'shown': '=shown',
|
||||||
'counter': '=counter'
|
'counter': '=counter',
|
||||||
|
'supportsRegenerate': '@supportsRegenerate',
|
||||||
|
'regenerate': '®enerate'
|
||||||
},
|
},
|
||||||
controller: function($scope, $element) {
|
controller: function($scope, $element) {
|
||||||
var updateCommand = function() {
|
var updateCommand = function() {
|
||||||
|
@ -2396,6 +2398,15 @@ quayApp.directive('dockerAuthDialog', function (Config) {
|
||||||
$scope.$watch('username', updateCommand);
|
$scope.$watch('username', updateCommand);
|
||||||
$scope.$watch('token', updateCommand);
|
$scope.$watch('token', updateCommand);
|
||||||
|
|
||||||
|
$scope.regenerating = true;
|
||||||
|
|
||||||
|
$scope.askRegenerate = function() {
|
||||||
|
bootbox.confirm('Are you sure you want to regenerate the token? All existing login credentials will become invalid', function(resp) {
|
||||||
|
$scope.regenerating = true;
|
||||||
|
$scope.regenerate({'username': $scope.username, 'token': $scope.token});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
$scope.isDownloadSupported = function() {
|
$scope.isDownloadSupported = function() {
|
||||||
var isSafari = /^((?!chrome).)*safari/i.test(navigator.userAgent);
|
var isSafari = /^((?!chrome).)*safari/i.test(navigator.userAgent);
|
||||||
if (isSafari) {
|
if (isSafari) {
|
||||||
|
@ -2421,6 +2432,8 @@ quayApp.directive('dockerAuthDialog', function (Config) {
|
||||||
};
|
};
|
||||||
|
|
||||||
var show = function(r) {
|
var show = function(r) {
|
||||||
|
$scope.regenerating = false;
|
||||||
|
|
||||||
if (!$scope.shown || !$scope.username || !$scope.token) {
|
if (!$scope.shown || !$scope.username || !$scope.token) {
|
||||||
$('#dockerauthmodal').modal('hide');
|
$('#dockerauthmodal').modal('hide');
|
||||||
return;
|
return;
|
||||||
|
@ -2661,6 +2674,8 @@ quayApp.directive('logsView', function () {
|
||||||
return 'Delete notification of event "' + eventData['title'] + '" for repository {repo}';
|
return 'Delete notification of event "' + eventData['title'] + '" for repository {repo}';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'regenerate_robot_token': 'Regenerated token for robot {robot}',
|
||||||
|
|
||||||
// Note: These are deprecated.
|
// Note: These are deprecated.
|
||||||
'add_repo_webhook': 'Add webhook in repository {repo}',
|
'add_repo_webhook': 'Add webhook in repository {repo}',
|
||||||
'delete_repo_webhook': 'Delete webhook in repository {repo}'
|
'delete_repo_webhook': 'Delete webhook in repository {repo}'
|
||||||
|
@ -2704,6 +2719,7 @@ quayApp.directive('logsView', function () {
|
||||||
'reset_application_client_secret': 'Reset Client Secret',
|
'reset_application_client_secret': 'Reset Client Secret',
|
||||||
'add_repo_notification': 'Add repository notification',
|
'add_repo_notification': 'Add repository notification',
|
||||||
'delete_repo_notification': 'Delete repository notification',
|
'delete_repo_notification': 'Delete repository notification',
|
||||||
|
'regenerate_robot_token': 'Regenerate Robot Token',
|
||||||
|
|
||||||
// Note: these are deprecated.
|
// Note: these are deprecated.
|
||||||
'add_repo_webhook': 'Add webhook',
|
'add_repo_webhook': 'Add webhook',
|
||||||
|
@ -2875,6 +2891,20 @@ quayApp.directive('robotsManager', function () {
|
||||||
$scope.shownRobot = null;
|
$scope.shownRobot = null;
|
||||||
$scope.showRobotCounter = 0;
|
$scope.showRobotCounter = 0;
|
||||||
|
|
||||||
|
$scope.regenerateToken = function(username) {
|
||||||
|
if (!username) { return; }
|
||||||
|
|
||||||
|
var shortName = $scope.getShortenedName(username);
|
||||||
|
ApiService.regenerateRobotToken($scope.organization, null, {'robot_shortname': shortName}).then(function(updated) {
|
||||||
|
var index = $scope.findRobotIndexByName(username);
|
||||||
|
if (index >= 0) {
|
||||||
|
$scope.robots.splice(index, 1);
|
||||||
|
$scope.robots.push(updated);
|
||||||
|
}
|
||||||
|
$scope.shownRobot = updated;
|
||||||
|
}, ApiService.errorDisplay('Cannot regenerate robot account token'));
|
||||||
|
};
|
||||||
|
|
||||||
$scope.showRobot = function(info) {
|
$scope.showRobot = function(info) {
|
||||||
$scope.shownRobot = info;
|
$scope.shownRobot = info;
|
||||||
$scope.showRobotCounter++;
|
$scope.showRobotCounter++;
|
||||||
|
|
Binary file not shown.
|
@ -14,7 +14,9 @@ from endpoints.api.search import FindRepositories, EntitySearch
|
||||||
from endpoints.api.image import RepositoryImageChanges, RepositoryImage, RepositoryImageList
|
from endpoints.api.image import RepositoryImageChanges, RepositoryImage, RepositoryImageList
|
||||||
from endpoints.api.build import (FileDropResource, RepositoryBuildStatus, RepositoryBuildLogs,
|
from endpoints.api.build import (FileDropResource, RepositoryBuildStatus, RepositoryBuildLogs,
|
||||||
RepositoryBuildList)
|
RepositoryBuildList)
|
||||||
from endpoints.api.robot import UserRobotList, OrgRobot, OrgRobotList, UserRobot
|
from endpoints.api.robot import (UserRobotList, OrgRobot, OrgRobotList, UserRobot,
|
||||||
|
RegenerateOrgRobot, RegenerateUserRobot)
|
||||||
|
|
||||||
from endpoints.api.trigger import (BuildTriggerActivate, BuildTriggerSources, BuildTriggerSubdirs,
|
from endpoints.api.trigger import (BuildTriggerActivate, BuildTriggerSources, BuildTriggerSubdirs,
|
||||||
TriggerBuildList, ActivateBuildTrigger, BuildTrigger,
|
TriggerBuildList, ActivateBuildTrigger, BuildTrigger,
|
||||||
BuildTriggerList, BuildTriggerAnalyze)
|
BuildTriggerList, BuildTriggerAnalyze)
|
||||||
|
@ -1632,6 +1634,19 @@ class TestOrgRobotBuynlargeZ7pd(ApiTestCase):
|
||||||
ApiTestCase.setUp(self)
|
ApiTestCase.setUp(self)
|
||||||
self._set_url(OrgRobot, orgname="buynlarge", robot_shortname="Z7PD")
|
self._set_url(OrgRobot, orgname="buynlarge", robot_shortname="Z7PD")
|
||||||
|
|
||||||
|
def test_get_anonymous(self):
|
||||||
|
self._run_test('GET', 401, None, None)
|
||||||
|
|
||||||
|
def test_get_freshuser(self):
|
||||||
|
self._run_test('GET', 403, 'freshuser', None)
|
||||||
|
|
||||||
|
def test_get_reader(self):
|
||||||
|
self._run_test('GET', 403, 'reader', None)
|
||||||
|
|
||||||
|
def test_get_devtable(self):
|
||||||
|
self._run_test('GET', 400, 'devtable', None)
|
||||||
|
|
||||||
|
|
||||||
def test_put_anonymous(self):
|
def test_put_anonymous(self):
|
||||||
self._run_test('PUT', 401, None, None)
|
self._run_test('PUT', 401, None, None)
|
||||||
|
|
||||||
|
@ -1644,6 +1659,7 @@ class TestOrgRobotBuynlargeZ7pd(ApiTestCase):
|
||||||
def test_put_devtable(self):
|
def test_put_devtable(self):
|
||||||
self._run_test('PUT', 400, 'devtable', None)
|
self._run_test('PUT', 400, 'devtable', None)
|
||||||
|
|
||||||
|
|
||||||
def test_delete_anonymous(self):
|
def test_delete_anonymous(self):
|
||||||
self._run_test('DELETE', 401, None, None)
|
self._run_test('DELETE', 401, None, None)
|
||||||
|
|
||||||
|
@ -3040,6 +3056,19 @@ class TestUserRobot5vdy(ApiTestCase):
|
||||||
ApiTestCase.setUp(self)
|
ApiTestCase.setUp(self)
|
||||||
self._set_url(UserRobot, robot_shortname="robotname")
|
self._set_url(UserRobot, robot_shortname="robotname")
|
||||||
|
|
||||||
|
def test_get_anonymous(self):
|
||||||
|
self._run_test('GET', 401, None, None)
|
||||||
|
|
||||||
|
def test_get_freshuser(self):
|
||||||
|
self._run_test('GET', 400, 'freshuser', None)
|
||||||
|
|
||||||
|
def test_get_reader(self):
|
||||||
|
self._run_test('GET', 400, 'reader', None)
|
||||||
|
|
||||||
|
def test_get_devtable(self):
|
||||||
|
self._run_test('GET', 400, 'devtable', None)
|
||||||
|
|
||||||
|
|
||||||
def test_put_anonymous(self):
|
def test_put_anonymous(self):
|
||||||
self._run_test('PUT', 401, None, None)
|
self._run_test('PUT', 401, None, None)
|
||||||
|
|
||||||
|
@ -3052,6 +3081,7 @@ class TestUserRobot5vdy(ApiTestCase):
|
||||||
def test_put_devtable(self):
|
def test_put_devtable(self):
|
||||||
self._run_test('PUT', 201, 'devtable', None)
|
self._run_test('PUT', 201, 'devtable', None)
|
||||||
|
|
||||||
|
|
||||||
def test_delete_anonymous(self):
|
def test_delete_anonymous(self):
|
||||||
self._run_test('DELETE', 401, None, None)
|
self._run_test('DELETE', 401, None, None)
|
||||||
|
|
||||||
|
@ -3065,6 +3095,42 @@ class TestUserRobot5vdy(ApiTestCase):
|
||||||
self._run_test('DELETE', 400, 'devtable', None)
|
self._run_test('DELETE', 400, 'devtable', None)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRegenerateUserRobot(ApiTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
ApiTestCase.setUp(self)
|
||||||
|
self._set_url(RegenerateUserRobot, robot_shortname="robotname")
|
||||||
|
|
||||||
|
def test_post_anonymous(self):
|
||||||
|
self._run_test('POST', 401, None, None)
|
||||||
|
|
||||||
|
def test_post_freshuser(self):
|
||||||
|
self._run_test('POST', 400, 'freshuser', None)
|
||||||
|
|
||||||
|
def test_post_reader(self):
|
||||||
|
self._run_test('POST', 400, 'reader', None)
|
||||||
|
|
||||||
|
def test_post_devtable(self):
|
||||||
|
self._run_test('POST', 400, 'devtable', None)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRegenerateOrgRobot(ApiTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
ApiTestCase.setUp(self)
|
||||||
|
self._set_url(RegenerateOrgRobot, orgname="buynlarge", robot_shortname="robotname")
|
||||||
|
|
||||||
|
def test_post_anonymous(self):
|
||||||
|
self._run_test('POST', 401, None, None)
|
||||||
|
|
||||||
|
def test_post_freshuser(self):
|
||||||
|
self._run_test('POST', 403, 'freshuser', None)
|
||||||
|
|
||||||
|
def test_post_reader(self):
|
||||||
|
self._run_test('POST', 403, 'reader', None)
|
||||||
|
|
||||||
|
def test_post_devtable(self):
|
||||||
|
self._run_test('POST', 400, 'devtable', None)
|
||||||
|
|
||||||
|
|
||||||
class TestOrganizationBuynlarge(ApiTestCase):
|
class TestOrganizationBuynlarge(ApiTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
ApiTestCase.setUp(self)
|
ApiTestCase.setUp(self)
|
||||||
|
|
|
@ -16,7 +16,8 @@ from endpoints.api.tag import RepositoryTagImages, RepositoryTag
|
||||||
from endpoints.api.search import FindRepositories, EntitySearch
|
from endpoints.api.search import FindRepositories, EntitySearch
|
||||||
from endpoints.api.image import RepositoryImage, RepositoryImageList
|
from endpoints.api.image import RepositoryImage, RepositoryImageList
|
||||||
from endpoints.api.build import RepositoryBuildStatus, RepositoryBuildLogs, RepositoryBuildList
|
from endpoints.api.build import RepositoryBuildStatus, RepositoryBuildLogs, RepositoryBuildList
|
||||||
from endpoints.api.robot import UserRobotList, OrgRobot, OrgRobotList, UserRobot
|
from endpoints.api.robot import (UserRobotList, OrgRobot, OrgRobotList, UserRobot,
|
||||||
|
RegenerateUserRobot, RegenerateOrgRobot)
|
||||||
from endpoints.api.trigger import (BuildTriggerActivate, BuildTriggerSources, BuildTriggerSubdirs,
|
from endpoints.api.trigger import (BuildTriggerActivate, BuildTriggerSources, BuildTriggerSubdirs,
|
||||||
TriggerBuildList, ActivateBuildTrigger, BuildTrigger,
|
TriggerBuildList, ActivateBuildTrigger, BuildTrigger,
|
||||||
BuildTriggerList, BuildTriggerAnalyze)
|
BuildTriggerList, BuildTriggerAnalyze)
|
||||||
|
@ -1572,6 +1573,30 @@ class TestUserRobots(ApiTestCase):
|
||||||
robots = self.getRobotNames()
|
robots = self.getRobotNames()
|
||||||
assert not NO_ACCESS_USER + '+bender' in robots
|
assert not NO_ACCESS_USER + '+bender' in robots
|
||||||
|
|
||||||
|
def test_regenerate(self):
|
||||||
|
self.login(NO_ACCESS_USER)
|
||||||
|
|
||||||
|
# Create a robot.
|
||||||
|
json = self.putJsonResponse(UserRobot,
|
||||||
|
params=dict(robot_shortname='bender'),
|
||||||
|
expected_code=201)
|
||||||
|
|
||||||
|
token = json['token']
|
||||||
|
|
||||||
|
# Regenerate the robot.
|
||||||
|
json = self.postJsonResponse(RegenerateUserRobot,
|
||||||
|
params=dict(robot_shortname='bender'),
|
||||||
|
expected_code=200)
|
||||||
|
|
||||||
|
# Verify the token changed.
|
||||||
|
self.assertNotEquals(token, json['token'])
|
||||||
|
|
||||||
|
json2 = self.getJsonResponse(UserRobot,
|
||||||
|
params=dict(robot_shortname='bender'),
|
||||||
|
expected_code=200)
|
||||||
|
|
||||||
|
self.assertEquals(json['token'], json2['token'])
|
||||||
|
|
||||||
|
|
||||||
class TestOrgRobots(ApiTestCase):
|
class TestOrgRobots(ApiTestCase):
|
||||||
def getRobotNames(self):
|
def getRobotNames(self):
|
||||||
|
@ -1601,6 +1626,31 @@ class TestOrgRobots(ApiTestCase):
|
||||||
assert not ORGANIZATION + '+bender' in robots
|
assert not ORGANIZATION + '+bender' in robots
|
||||||
|
|
||||||
|
|
||||||
|
def test_regenerate(self):
|
||||||
|
self.login(ADMIN_ACCESS_USER)
|
||||||
|
|
||||||
|
# Create a robot.
|
||||||
|
json = self.putJsonResponse(OrgRobot,
|
||||||
|
params=dict(orgname=ORGANIZATION, robot_shortname='bender'),
|
||||||
|
expected_code=201)
|
||||||
|
|
||||||
|
token = json['token']
|
||||||
|
|
||||||
|
# Regenerate the robot.
|
||||||
|
json = self.postJsonResponse(RegenerateOrgRobot,
|
||||||
|
params=dict(orgname=ORGANIZATION, robot_shortname='bender'),
|
||||||
|
expected_code=200)
|
||||||
|
|
||||||
|
# Verify the token changed.
|
||||||
|
self.assertNotEquals(token, json['token'])
|
||||||
|
|
||||||
|
json2 = self.getJsonResponse(OrgRobot,
|
||||||
|
params=dict(orgname=ORGANIZATION, robot_shortname='bender'),
|
||||||
|
expected_code=200)
|
||||||
|
|
||||||
|
self.assertEquals(json['token'], json2['token'])
|
||||||
|
|
||||||
|
|
||||||
class TestLogs(ApiTestCase):
|
class TestLogs(ApiTestCase):
|
||||||
def test_user_logs(self):
|
def test_user_logs(self):
|
||||||
self.login(ADMIN_ACCESS_USER)
|
self.login(ADMIN_ACCESS_USER)
|
||||||
|
|
Reference in a new issue