Context
My core goal is to write an Azure WebApps deployment tool in C#. The process will be roughly
User logs in
User selects subscription
User selects or creates resource group
User selects or creates storage for the web app
User selects or creates web service plan
User selects or creates web app
Tool uploads the web app using Kudu to POST a zip
Since the last step can't be done in the portal, my idea was to do everything in a GUI tool.
I started out using Kudu's ARMClient.Authentication and Microsoft.Azure.ResourceManager 1.0.0-preview. However, when it comes to creating a storage account I get a permissions error (The subscription is not registered to use namespace Microsoft.Storage), so my plan B was to do the authentication myself following Brady Gaster's blog post.
The problem
I've set up an application as documented, and using its clientId and tenantId I'm able to log in and list tenants. But I can't list any subscriptions. (NB I've partly elided the clientId and tenantId in case there are security risks with giving them in full).
string clientId = "f62903b9-ELIDED";
string tenantId = "47b6e6c3-ELIDED";
const string redirectUri = "urn:ietf:wg:oauth:2.0:oob";
const string baseAuthUri = "https://login.microsoftonline.com/";
const string resource = "https://management.core.windows.net/";
var ctx = new AuthenticationContext(baseAuthUri + tenantId);
var authResult = ctx.AcquireToken(resource, clientId, new Uri(redirectUri), PromptBehavior.Auto);
var token = new TokenCredentials(authResult.AccessToken);
var subClient = new SubscriptionClient(token);
var tenants = await subClient.Tenants.ListAsync();
foreach (var tenant in tenants) Console.WriteLine(tenant.TenantId);
var subs = await subClient.Subscriptions.ListAsync();
foreach (var sub in subs) Console.WriteLine(sub.DisplayName);
When I run this it prompts me to login, and lists the tenants corresponding to the subscriptions I own or co-administer. But it doesn't list a single subscription. If I change the IDs to the commonly used (I think officially for Powershell) values
clientId = "1950a258-227b-4e31-a9cf-717495945fc2";
tenantId = "common";
then it's the same.
What is the step I've missed in order to get a list of my subscriptions?
You need to iterate through the tenants, authenticate in tenant and get a subscription list for every tenant.
The following code will output the Subscriptions like Get-AzureRmSubscription powershell cmdlet does.
class Program
{
private static string m_resource = "https://management.core.windows.net/";
private static string m_clientId = "1950a258-227b-4e31-a9cf-717495945fc2"; // well-known client ID for Azure PowerShell
private static string m_redirectURI = "urn:ietf:wg:oauth:2.0:oob"; // redirect URI for Azure PowerShell
static void Main(string[] args)
{
try
{
var ctx = new AuthenticationContext("https://login.microsoftonline.com/common");
// This will show the login window
var mainAuthRes = ctx.AcquireToken(m_resource, m_clientId, new Uri(m_redirectURI), PromptBehavior.Always);
var subscriptionCredentials = new TokenCloudCredentials(mainAuthRes.AccessToken);
var cancelToken = new CancellationToken();
using (var subscriptionClient = new SubscriptionClient(subscriptionCredentials))
{
var tenants = subscriptionClient.Tenants.ListAsync(cancelToken).Result;
foreach (var tenantDescription in tenants.TenantIds)
{
var tenantCtx = new AuthenticationContext("https://login.microsoftonline.com/" + tenantDescription.TenantId);
// This will NOT show the login window
var tenantAuthRes = tenantCtx.AcquireToken(
m_resource,
m_clientId,
new Uri(m_redirectURI),
PromptBehavior.Never,
new UserIdentifier(mainAuthRes.UserInfo.DisplayableId, UserIdentifierType.RequiredDisplayableId));
var tenantTokenCreds = new TokenCloudCredentials(tenantAuthRes.AccessToken);
using (var tenantSubscriptionClient = new SubscriptionClient(tenantTokenCreds))
{
var tenantSubscriptioins = tenantSubscriptionClient.Subscriptions.ListAsync(cancelToken).Result;
foreach (var sub in tenantSubscriptioins.Subscriptions)
{
Console.WriteLine($"SubscriptionName : {sub.DisplayName}");
Console.WriteLine($"SubscriptionId : {sub.SubscriptionId}");
Console.WriteLine($"TenantId : {tenantDescription.TenantId}");
Console.WriteLine($"State : {sub.State}");
Console.WriteLine();
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
Console.WriteLine("press something");
Console.ReadLine();
}
}
}
A couple things you can look into...
1) the error you saw during creating of the storage account is likely due to the Resource Provider not being registered for use with the subscription. Any RP needs to be registered before use, some clients (Portal, PowerShell) will register the RP for you so you never notice it. See: https://msdn.microsoft.com/en-us/library/azure/dn790548.aspx - you should be able to do that from your code if the user has sufficient perms.
2) You may not be getting any subscriptions back because your endpoint (management.core.windows.net) is the endpoint for Azure Service Management not Azure Resource Manager (management.azure.com). If the subscription access is granted via AzureRM and RBAC, the old ASM apis will not see (i.e. have access to) those subscriptions.
Related
I created a new ASP.NET MVC 5 project in Visual Studio 2013 (Express for Web), and I imported a database, but when I try to validate the user role by using
if(User.IsInRole("Admin"))
it still is identifying the user as a regular user, and not as Admin. I'm wondering how I can get the user roles in a controller after importing the database?
And, how do I make sure that the roles are imported to my ASP.NET MVC application?
Make sure you have the necessary data in your database. You got to be sure that the role is in the "AspNetRoles" table and the user is linked in the "AspNetUserRoles" table. To make sure you have data, you can use following seeds:
public static class RoleSeeder
{
public static void Seed()
{
CreateRole("Admin", "Administratie", "Administratie");
...
}
private static void CreateRole(string name, string description, string group)
{
using (var db = new DbContext())
{
if (!db.Roles.Any(r => r.Name == name))
{
var role = new Role { Name = name, Description = description, Group = group };
var store = new RoleStore<Role>(db);
var manager = new RoleManager<Role>(store);
manager.Create(role);
}
else
{
var store = new RoleStore<Role>(db);
var manager = new RoleManager<Role>(store);
var dbRole = manager.FindByName(name);
dbRole.Description = description;
dbRole.Group = group;
manager.Update(dbRole);
}
}
}
}
No reason to write your own role manager.
I am working on a APP's server program which using asp.net core MVC mode. And in the XXXXcontrller i never use code like "Connection.open()" and "Connection.close()",a HTTP GET action is like this.
public async Task<IActionResult> register(string phoneNum, string password, string userType)
{
//从数据库里找到m.Id和 id相同的车,赋给dbCars
var dbCars = await _context.dbUsers.SingleOrDefaultAsync(m => m.UserName == phoneNum);
// var dbCars= _context.dbCars.Where(s => s.Id == id).FirstOrDefault<dbCars>();
if (dbCars == null)//如果为空,则注册
{
//注册新用户
dbCars = new dbUsers();
dbCars.UserName = phoneNum;
dbCars.Password = password;
dbCars.UserType = userType;
dbCars.IsVerified = true;
_context.Add(dbCars);
await _context.SaveChangesAsync();
//注册完成之后,把ID、用户类型和是否认证返回回去
List<string> userInfo = new List<string>();
userInfo.Add(dbCars.Id.ToString().Trim());
userInfo.Add(dbCars.UserType.ToString().Trim());
userInfo.Add(dbCars.IsVerified.ToString());
string json = JsonConvert.SerializeObject(userInfo);
return Ok(json);
}
else //如果不为空就代表注册过了
{
return Ok("0"); //返回1代表注册过了
}
}
I wondered how should i maintain the SQL Connections when many users access this service?? Should I close these connections manually? Or it can be done automatically by some magic?? If many users access this service, do my program breaked down??
I am confused, who can give me an answer??
if you are using Entity Framework or ORM you should see and set the connection string on the web.config or if it's on a separate layer you can see the settings on the app.config. If you run the application it will use the startup project of your application connectionstring app.config(windows application) or web.config (web application)
I have implemented Specflow to reuse some steps across features as in this example -Specflow,Selenium-Share data between different Step definitions or classes .Since, in our project, we are integrating multiple features & reusing them. What is the best way to share browser session across features if its triggered in between steps as per the above approach?
My Scenario:
Once an application created, I need to launch new session, login different User-set different services and approve it.
But after logging in fails with below error on Step definition 4 in reused Whenstep of Given(Set the service to (.*)). That particular step is from different feature, hence the new session needs to be used in those steps. The LaunchURl method below is just launching the website with url, no new session created - This works fine
OpenQA.Selenium.WebDriverException : Unexpected error. System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it "IP here"
[Given(#"A New Application is added")]
public void GivenANewApplicationIsAdded()
{
Given("UK_The Service is set");
Given("User Navigated to New Application screen");
When("User fills up form as in data row 1");
Then("new SID generated");
}
[Given(#"New Browser Launched")]
public void GivenNewBrowserLaunched()
{
SeleniumContext sl = new SeleniumContext();
this.seleniumContext = sl;
}
[Given(#"Login is successful with ""(.*)"" and ""(.*)""")]
public void GivenLoginIsSuccessfulWithAnd(string userName, string password)
{
SuperTests spr = new SuperTests();
_driver = spr.LaunchURL(seleniumContext.WebDriver);
//seleniumContext.WebDriver = _driver;
LoginPage lg = new LoginPage(_driver);
lg.LoginProcess(userName, password);
}
[Given(#"Set the service to ""(.*)""")]
public void GivenSetTheServiceTo(string serviceId)
{
When("Select a Service from the option "+serviceId);
Then("The Services is changed to the one selected " + serviceId);
}
In other feature
[When(#"Select a Service from the option (.*)")]
public void WhenSelectAServiceFromTheOptionTestTeam(string p0)
{
HomePage mst = new HomePage(seleniumContext.WebDriver);
mst.SetServiceId(p0);
}
The 2 work around what we figured was
Create a new instance of binding class to call the methods or steps as shown below
[Given(#"Set the service to ""(.*)""")]
public void GivenSetTheServiceTo(string serviceId)
{
var serIdSteps = new UK_SetServiceIDSteps(seleniumContext);
serIdSteps.WhenUK_SelectAServiceFromTheOptionTest(serviceId);
serIdSteps.ThenUK_TheServicesIsChangedToTheOneSelected(serviceId);
}
or
tried this which worked as well- basically calling a new method to create a new session. for this I need not create any new instance for Binding class. Called the Step directly.
[Given(#"New Browser Launched")]
public void GivenNewBrowserLaunched()
{
SuperTests spr = new SuperTests();
_driver = spr.LaunchURL("Firefox");
seleniumContext.WebDriver = _driver;
}
public void GivenSetTheServiceTo(string serviceId)
{
When("UK_Select a Service from the option "+serviceId);
Then("UK_The Services is changed to the one selected " + serviceId);
}
Not sure, which is correct way of doing it? Trying to figure it out from Reusable steps point?The latter one is not advised as we need to change the type of browser to launch at multiple place.
Good morning I'm trying to integrate the Google+ Domains API with my company domain but I'm facing some problems.
I'm trying the java approach following the quick start for java but after implement the code the response from the google server is :
Authenticate the domain for hugo.catarino#outsystems.com
Inserting activity
10/Set/2013 17:08:49 com.google.api.client.googleapis.services.AbstractGoogleClient <init>
WARNING: Application name is not set. Call Builder#setApplicationName.
Exception in thread "main" com.google.api.client.auth.oauth2.TokenResponseException:400 Bad Request
{
"error" : "access_denied"
}
at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:105)
at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:287)
at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:307)
at com.google.api.client.googleapis.auth.oauth2.GoogleCredential.executeRefreshToken(GoogleCredential.java:269)
at com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:489)
at com.google.api.client.auth.oauth2.Credential.intercept(Credential.java:217)
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:858)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:410)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:343)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:460)
at com.google.plus.samples.quickstart.domains.DomainDelegation.main(DomainDelegation.java:160)
here is used authentication method and my variables:
private static final String SERVICE_ACCOUNT_EMAIL = "638852846577#developer.gserviceaccount.com";
private static final String SERVICE_ACCOUNT_PKCS12_FILE_PATH =
"src/com/google/plus/samples/quickstart/domains/05cab8e819cbd0a747b180c1f22fc93dba916b7b-privatekey.p12";
private static final String USER_EMAIL = "hugo.catarino#outsystems.com";
private static Plus authenticate() throws GeneralSecurityException, IOException {
System.out.println(String.format("Authenticate the domain for %s", USER_EMAIL));
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
// Setting the sub field with USER_EMAIL allows you to make API calls using the special keyword
// 'me' in place of a user id for that user.
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(SCOPE)
.setServiceAccountUser(USER_EMAIL)
.setServiceAccountPrivateKeyFromP12File(
new java.io.File(SERVICE_ACCOUNT_PKCS12_FILE_PATH)).build();
// Create and return the Plus service object
Plus service = new Plus.Builder(httpTransport, jsonFactory, credential).build();
return service;
}
My main class has the following code like in the sample:
Plus service = authenticate();
String userId = "me";
String msg = "Happy Monday! #caseofthemondays";
System.out.println("Inserting activity");
// Create the audience of the post
PlusAclentryResource res = new PlusAclentryResource();
// Share to the domain
res.setType("domain");
List<PlusAclentryResource> aclEntries = new ArrayList<PlusAclentryResource>();
aclEntries.add(res);
Acl acl = new Acl();
acl.setItems(aclEntries);
// Required, this does the domain restriction
acl.setDomainRestricted(true);
Activity activity = new Activity()
.setObject(new Activity.PlusObject().setOriginalContent(msg))
.setAccess(acl);
activity = service.activities().insert(userId, activity).execute();
System.out.println(activity);
In domain cPanel the company defined for me the next scopes:
https://www.googleapis.com/auth/plus.circles.read
https://www.googleapis.com/auth/plus.circles.write
https://www.googleapis.com/auth/plus.me
https://www.googleapis.com/auth/plus.media.upload
https://www.googleapis.com/auth/plus.stream.read
https://www.googleapis.com/auth/plus.stream.write
My scope definition is:
private static final List<String> SCOPE = Arrays.asList(
"https://www.googleapis.com/auth/plus.circles.read",
"https://www.googleapis.com/auth/plus.circles.write",
"https://www.googleapis.com/auth/plus.me",
"https://www.googleapis.com/auth/plus.media.upload",
"https://www.googleapis.com/auth/plus.stream.read",
"https://www.googleapis.com/auth/plus.stream.write");
I'm a bit lost here , is there any way of debug this problem or know why is this access denied?
There are several things that you should check.
First, is the private key file that you downloaded from the Google APIs Console in the correct path with your code? This file is referenced by the following variable. This needs to tell the OAuth client library where to find the file.
private static final String SERVICE_ACCOUNT_PKCS12_FILE_PATH =
"/path/to/<public_key_fingerprint>-privatekey.p12";
It is very important that you do not rename the file.
Second, does your scope list in your code match the list of scopes set in the Admin console?
The configuration in the Admin console for your Google Apps domain, and the scopes provided in the request must be identical. Try adjusting the SCOPE variable in your code to be:
private static final List<String> SCOPE = Arrays.asList(
"https://www.googleapis.com/auth/plus.me",
"https://www.googleapis.com/auth/plus.circles.read",
"https://www.googleapis.com/auth/plus.circles.write",
"https://www.googleapis.com/auth/plus.media.upload",
"https://www.googleapis.com/auth/plus.stream.read",
"https://www.googleapis.com/auth/plus.stream.write");
In general, it is best to only request the scopes that you will need, rather than all scopes available.
Third, make sure that the client ID you generated is the one listed on the Admin console entry that specifies the scopes permitted.
Has anybody tried to use the Windows credentials to create an instance of OrganizationServiceProxy in CRM 2011 (On-Premise) ? I have a WCF service that uses
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows"/>
</security>
and I am able to confirm that the user is authenticated (OperationContext.Current.ServiceSecurityContext.WindowsIdentity.IsAuthenticated) but I don't know how to generate/pass the ClientCredentials to create an instance of the CRM service. This is called from a Silverlight application that does not live in an IFrame inside CRM.
Thanks.
What you need to use separate user account to log into the OrganizationServiceProxy.
You wont be able retrieve the windows credentials to pass to the proxy for authentication.
The user that you do use needs prvActOnBehalfOfAnotherUser privilege associated with it.
Once this is done and you can successfullly login and retrieve a valid OrganizationServiceProxy, what you need to do as a consumer of the service is specify the CallerId whenever you are calling operations on it. This token you should retrieve from the xrm model using Xrm.Page.context.getUserId. See. http://msdn.microsoft.com/en-us/library/gg334511.aspx.
Then from silverlight you would use the System.Windows.Browser.ScriptObject bridge to execute client side javascript to retrieve the userid of current user logged into crm.
Preferably do this at application bootstrap time and save the value into an applicationdata variable so so can access it globally from within your silverlight app.
Eg. of client side script.
function CrmContext() {
}
var context = null;
with (window.parent) {
context = Xrm.Page.context;}
CrmContext.prototype.ReadUserId = function () {
var userId = context.getUserId();
return userId;
}
Once you have the user token set the Proxy CallerId with this value
Eg.
private OrganizationServiceProxy Proxy { get; set; }
public Guid Create(CreateEntity request)
{
if (request == null || request.UserId == Guid.Empty || request.Entity == null)
{
throw new InvalidMessageException("Invalid reqest message. Please provide compulsory criteria");
}
var result = Guid.Empty;
try
{
if (Proxy != null)
{
Proxy.CallerId = request.UserId;
using (Proxy)
{
result = Proxy.Create(request.Entity);
}
}
}
catch (FaultException<OrganizationServiceFault> e)
{
Log.Error(e.Message);
throw new IntegrationException(e.Message);
}
return result;
}
The approach ive taken to solve this was to create a crm adapter encapsulating the crm proxy and sending request object to service interface that includes the user token.
public OrganizationServiceAdapter(ICrmConfigurationContext crmConfigurationConext)
{
try
{
Proxy = new OrganizationServiceProxy(
crmConfigurationConext.OrganizationServiceConfiguration,
crmConfigurationConext.Credentials);
}
catch (Exception e)
{
//// TODO: Add local proxy pattern implementation for failover
Proxy = null;
Log.Error(e.Message);
throw new IntegrationException(ExceptionMessages.CouldNotLoginToOrganizationService());
}
}