How to specify credentials from a Java Web Service in PTC Windchill PDMLink - ptc-windchill

I am currently investigating the possibility of using a Java Web Service (as described by the Info*Engine documentation of Windchill) in order to retrieve information regarding parts. I am using Windchill version 10.1.
I have successfully deployed a web service, which I consume in a .Net application. Calls which do not try to access Windchill information complete successfully. However, when trying to retrieve part information, I get a wt.method.AuthenticationException.
Here is the code that runs within the webService (The web service method simply calls this method)
public static String GetOnePart(String partNumber) throws WTException
{
WTPart part=null;
RemoteMethodServer server = RemoteMethodServer.getDefault();
server.setUserName("theUsername");
server.setPassword("thePassword");
try {
QuerySpec qspec= new QuerySpec(WTPart.class);
qspec.appendWhere(new SearchCondition(WTPart.class,WTPart.NUMBER,SearchCondition.LIKE,partNumber),new int[]{0,1});
// This fails.
QueryResult qr=PersistenceHelper.manager.find((StatementSpec)qspec);
while(qr.hasMoreElements())
{
part=(WTPart) qr.nextElement();
partName = part.getName();
}
} catch (AuthenticationException e) {
// Exception caught here.
partName = e.toString();
}
return partName;
}
This code works in a command line application deployed on the server, but fails with a wt.method.AuthenticationException when performed from within the web service. I feel it fails because the use of RemoteMethodServer is not what I should be doing since the web service is within the MethodServer.
Anyhow, if anyone knows how to do this, it would be awesome.
A bonus question would be how to log from within the web service, and how to configure this logging.
Thank you.

You don't need to authenticate on the server side with this code
RemoteMethodServer server = RemoteMethodServer.getDefault();
server.setUserName("theUsername");
server.setPassword("thePassword");
If you have followed the documentation (windchill help center), your web service should be something annotated with #WebServices and #WebMethod(operationName="getOnePart") and inherit com.ptc.jws.servlet.JaxWsService
Also you have to take care to the policy used during deployment.
The default ant script is configured with
security.policy=userNameAuthSymmetricKeys
So you need to manage it when you consume your ws with .Net.
For logging events, you just need to call the log4j logger instantiated by default with $log.debug("Hello")

You can't pre-authenticate server side.
You can write the auth into your client tho. Not sure what the .Net equivilent is, but this works for Java clients:
private static final String USERNAME = "admin";
private static final String PASSWORD = "password";
static {
java.net.Authenticator.setDefault(new java.net.Authenticator() {
#Override
protected java.net.PasswordAuthentication getPasswordAuthentication() {
return new java.net.PasswordAuthentication(USERNAME, PASSWORD.toCharArray());
}
});
}

Related

How to add multiple Bindable services to a grpc server builder?

I have the gRPC server code as below:
public void buildServer() {
List<BindableService> theServiceList = new ArrayList<BindableService>();
theServiceList.add(new CreateModuleContentService());
theServiceList.add(new RemoveModuleContentService());
ServerBuilder<?> sb = ServerBuilder.forPort(m_port);
for (BindableService aService : theServiceList) {
sb.addService(aService);
}
m_server = sb.build();
}
and client code as below:
public class JavaMainClass {
public static void main(String[] args) {
CreateModuleService createModuleService = new CreateModuleService();
ESDStandardResponse esdReponse = createModuleService.createAtomicBlock("8601934885970354030", "atm1");
RemoveModuleService moduleService = new RemoveModuleService();
moduleService.removeAtomicBlock("8601934885970354030", esdReponse.getId());
}
}
While I am running the client I am getting an exception as below:
Exception in thread "main" io.grpc.StatusRuntimeException: UNIMPLEMENTED: Method grpc.blocks.operations.ModuleContentServices/createAtomicBlock is unimplemented
at io.grpc.stub.ClientCalls.toStatusRuntimeException(ClientCalls.java:233)
at io.grpc.stub.ClientCalls.getUnchecked(ClientCalls.java:214)
at io.grpc.stub.ClientCalls.blockingUnaryCall(ClientCalls.java:139)
In the above server class, if I am commenting the line theServiceList.add(new RemoveModuleContentService()); then the CreateModuleContentService service is working fine, also without commenting all the services of RemoveModuleContentService class are working as expected, which means the problem is with the first service when another gets added.
Can someone please suggest how can I add two services to Server Builder.
A particular gRPC service can only be implemented once per server. Since the name of the gRPC service in the error message is ModuleContentServices, I'm assuming CreateModuleContentService and RemoveModuleContentService both extend ModuleContentServicesImplBase.
When you add the same service multiple times, the last one wins. The way the generated code works, every method of a service is registered even if you don't implement that particular method. Every service method defaults to a handler that simply returns "UNIMPLEMENTED: Method X is unimplemented". createAtomicBlock isn't implemented in RemoveModuleContentService, so it returns that error.
If you interact with the ServerServiceDefinition returned by bindService(), you can mix-and-match methods a bit more, but this is a more advanced API and is intended more for frameworks to use because it can become verbose to compose every application service individually.

Is Azure Function to Function authentication with MSI supported

I created 2 Azure Function Apps, both setup with Authentication/Authorization so an AD App was created for both. I would like to setup AD Auth from one Function to the other using MSI. I setup the client Function with Managed Service Identity using an ARM template. I created a simple test function to get the access token and it returns: Microsoft.Azure.Services.AppAuthentication: Token response is not in the expected format.
try {
var azureServiceTokenProvider = new AzureServiceTokenProvider();
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://myapp-registration-westus-dev.azurewebsites.net/");
log.Info($"Access Token: {accessToken}");
return req.CreateResponse(new {token = accessToken});
}
catch(Exception ex) {
log.Error("Error", ex);
throw;
}
Yes, there is a way to do this. I'll explain at a high level, and then add an item to the MSI documentation backlog to write a proper tutorial for this.
What you want to do is follow this Azure AD authentication sample, but only configure and implement the parts for the TodoListService: https://github.com/Azure-Samples/active-directory-dotnet-daemon.
The role of the TodoListDaemon will be played by a Managed Service Identity instead. So you don't need to register the TodoListDaemon app in Azure AD as instructed in the readme. Just enable MSI on your VM/App Service/Function.
In your code client side code, when you make the call to MSI (on a VM or in a Function or App Service), supply the TodoListService's AppID URI as the resource parameter. MSI will fetch a token for that audience for you.
The code in the TodoListService example will show you how to validate that token when you receive it.
So essentially, what you want to do is register an App in Azure AD, give it an AppID URI, and use that AppID URI as the resource parameter when you make the call to MSI. Then validate the token you receive at your service/receiving side.
Please check that the resource id used "https://myapp-registration-westus-dev.azurewebsites.net/" is accurate. I followed steps here to setup Azure AD authentication, and used the same code as you, and was able to get a token.
https://learn.microsoft.com/en-us/azure/app-service/app-service-mobile-how-to-configure-active-directory-authentication
You can also run this code to check the exact error returned by MSI. Do post the error if it does not help resolve the issue.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Secret", Environment.GetEnvironmentVariable("MSI_SECRET"));
var response = await client.GetAsync(String.Format("{0}/?resource={1}&api-version={2}", Environment.GetEnvironmentVariable("MSI_ENDPOINT"), "https://myapp-registration-westus-dev.azurewebsites.net/", "2017-09-01"));
string msiResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
log.Info($"MSI Response: {msiResponse}");
Update:-
This project.json file and run.csx file work for me. Note: The project.json refers to .NET 4.6, and as per Azure Functions documentation (link in comments), .NET 4.6 is the only supported version as of now. You do not need to upload the referenced assembly again. Most probably, incorrect manual upload of netstandard assembly, instead of net452 is causing your issue.
Only the .NET Framework 4.6 is supported, so make sure that your
project.json file specifies net46 as shown here. When you upload a
project.json file, the runtime gets the packages and automatically
adds references to the package assemblies. You don't need to add #r
"AssemblyName" directives. To use the types defined in the NuGet
packages, add the required using statements to your run.csx file.
project.json
{
"frameworks": {
"net46":{
"dependencies": {
"Microsoft.Azure.Services.AppAuthentication": "1.0.0-preview"
}
}
}
}
run.csx
using Microsoft.Azure.Services.AppAuthentication;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
try
{
var azureServiceTokenProvider = new AzureServiceTokenProvider();
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://vault.azure.net/");
log.Info($"Access Token: {accessToken}");
return req.CreateResponse(new {token = accessToken});
}
catch(Exception ex)
{
log.Error("Error", ex);
throw;
}
}

How do I make a SignalR external reference to hub and not perform circular reference?

So, I'm trying to create a sample where there are the following components/features:
A hangfire server OWIN self-hosted from a Windows Service
SignalR notifications when jobs are completed
Github Project
I can get the tasks queued and performed, but I'm having a hard time sorting out how to then notify the clients (all currently, just until I get it working well) of when the task/job is completed.
My current issue is that I want the SignalR hub to be located in the "core" library SampleCore, but I don't see how to "register it" when starting the webapp SampleWeb. One way I've gotten around that is to create a hub class NotificationHubProxy that inherits the actual hub and that works fine for simple stuff (sending messages from one client to all).
In NotifyTaskComplete, I believe I can get the hub context and then send the message like so:
private void NotifyTaskComplete(int taskId)
{
try
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
if (hubContext != null)
{
hubContext.Clients.All.sendMessage(string.Format("Task {0} completed.", taskId));
}
}
catch (Exception ex)
{
}
}
BUT, I can't do that if NotificationHubProxy is the class being used as it's part of the SampleWeb library and referencing it from SampleCore would lead to a circular reference.
I know the major issue is the hub in the external assembly, but I can't for the life of me find a relevant sample that's using SignalR or MVC5 or setup in this particular way.
Any ideas?
So, the solution was to do the following two things:
I had to use the SignalR .NET client from the SampleCore assembly to create a HubConnection, to create a HubProxy to "NotificationHub" and use that to Invoke the "SendMessage" method - like so:
private void NotifyTaskComplete(string hostUrl, int taskId)
{
var hubConnection = new HubConnection(hostUrl);
var hub = hubConnection.CreateHubProxy("NotificationHub");
hubConnection.Start().Wait();
hub.Invoke("SendMessage", taskId.ToString()).Wait();
}
BUT, as part of creating that HubConnection - I needed to know the url to the OWIN instance. I decided to pass that a parameter to the task, retrieving it like:
private string GetHostAddress()
{
var request = this.HttpContext.Request;
return string.Format("{0}://{1}", request.Url.Scheme, request.Url.Authority);
}
The solution to having a Hub located in an external assembly is that the assembly needs to be loaded before the SignalR routing is setup, like so:
AppDomain.CurrentDomain.Load(typeof(SampleCore.NotificationHub).Assembly.FullName);
app.MapSignalR();
This solution for this part came from here.

Trying to implement PublicKey Authentication for SFTP test server using Apache Mina. However Authenticate method is not being called

I am using Apache Mina SSHD to implement a test SFTPServer. I have been able to get things working for simple Password authentication, however I am not able to configure things for PublicKey Authentication. I have a implemented the PublickeyAuthenticator interface as follows,
public class SimpleKeyAuthenticator implements PublickeyAuthenticator {
#Override
public boolean authenticate(String username, PublicKey key, ServerSession session) {
System.out.println("In authenticate");
return false;
}
}
My server implementation is as follows,
...
sshd = SshServer.setUpDefaultServer();
sshd.setPort(2222);
//sshd.setPort(config.getSFTPPort());
//sshd.setKeyPairProvider(new
sshd.setKeyPairProvider(new PEMGeneratorHostKeyProvider("hostkey.pem"));
//sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
sshd.setPublickeyAuthenticator(new SimpleKeyAuthenticator());
sshd.setFileSystemFactory(new SimpleFileSystemFactory());
List<NamedFactory<UserAuth>> userAuthFactories = new ArrayList<NamedFactory<UserAuth>>();
userAuthFactories.add(new UserAuthNone.Factory());
sshd.setUserAuthFactories(userAuthFactories);
sshd.setCommandFactory(new ScpCommandFactory());
List<NamedFactory<Command>> namedFactoryList = new ArrayList<NamedFactory<Command>>();
namedFactoryList.add(new SftpSubsystem.Factory());
sshd.setSubsystemFactories(namedFactoryList);
sshd.setSessionFactory(new SimpleSessionFactory(handler));
try {
sshd.start();
} catch (Exception e) {
e.printStackTrace();
}
However when I try to get a file using my SFTP client everything works. I would expect the authenticate method to fail given that it always returns false. I have tried setting the KeyPairProvider to use both the PEMGeneratorHostKeyProvider and the SimpleGeneratorHostKeyProvider. I have also set the PublicKeyAuthenticator to use my SimpleKeyAuthenticator class. Note, when I look at the console output I never see 'In authenticate' so I know that Authenticate is never being called. Could someone please point me to what I have missed? Any help is appreciated.
Regards,
Mark
// below line will make client login without any validation.
userAuthFactories.add(new UserAuthNone.Factory());
You should change it like this:
userAuthFactories.add(new UserAuthPublicKey.Factory());

WCF Client Proxies, Client/Channel Caching in ASP.Net - Code Review

long time ASP.Net interface developer being asked to learn WCF, looking for some education on more architecture related fronts - as its not my strong suit but I'm having to deal.
In our current ASMX world we adopted a model of creating ServiceManager static classes for our interaction with web services. We're starting to migrate to WCF, attempting to follow the same model. At first I was dealing with performance problems, but I've tweaked a bit and we're running smoothly now, but I'm questioning my tactics. Here's a simplified version (removed error handling, caching, object manipulation, etc.) of what we're doing:
public static class ContentManager
{
private static StoryManagerClient _clientProxy = null;
const string _contentServiceResourceCode = "StorySvc";
// FOR CACHING
const int _getStoriesTTL = 300;
private static Dictionary<string, GetStoriesCacheItem> _getStoriesCache = new Dictionary<string, GetStoriesCacheItem>();
private static ReaderWriterLockSlim _cacheLockStories = new ReaderWriterLockSlim();
public static Story[] GetStories(string categoryGuid)
{
// OMITTED - if category is cached and not expired, return from cache
// get endpoint address from FinderClient (ResourceManagement SVC)
UrlResource ur = FinderClient.GetUrlResource(_contentServiceResourceCode);
// Get proxy
StoryManagerClient svc = GetStoryServiceClient(ur.Url);
// create request params
GetStoriesRequest request = new GetStoriesRequest{}; // SIMPLIFIED
Manifest manifest = new Manifest{}; // SIMPLIFIED
// execute GetStories at WCF service
try
{
GetStoriesResponse response = svc.GetStories(manifest, request);
}
catch (Exception)
{
if (svc.State == CommunicationState.Faulted)
{
svc.Abort();
}
throw;
}
// OMITTED - do stuff with response, cache if needed
// return....
}
internal static StoryManagerClient GetStoryServiceClient(string endpointAddress)
{
if (_clientProxy == null)
_clientProxy = new StoryManagerClient(GetServiceBinding(_contentServiceResourceCode), new EndpointAddress(endpointAddress));
return _clientProxy;
}
public static Binding GetServiceBinding(string bindingSettingName)
{
// uses Finder service to load a binding object - our alternative to definition in web.config
}
public static void PreloadContentServiceClient()
{
// get finder location
UrlResource ur = FinderClient.GetUrlResource(_contentServiceResourceCode);
// preload proxy
GetStoryServiceClient(ur.Url);
}
}
We're running smoothly now with round-trip calls completing in the 100ms range. Creating the PreloadContentServiceClient() method and adding to our global.asax got that "first call" performance down to that same level. And you might want to know we're using the DataContractSerializer, and the "Add Service Reference" method.
I've done a lot of reading on static classes, singletons, shared data contract assemblies, how to use the ChannelFactory pattern and a whole bunch of other things that I could do to our usage model...admittedly, some of its gone over my head. And, like I said, we seem to be running smoothly. I know I'm not seeing the big picture, though. Can someone tell me what I've ended up here with regards to channel pooling, proxy failures, etc. and why I should head down the ChannelFactory path? My gut says to just do it, but my head can't comprehend why...
Thanks!
ChannelFactory is typically used when you aren't using Add Service Reference - you have the contract via a shared assembly not generated via a WSDL. Add Service Reference uses ClientBase which is essentially creating the WCF channel for you behind the scenes.
When you are dealing with REST-ful services, WebChannelFactory provides a service-client like interface based off the shared assembly contract. You can't use Add Service Reference if your service only supports a REST-ful endpoint binding.
The only difference to you is preference - do you need full access the channel for custom behaviors, bindings, etc. or does Add Service Reference + SOAP supply you with enough of an interface for your needs.

Resources