示例#1
0
文件: auth.go 项目: bac/juju
// AuthToken returns a service principal token, suitable for authorizing
// Resource Manager API requests, based on the supplied CloudSpec.
func AuthToken(cloud environs.CloudSpec, sender autorest.Sender) (*azure.ServicePrincipalToken, error) {
	if authType := cloud.Credential.AuthType(); authType != clientCredentialsAuthType {
		// We currently only support a single auth-type for
		// non-interactive authentication. Interactive auth
		// is used only to generate a service-principal.
		return nil, errors.NotSupportedf("auth-type %q", authType)
	}

	credAttrs := cloud.Credential.Attributes()
	subscriptionId := credAttrs[credAttrSubscriptionId]
	appId := credAttrs[credAttrAppId]
	appPassword := credAttrs[credAttrAppPassword]
	client := subscriptions.Client{subscriptions.NewWithBaseURI(cloud.Endpoint)}
	client.Sender = sender
	oauthConfig, _, err := azureauth.OAuthConfig(client, cloud.Endpoint, subscriptionId)
	if err != nil {
		return nil, errors.Trace(err)
	}

	resource := azureauth.TokenResource(cloud.Endpoint)
	token, err := azure.NewServicePrincipalToken(
		*oauthConfig,
		appId,
		appPassword,
		resource,
	)
	if err != nil {
		return nil, errors.Annotate(err, "constructing service principal token")
	}
	if sender != nil {
		token.SetSender(sender)
	}
	return token, nil
}
示例#2
0
文件: discovery.go 项目: bac/juju
// DiscoverAuthorizationID returns the OAuth authorization URI for the given
// subscription ID. This can be used to determine the AD tenant ID.
func DiscoverAuthorizationURI(client subscriptions.Client, subscriptionID string) (*url.URL, error) {
	// We make an unauthenticated request to the Azure API, which
	// responds with the authentication URL with the tenant ID in it.
	result, err := client.Get(subscriptionID)
	if err == nil {
		return nil, errors.New("expected unauthorized error response")
	}
	if result.Response.Response == nil {
		return nil, errors.Trace(err)
	}
	if result.StatusCode != http.StatusUnauthorized {
		return nil, errors.Annotatef(err, "expected unauthorized error response, got %v", result.StatusCode)
	}

	header := result.Header.Get(authenticateHeaderKey)
	if header == "" {
		return nil, errors.Errorf("%s header not found", authenticateHeaderKey)
	}
	match := authorizationUriRegexp.FindStringSubmatch(header)
	if match == nil {
		return nil, errors.Errorf(
			"authorization_uri not found in %s header (%q)",
			authenticateHeaderKey, header,
		)
	}
	return url.Parse(match[1])
}
示例#3
0
文件: oauth_test.go 项目: bac/juju
func (s *OAuthConfigSuite) TestOAuthConfig(c *gc.C) {
	client := subscriptions.Client{subscriptions.NewWithBaseURI("https://testing.invalid")}
	client.Sender = oauthConfigSender()
	cfg, tenantId, err := azureauth.OAuthConfig(client, "https://testing.invalid", "subscription-id")
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(tenantId, gc.Equals, fakeTenantId)

	baseURL := url.URL{
		Scheme:   "https",
		Host:     "testing.invalid",
		RawQuery: "api-version=1.0",
	}
	expectedCfg := &azure.OAuthConfig{
		AuthorizeEndpoint:  baseURL,
		TokenEndpoint:      baseURL,
		DeviceCodeEndpoint: baseURL,
	}
	expectedCfg.AuthorizeEndpoint.Path = "/11111111-1111-1111-1111-111111111111/oauth2/authorize"
	expectedCfg.TokenEndpoint.Path = "/11111111-1111-1111-1111-111111111111/oauth2/token"
	expectedCfg.DeviceCodeEndpoint.Path = "/11111111-1111-1111-1111-111111111111/oauth2/devicecode"

	c.Assert(cfg, jc.DeepEquals, expectedCfg)
}
示例#4
0
文件: interactive.go 项目: bac/juju
// InteractiveCreateServicePrincipal interactively creates service
// principals for a subscription.
func InteractiveCreateServicePrincipal(
	stderr io.Writer,
	sender autorest.Sender,
	requestInspector autorest.PrepareDecorator,
	resourceManagerEndpoint string,
	graphEndpoint string,
	subscriptionId string,
	clock clock.Clock,
	newUUID func() (utils.UUID, error),
) (appId, password string, _ error) {

	subscriptionsClient := subscriptions.Client{
		subscriptions.NewWithBaseURI(resourceManagerEndpoint),
	}
	subscriptionsClient.Sender = sender
	setClientInspectors(&subscriptionsClient.Client, requestInspector, "azure.subscriptions")

	oauthConfig, tenantId, err := OAuthConfig(
		subscriptionsClient,
		resourceManagerEndpoint,
		subscriptionId,
	)
	if err != nil {
		return "", "", errors.Trace(err)
	}

	client := autorest.NewClientWithUserAgent("juju")
	client.Sender = sender
	setClientInspectors(&client, requestInspector, "azure.autorest")

	// Perform the interactive authentication. The user will be prompted to
	// open a URL and input a device code, after which they will have to
	// enter their username and password if they are not already
	// authenticated with Azure.
	fmt.Fprintln(stderr, "Initiating interactive authentication.")
	fmt.Fprintln(stderr)
	armResource := TokenResource(resourceManagerEndpoint)
	clientId := jujuApplicationId
	deviceCode, err := azure.InitiateDeviceAuth(&client, *oauthConfig, clientId, armResource)
	if err != nil {
		return "", "", errors.Annotate(err, "initiating interactive authentication")
	}
	fmt.Fprintln(stderr, to.String(deviceCode.Message)+"\n")
	token, err := azure.WaitForUserCompletion(&client, deviceCode)
	if err != nil {
		return "", "", errors.Annotate(err, "waiting for interactive authentication to completed")
	}

	// Create service principal tokens that we can use to authorize API
	// requests to Active Directory and Resource Manager. These tokens
	// are only valid for a short amount of time, so we must create a
	// service principal password that can be used to obtain new tokens.
	armSpt, err := azure.NewServicePrincipalTokenFromManualToken(*oauthConfig, clientId, armResource, *token)
	if err != nil {
		return "", "", errors.Annotate(err, "creating temporary ARM service principal token")
	}
	if client.Sender != nil {
		armSpt.SetSender(client.Sender)
	}
	if err := armSpt.Refresh(); err != nil {
		return "", "", errors.Trace(err)
	}

	// The application requires permissions for both ARM and AD, so we
	// can use the token for both APIs.
	graphResource := TokenResource(graphEndpoint)
	graphToken := armSpt.Token
	graphToken.Resource = graphResource
	graphSpt, err := azure.NewServicePrincipalTokenFromManualToken(*oauthConfig, clientId, graphResource, graphToken)
	if err != nil {
		return "", "", errors.Annotate(err, "creating temporary Graph service principal token")
	}
	if client.Sender != nil {
		graphSpt.SetSender(client.Sender)
	}
	if err := graphSpt.Refresh(); err != nil {
		return "", "", errors.Trace(err)
	}

	directoryURL, err := url.Parse(graphEndpoint)
	if err != nil {
		return "", "", errors.Annotate(err, "parsing identity endpoint")
	}
	directoryURL.Path = path.Join(directoryURL.Path, tenantId)
	directoryClient := ad.NewManagementClient(directoryURL.String())
	authorizationClient := authorization.NewWithBaseURI(resourceManagerEndpoint, subscriptionId)
	directoryClient.Authorizer = graphSpt
	authorizationClient.Authorizer = armSpt
	authorizationClient.Sender = client.Sender
	directoryClient.Sender = client.Sender
	setClientInspectors(&directoryClient.Client, requestInspector, "azure.directory")
	setClientInspectors(&authorizationClient.Client, requestInspector, "azure.authorization")

	userObject, err := ad.UsersClient{directoryClient}.GetCurrentUser()
	if err != nil {
		return "", "", errors.Trace(err)
	}
	fmt.Fprintf(stderr, "Authenticated as %q.\n", userObject.DisplayName)

	fmt.Fprintln(stderr, "Creating/updating service principal.")
	servicePrincipalObjectId, password, err := createOrUpdateServicePrincipal(
		ad.ServicePrincipalsClient{directoryClient},
		subscriptionId,
		clock,
		newUUID,
	)
	if err != nil {
		return "", "", errors.Trace(err)
	}

	fmt.Fprintln(stderr, "Assigning Owner role to service principal.")
	if err := createRoleAssignment(
		authorizationClient,
		subscriptionId,
		servicePrincipalObjectId,
		newUUID,
	); err != nil {
		return "", "", errors.Trace(err)
	}
	return jujuApplicationId, password, nil
}