Skip to content

Commit 6469d2e

Browse files
Ticket #113 : Create BPMN workflow used to update the password
1 parent e65a849 commit 6469d2e

File tree

72 files changed

+1129
-236
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+1129
-236
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
## Ignore Visual Studio temporary files, build results, and
22
## files generated by popular Visual Studio add-ons.
33

4+
credentials.json
5+
46
# User-specific files
57
*.suo
68
*.user
Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="sid-38422fae-e03e-43a3-bef4-bd33b32041b2" targetNamespace="http://bpmn.io/bpmn" exporter="bpmn-js (https://demo.bpmn.io)" exporterVersion="8.7.1">
2+
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cmg="https://github.com/simpleidserver/CaseManagement" id="sid-38422fae-e03e-43a3-bef4-bd33b32041b2" targetNamespace="http://bpmn.io/bpmn" exporter="bpmn-js (https://demo.bpmn.io)" exporterVersion="8.7.1">
33
<message id="userMessage" name="user" />
4+
<message id="humanTaskCreated" name="humanTaskCreated" />
45
<process id="Process_1" isExecutable="false">
5-
<serviceTask id="Task_1hcentk" name="Send Email">
6+
<serviceTask id="Task_1hcentk" name="Send Email" implementation="##csharpcallback" cmg:delegateId="SendEmailDelegate">
67
<incoming>Flow_0kge1w3</incoming>
78
</serviceTask>
8-
<userTask id="Activity_0fhwdxz" name="Submit Password">
9+
<userTask id="Activity_0fhwdxz" name="Submit Password" implementation="##WsHumanTask" cmg:wsHumanTaskDefName="updatePassword">
910
<incoming>Flow_1fdpmlk</incoming>
1011
<outgoing>Flow_08bn5nb</outgoing>
1112
</userTask>
@@ -14,7 +15,7 @@
1415
</endEvent>
1516
<sequenceFlow id="Flow_08bn5nb" sourceRef="Activity_0fhwdxz" targetRef="Activity_0yov4bm" />
1617
<sequenceFlow id="Flow_1p3mrfd" sourceRef="Activity_0yov4bm" targetRef="Event_08zcpaz" />
17-
<serviceTask id="Activity_0yov4bm" name="Update Password">
18+
<serviceTask id="Activity_0yov4bm" name="Update Password" implementation="##csharpcallback" cmg:delegateId="UpdateUserPasswordDelegate">
1819
<incoming>Flow_08bn5nb</incoming>
1920
<outgoing>Flow_1p3mrfd</outgoing>
2021
</serviceTask>
@@ -26,11 +27,19 @@
2627
<sequenceFlow id="Flow_0kge1w3" sourceRef="Event_0q7uygb" targetRef="Task_1hcentk" />
2728
<boundaryEvent id="Event_0q7uygb" attachedToRef="Activity_0fhwdxz">
2829
<outgoing>Flow_0kge1w3</outgoing>
29-
<messageEventDefinition id="MessageEventDefinition_135rspe" />
30+
<messageEventDefinition id="MessageEventDefinition_135rspe" messageRef="humanTaskCreated" />
3031
</boundaryEvent>
3132
</process>
3233
<bpmndi:BPMNDiagram id="BpmnDiagram_1">
3334
<bpmndi:BPMNPlane id="BpmnPlane_1" bpmnElement="Process_1">
35+
<bpmndi:BPMNEdge id="Flow_0kge1w3_di" bpmnElement="Flow_0kge1w3">
36+
<omgdi:waypoint x="490" y="208" />
37+
<omgdi:waypoint x="490" y="280" />
38+
</bpmndi:BPMNEdge>
39+
<bpmndi:BPMNEdge id="Flow_1fdpmlk_di" bpmnElement="Flow_1fdpmlk">
40+
<omgdi:waypoint x="188" y="150" />
41+
<omgdi:waypoint x="390" y="150" />
42+
</bpmndi:BPMNEdge>
3443
<bpmndi:BPMNEdge id="Flow_1p3mrfd_di" bpmnElement="Flow_1p3mrfd">
3544
<omgdi:waypoint x="820" y="150" />
3645
<omgdi:waypoint x="912" y="150" />
@@ -39,14 +48,12 @@
3948
<omgdi:waypoint x="490" y="150" />
4049
<omgdi:waypoint x="720" y="150" />
4150
</bpmndi:BPMNEdge>
42-
<bpmndi:BPMNEdge id="Flow_1fdpmlk_di" bpmnElement="Flow_1fdpmlk">
43-
<omgdi:waypoint x="188" y="150" />
44-
<omgdi:waypoint x="390" y="150" />
45-
</bpmndi:BPMNEdge>
46-
<bpmndi:BPMNEdge id="Flow_0kge1w3_di" bpmnElement="Flow_0kge1w3">
47-
<omgdi:waypoint x="490" y="208" />
48-
<omgdi:waypoint x="490" y="280" />
49-
</bpmndi:BPMNEdge>
51+
<bpmndi:BPMNShape id="Activity_1saua76_di" bpmnElement="Task_1hcentk">
52+
<omgdc:Bounds x="440" y="280" width="100" height="80" />
53+
</bpmndi:BPMNShape>
54+
<bpmndi:BPMNShape id="Activity_1j5vxke_di" bpmnElement="Activity_0fhwdxz">
55+
<omgdc:Bounds x="390" y="110" width="100" height="80" />
56+
</bpmndi:BPMNShape>
5057
<bpmndi:BPMNShape id="Event_08zcpaz_di" bpmnElement="Event_08zcpaz">
5158
<omgdc:Bounds x="912" y="132" width="36" height="36" />
5259
</bpmndi:BPMNShape>
@@ -56,15 +63,9 @@
5663
<bpmndi:BPMNShape id="Event_0bfv2sq_di" bpmnElement="StartEvent_1y45yut">
5764
<omgdc:Bounds x="152" y="132" width="36" height="36" />
5865
</bpmndi:BPMNShape>
59-
<bpmndi:BPMNShape id="Activity_1j5vxke_di" bpmnElement="Activity_0fhwdxz">
60-
<omgdc:Bounds x="390" y="110" width="100" height="80" />
61-
</bpmndi:BPMNShape>
62-
<bpmndi:BPMNShape id="Activity_1saua76_di" bpmnElement="Task_1hcentk">
63-
<omgdc:Bounds x="440" y="280" width="100" height="80" />
64-
</bpmndi:BPMNShape>
6566
<bpmndi:BPMNShape id="Event_10s7arv_di" bpmnElement="Event_0q7uygb">
6667
<omgdc:Bounds x="472" y="172" width="36" height="36" />
6768
</bpmndi:BPMNShape>
6869
</bpmndi:BPMNPlane>
6970
</bpmndi:BPMNDiagram>
70-
</definitions>
71+
</definitions>

src/CaseManagement.BPMN.Host/CaseManagement.BPMN.Host.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.0" />
1515
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.3" />
1616
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.3" />
17+
<PackageReference Include="MassTransit.AspNetCore" Version="7.1.7" />
1718
</ItemGroup>
1819
<ItemGroup>
1920
<ProjectReference Include="..\CaseManagement.BPMN.AspNetCore\CaseManagement.BPMN.AspNetCore.csproj" />

src/CaseManagement.BPMN.Host/Delegates/SendEmailDelegate.cs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using CaseManagement.BPMN.Domains;
22
using CaseManagement.BPMN.Exceptions;
3+
using Newtonsoft.Json.Linq;
34
using System.Collections.Generic;
45
using System.Linq;
56
using System.Net;
@@ -13,26 +14,24 @@ public class SendEmailDelegate : IDelegateHandler
1314
{
1415
public Task<ICollection<MessageToken>> Execute(ICollection<MessageToken> incoming, DelegateConfigurationAggregate delegateConfiguration, CancellationToken cancellationToken)
1516
{
16-
var user = incoming.FirstOrDefault(i => i.Name == "userMessage");
17-
if (user == null)
17+
var humanTaskInstanceCreated = incoming.FirstOrDefault(i => i.Name == "humanTaskCreated");
18+
if (humanTaskInstanceCreated == null)
1819
{
19-
throw new BPMNProcessorException("userMessage must be passed in the request");
20-
}
21-
22-
var email = user.GetProperty("email");
23-
if (string.IsNullOrWhiteSpace(email))
24-
{
25-
throw new BPMNProcessorException("email is not passed in the request");
20+
throw new BPMNProcessorException("humanTaskCreated must be passed in the request");
2621
}
2722

23+
var inc = humanTaskInstanceCreated.JObjMessageContent.SelectToken("$.incoming[?(@.Name == 'user')].MessageContent");
24+
var messageContent = JObject.Parse(inc.ToString());
25+
var email = messageContent["email"].ToString();
2826
var parameter = SendDelegateParameter.Build(delegateConfiguration);
29-
using (var smtpClient = new SmtpClient(parameter.SmtpHost)
30-
{
31-
Port = parameter.SmtpPort,
32-
Credentials = new NetworkCredential(parameter.SmtpUserName, parameter.SmtpPassword),
33-
EnableSsl = parameter.SmtpEnableSsl
34-
})
27+
using (var smtpClient = new SmtpClient())
3528
{
29+
smtpClient.EnableSsl = parameter.SmtpEnableSsl;
30+
smtpClient.UseDefaultCredentials = false;
31+
smtpClient.Credentials = new NetworkCredential(parameter.SmtpUserName, parameter.SmtpPassword);
32+
smtpClient.Host = parameter.SmtpHost;
33+
smtpClient.Port = parameter.SmtpPort;
34+
smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
3635
var mailMessage = new MailMessage
3736
{
3837
From = new MailAddress(parameter.FromEmail),

src/CaseManagement.BPMN.Host/Delegates/UpdateUserPasswordDelegate.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ namespace CaseManagement.BPMN.Host.Delegates
1414
{
1515
public class UpdateUserPasswordDelegate : IDelegateHandler
1616
{
17-
private const string ActivityName = "Activity_12xhvyl";
17+
private const string ActivityName = "Activity_0fhwdxz";
1818

1919
public async Task<ICollection<MessageToken>> Execute(ICollection<MessageToken> incoming, DelegateConfigurationAggregate delegateConfiguration, CancellationToken cancellationToken)
2020
{
21-
var user = incoming.FirstOrDefault(i => i.Name == "userMessage");
21+
var user = incoming.FirstOrDefault(i => i.Name == "user");
2222
if (user == null)
2323
{
2424
throw new BPMNProcessorException("userMessage must be passed in the request");
@@ -36,7 +36,7 @@ public async Task<ICollection<MessageToken>> Execute(ICollection<MessageToken> i
3636
throw new BPMNProcessorException($"incoming token '{ActivityName}' doesn't exist");
3737
}
3838

39-
var password = messageToken.GetProperty("password");
39+
var password = messageToken.GetProperty("pwd");
4040
var parameter = UpdateUserPasswordParameter.Create(delegateConfiguration);
4141
using (var httpClient = new HttpClient())
4242
{
@@ -57,11 +57,12 @@ public async Task<ICollection<MessageToken>> Execute(ICollection<MessageToken> i
5757
{ "password", password }
5858
};
5959
var content = new StringContent(obj.ToString(), Encoding.UTF8, "application/json");
60+
var url = parameter.UserUrl.Replace("{id}", userId);
6061
var request = new HttpRequestMessage
6162
{
6263
Method = HttpMethod.Put,
6364
Content = content,
64-
RequestUri = new Uri($"{parameter.UserUrl}/{userId}/password")
65+
RequestUri = new Uri(url)
6566
};
6667
request.Headers.Add("Authorization", $"Bearer {tokenResponse.AccessToken}");
6768
var httpResponse = await httpClient.SendAsync(request, cancellationToken);

src/CaseManagement.BPMN.Host/Startup.cs

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
33
using CaseManagement.BPMN.Domains;
44
using CaseManagement.BPMN.Host.Delegates;
5+
using MassTransit;
56
using Microsoft.AspNetCore.Authentication.JwtBearer;
67
using Microsoft.AspNetCore.Builder;
78
using Microsoft.AspNetCore.Hosting;
@@ -11,17 +12,25 @@
1112
using Microsoft.Extensions.Logging;
1213
using Microsoft.IdentityModel.Tokens;
1314
using Newtonsoft.Json;
15+
using Newtonsoft.Json.Linq;
1416
using System;
1517
using System.Collections.Concurrent;
1618
using System.Collections.Generic;
1719
using System.IO;
1820
using System.Linq;
21+
using System.Net.Http;
22+
using System.Security.Claims;
1923
using System.Security.Cryptography;
2024

2125
namespace CaseManagement.BPMN.Host
2226
{
2327
public class Startup
2428
{
29+
private Dictionary<string, string> MAPPING_OPENIDCLAIM_TO_CLAIM = new Dictionary<string, string>
30+
{
31+
{ "sub", ClaimTypes.NameIdentifier },
32+
{ "role", ClaimTypes.Role }
33+
};
2534
private readonly IHostingEnvironment _env;
2635
private readonly IConfiguration _configuration;
2736

@@ -44,11 +53,58 @@ public void ConfigureServices(IServiceCollection services)
4453
})
4554
.AddJwtBearer(options =>
4655
{
56+
options.Events = new JwtBearerEvents
57+
{
58+
OnTokenValidated = async (ctx) =>
59+
{
60+
var issuer = ctx.Principal.Claims.First(c => c.Type == "iss").Value;
61+
using (var httpClient = new HttpClient())
62+
{
63+
var authorization = ctx.Request.Headers["Authorization"][0];
64+
var bearer = authorization.Split(" ").Last();
65+
var requestMessage = new HttpRequestMessage
66+
{
67+
RequestUri = new Uri($"{issuer}/userinfo"),
68+
Method = HttpMethod.Get
69+
};
70+
requestMessage.Headers.Add("Authorization", $"Bearer {bearer}");
71+
var httpResponse = await httpClient.SendAsync(requestMessage);
72+
var json = await httpResponse.Content.ReadAsStringAsync();
73+
var jObj = JObject.Parse(json);
74+
var identity = new ClaimsIdentity("userInfo");
75+
foreach (var kvp in jObj)
76+
{
77+
var key = kvp.Key;
78+
if (MAPPING_OPENIDCLAIM_TO_CLAIM.ContainsKey(key))
79+
{
80+
key = MAPPING_OPENIDCLAIM_TO_CLAIM[key];
81+
}
82+
83+
if (kvp.Value.ToString().StartsWith('['))
84+
{
85+
var arr = JArray.Parse(kvp.Value.ToString()).Select(_ => _.ToString()).ToList();
86+
foreach (var str in arr)
87+
{
88+
identity.AddClaim(new Claim(kvp.Key, str));
89+
}
90+
}
91+
else
92+
{
93+
identity.AddClaim(new Claim(kvp.Key, kvp.Value.ToString()));
94+
}
95+
}
96+
97+
var principal = new ClaimsPrincipal(identity);
98+
ctx.Principal = principal;
99+
}
100+
}
101+
};
47102
options.TokenValidationParameters = new TokenValidationParameters
48103
{
49104
IssuerSigningKey = ExtractKey("openid_puk.txt"),
50105
ValidAudiences = new List<string>
51106
{
107+
"caseManagementWebsite",
52108
"https://localhost:60000",
53109
"https://simpleidserver.northeurope.cloudapp.azure.com/openid"
54110
},
@@ -69,6 +125,7 @@ public void ConfigureServices(IServiceCollection services)
69125
opts.CallbackUrl = "http://localhost:60007/processinstances/{id}/complete/{eltId}";
70126
}).AddProcessFiles(files).AddDelegateConfigurations(GetDelegateConfigurations());
71127
services.AddSwaggerGen();
128+
services.AddMassTransitHostedService();
72129
services.Configure<ForwardedHeadersOptions>(options =>
73130
{
74131
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
@@ -131,6 +188,7 @@ private static byte[] Base64DecodeBytes(string base64EncodedData)
131188

132189
private static ConcurrentBag<DelegateConfigurationAggregate> GetDelegateConfigurations()
133190
{
191+
var credential = JsonConvert.DeserializeObject<CredentialsParameter>(File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), "credentials.json")));
134192
var getWeatherInformationDelegate = DelegateConfigurationAggregate.Create("GetWeatherInformationDelegate", typeof(GetWeatherInformationDelegate).FullName);
135193
getWeatherInformationDelegate.AddDisplayName("fr", "Récupérer météo");
136194
getWeatherInformationDelegate.AddDescription("fr", "Récupérer les informations sur la météo");
@@ -142,21 +200,21 @@ private static ConcurrentBag<DelegateConfigurationAggregate> GetDelegateConfigur
142200
sendEmailDelegate.AddDisplayName("en", "Send email");
143201
sendEmailDelegate.AddRecord("httpBody", "Please click on this link to update the password");
144202
sendEmailDelegate.AddRecord("subject", "Update password");
145-
sendEmailDelegate.AddRecord("fromEmail", "");
146-
sendEmailDelegate.AddRecord("smtpHost", "");
147-
sendEmailDelegate.AddRecord("smtpPort", "");
148-
sendEmailDelegate.AddRecord("smtpUserName", "");
149-
sendEmailDelegate.AddRecord("smtpPassword", "");
150-
sendEmailDelegate.AddRecord("smtpEnableSsl", "");
203+
sendEmailDelegate.AddRecord("fromEmail", credential.Login);
204+
sendEmailDelegate.AddRecord("smtpHost", "smtp.gmail.com");
205+
sendEmailDelegate.AddRecord("smtpPort", "587");
206+
sendEmailDelegate.AddRecord("smtpUserName", credential.Login);
207+
sendEmailDelegate.AddRecord("smtpPassword", credential.Password);
208+
sendEmailDelegate.AddRecord("smtpEnableSsl", "true");
151209

152210
var updateUserPasswordDelegate = DelegateConfigurationAggregate.Create("UpdateUserPasswordDelegate", typeof(UpdateUserPasswordDelegate).FullName);
153211
updateUserPasswordDelegate.AddDisplayName("fr", "Mettre à jour le mot de passe");
154212
updateUserPasswordDelegate.AddDisplayName("en", "Update password");
155-
updateUserPasswordDelegate.AddRecord("clientId", "");
156-
updateUserPasswordDelegate.AddRecord("clientSecret", "");
213+
updateUserPasswordDelegate.AddRecord("clientId", "humanTaskClient");
214+
updateUserPasswordDelegate.AddRecord("clientSecret", "humanTaskClientSecret");
157215
updateUserPasswordDelegate.AddRecord("tokenUrl", "https://localhost:60000/token");
158-
updateUserPasswordDelegate.AddRecord("userUrl", "");
159-
updateUserPasswordDelegate.AddRecord("scope", "update_password");
216+
updateUserPasswordDelegate.AddRecord("userUrl", "https://localhost:60000/management/users/{id}/password");
217+
updateUserPasswordDelegate.AddRecord("scope", "manage_users");
160218

161219
return new ConcurrentBag<DelegateConfigurationAggregate>
162220
{
@@ -165,5 +223,11 @@ private static ConcurrentBag<DelegateConfigurationAggregate> GetDelegateConfigur
165223
updateUserPasswordDelegate
166224
};
167225
}
226+
227+
private class CredentialsParameter
228+
{
229+
public string Login { get; set; }
230+
public string Password { get; set; }
231+
}
168232
}
169233
}

src/CaseManagement.BPMN/Domains/ProcessInstance/MessageToken.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public JObject JObjMessageContent
2323

2424
public string GetProperty(string key)
2525
{
26-
if (JObjMessageContent.ContainsKey(key))
26+
if (!JObjMessageContent.ContainsKey(key))
2727
{
2828
return null;
2929
}

src/CaseManagement.BPMN/Parser/BPMNParser.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,18 @@ public static ICollection<ProcessInstanceAggregate> BuildInstances(tDefinitions
4141
var result = new List<ProcessInstanceAggregate>();
4242
var processes = definitions.Items.Where(_ => _ is tProcess).Cast<tProcess>();
4343
var messages = definitions.Items.Where(_ => _ is tMessage).Cast<tMessage>();
44+
var itemDefs = definitions.Items.Where(_ => _ is tItemDefinition).Cast<tItemDefinition>();
4445
foreach(var process in processes)
4546
{
4647
var builder = ProcessInstanceBuilder.New(processFileId);
4748
foreach(var message in messages)
4849
{
49-
builder.AddMessage(message.id, message.name, string.Empty);
50+
builder.AddMessage(message.id, message.name, message.itemRef?.Name);
51+
}
52+
53+
foreach(var itemDef in itemDefs)
54+
{
55+
builder.AddItemDef(itemDef.id, ItemKinds.Information, false, null);
5056
}
5157

5258
foreach(var item in process.Items)

0 commit comments

Comments
 (0)